feat: optionally save complete performance log in chrome benchpress tests (#27551)

If RAW_PERFLOG_PATH is passed in as an option, benchpress saves chrome's
performance log to a json file. This allows developers to download the
json file and upload it to their browser to get a breakdown of chrome-side
resource usage during a test.

PR Close #27551
This commit is contained in:
Omar Griffin 2018-12-07 10:23:43 -08:00 committed by Matias Niemelä
parent 2ea4f690e4
commit d42f32cc61
3 changed files with 22 additions and 10 deletions

View File

@ -26,6 +26,7 @@ export class Options {
static RECEIVED_DATA = new InjectionToken('Options.receivedData'); static RECEIVED_DATA = new InjectionToken('Options.receivedData');
static REQUEST_COUNT = new InjectionToken('Options.requestCount'); static REQUEST_COUNT = new InjectionToken('Options.requestCount');
static CAPTURE_FRAMES = new InjectionToken('Options.frameCapture'); static CAPTURE_FRAMES = new InjectionToken('Options.frameCapture');
static RAW_PERFLOG_PATH = new InjectionToken('Options.rawPerflogPath');
static DEFAULT_PROVIDERS = [ static DEFAULT_PROVIDERS = [
{provide: Options.DEFAULT_DESCRIPTION, useValue: {}}, {provide: Options.DEFAULT_DESCRIPTION, useValue: {}},
{provide: Options.SAMPLE_DESCRIPTION, useValue: {}}, {provide: Options.SAMPLE_DESCRIPTION, useValue: {}},
@ -36,7 +37,8 @@ export class Options {
{provide: Options.RECEIVED_DATA, useValue: false}, {provide: Options.RECEIVED_DATA, useValue: false},
{provide: Options.REQUEST_COUNT, useValue: false}, {provide: Options.REQUEST_COUNT, useValue: false},
{provide: Options.CAPTURE_FRAMES, useValue: false}, {provide: Options.CAPTURE_FRAMES, useValue: false},
{provide: Options.WRITE_FILE, useValue: writeFile} {provide: Options.WRITE_FILE, useValue: writeFile},
{provide: Options.RAW_PERFLOG_PATH, useValue: null}
]; ];
} }

View File

@ -7,6 +7,7 @@
*/ */
import {Inject, Injectable, StaticProvider} from '@angular/core'; import {Inject, Injectable, StaticProvider} from '@angular/core';
import * as fs from 'fs';
import {Options} from '../common_options'; import {Options} from '../common_options';
import {WebDriverAdapter} from '../web_driver_adapter'; import {WebDriverAdapter} from '../web_driver_adapter';
@ -23,15 +24,19 @@ import {PerfLogEvent, PerfLogFeatures, WebDriverExtension} from '../web_driver_e
export class ChromeDriverExtension extends WebDriverExtension { export class ChromeDriverExtension extends WebDriverExtension {
static PROVIDERS = <StaticProvider>[{ static PROVIDERS = <StaticProvider>[{
provide: ChromeDriverExtension, provide: ChromeDriverExtension,
deps: [WebDriverAdapter, Options.USER_AGENT] deps: [WebDriverAdapter, Options.USER_AGENT, Options.RAW_PERFLOG_PATH]
}]; }];
private _majorChromeVersion: number; private _majorChromeVersion: number;
private _firstRun = true; private _firstRun = true;
private _rawPerflogPath: string|null;
constructor(private _driver: WebDriverAdapter, @Inject(Options.USER_AGENT) userAgent: string) { constructor(
private driver: WebDriverAdapter, @Inject(Options.USER_AGENT) userAgent: string,
@Inject(Options.RAW_PERFLOG_PATH) rawPerflogPath: string|null) {
super(); super();
this._majorChromeVersion = this._parseChromeVersion(userAgent); this._majorChromeVersion = this._parseChromeVersion(userAgent);
this._rawPerflogPath = rawPerflogPath;
} }
private _parseChromeVersion(userAgent: string): number { private _parseChromeVersion(userAgent: string): number {
@ -49,16 +54,16 @@ export class ChromeDriverExtension extends WebDriverExtension {
return parseInt(v, 10); return parseInt(v, 10);
} }
gc() { return this._driver.executeScript('window.gc()'); } gc() { return this.driver.executeScript('window.gc()'); }
async timeBegin(name: string): Promise<any> { async timeBegin(name: string): Promise<any> {
if (this._firstRun) { if (this._firstRun) {
this._firstRun = false; this._firstRun = false;
// Before the first run, read out the existing performance logs // Before the first run, read out the existing performance logs
// so that the chrome buffer does not fill up. // so that the chrome buffer does not fill up.
await this._driver.logs('performance'); await this.driver.logs('performance');
} }
return this._driver.executeScript(`performance.mark('${name}-bpstart');`); return this.driver.executeScript(`performance.mark('${name}-bpstart');`);
} }
timeEnd(name: string, restartName: string|null = null): Promise<any> { timeEnd(name: string, restartName: string|null = null): Promise<any> {
@ -66,7 +71,7 @@ export class ChromeDriverExtension extends WebDriverExtension {
if (restartName) { if (restartName) {
script += `performance.mark('${restartName}-bpstart');`; script += `performance.mark('${restartName}-bpstart');`;
} }
return this._driver.executeScript(script); return this.driver.executeScript(script);
} }
// See [Chrome Trace Event // See [Chrome Trace Event
@ -74,8 +79,8 @@ export class ChromeDriverExtension extends WebDriverExtension {
readPerfLog(): Promise<PerfLogEvent[]> { readPerfLog(): Promise<PerfLogEvent[]> {
// TODO(tbosch): Chromedriver bug https://code.google.com/p/chromedriver/issues/detail?id=1098 // TODO(tbosch): Chromedriver bug https://code.google.com/p/chromedriver/issues/detail?id=1098
// Need to execute at least one command so that the browser logs can be read out! // Need to execute at least one command so that the browser logs can be read out!
return this._driver.executeScript('1+1') return this.driver.executeScript('1+1')
.then((_) => this._driver.logs('performance')) .then((_) => this.driver.logs('performance'))
.then((entries) => { .then((entries) => {
const events: PerfLogEvent[] = []; const events: PerfLogEvent[] = [];
entries.forEach((entry: any) => { entries.forEach((entry: any) => {
@ -87,6 +92,10 @@ export class ChromeDriverExtension extends WebDriverExtension {
throw new Error('The DevTools trace buffer filled during the test!'); throw new Error('The DevTools trace buffer filled during the test!');
} }
}); });
if (this._rawPerflogPath && events.length) {
fs.appendFileSync(this._rawPerflogPath, JSON.stringify(events));
}
return this._convertPerfRecordsToEvents(events); return this._convertPerfRecordsToEvents(events);
}); });
} }

View File

@ -47,7 +47,8 @@ import {TraceEventFactory} from '../trace_event_factory';
provide: WebDriverAdapter, provide: WebDriverAdapter,
useValue: new MockDriverAdapter(log, perfRecords, messageMethod) useValue: new MockDriverAdapter(log, perfRecords, messageMethod)
}, },
{provide: Options.USER_AGENT, useValue: userAgent} {provide: Options.USER_AGENT, useValue: userAgent},
{provide: Options.RAW_PERFLOG_PATH, useValue: null}
]) ])
.get(ChromeDriverExtension); .get(ChromeDriverExtension);
return extension; return extension;