Skip to content

Commit

Permalink
fix(benchpress): make benchpress fit for chrome 45
Browse files Browse the repository at this point in the history
Closes #3411

Closes #3982
  • Loading branch information
goderbauer committed Sep 5, 2015
1 parent d8c5ab2 commit 67b9414
Show file tree
Hide file tree
Showing 3 changed files with 404 additions and 160 deletions.
185 changes: 132 additions & 53 deletions modules/benchpress/src/webdriver/chrome_driver_extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,11 @@ import {
import {WebDriverExtension, PerfLogFeatures} from '../web_driver_extension';
import {WebDriverAdapter} from '../web_driver_adapter';
import {Promise} from 'angular2/src/core/facade/async';
import {Options} from '../common_options';

/**
* Set the following 'traceCategories' to collect metrics in Chrome:
* 'v8,blink.console,disabled-by-default-devtools.timeline'
* 'v8,blink.console,disabled-by-default-devtools.timeline,devtools.timeline'
*
* In order to collect the frame rate related metrics, add 'benchmark'
* to the list above.
Expand All @@ -25,7 +26,27 @@ export class ChromeDriverExtension extends WebDriverExtension {
// TODO(tbosch): use static values when our transpiler supports them
static get BINDINGS(): Binding[] { return _BINDINGS; }

constructor(private _driver: WebDriverAdapter) { super(); }
private _majorChromeVersion: number;

constructor(private _driver: WebDriverAdapter, userAgent: string) {
super();
this._majorChromeVersion = this._parseChromeVersion(userAgent);
}

private _parseChromeVersion(userAgent: string): number {
if (isBlank(userAgent)) {
return -1;
}
var v = StringWrapper.split(userAgent, /Chrom(e|ium)\//g)[2];
if (isBlank(v)) {
return -1;
}
v = StringWrapper.split(v, /\./g)[0];
if (isBlank(v)) {
return -1;
}
return NumberWrapper.parseInt(v, 10);
}

gc() { return this._driver.executeScript('window.gc()'); }

Expand Down Expand Up @@ -63,81 +84,138 @@ export class ChromeDriverExtension extends WebDriverExtension {
});
}

_convertPerfRecordsToEvents(chromeEvents: Array<StringMap<string, any>>,
normalizedEvents: Array<StringMap<string, any>> = null) {
private _convertPerfRecordsToEvents(chromeEvents: Array<StringMap<string, any>>,
normalizedEvents: Array<StringMap<string, any>> = null) {
if (isBlank(normalizedEvents)) {
normalizedEvents = [];
}
var majorGCPids = {};
chromeEvents.forEach((event) => {
var cat = event['cat'];
var categories = this._parseCategories(event['cat']);
var name = event['name'];
var args = event['args'];
var pid = event['pid'];
var ph = event['ph'];
if (StringWrapper.equals(cat, 'disabled-by-default-devtools.timeline')) {
if (StringWrapper.equals(name, 'FunctionCall') &&
(isBlank(args) || isBlank(args['data']) ||
!StringWrapper.equals(args['data']['scriptName'], 'InjectedScript'))) {
normalizedEvents.push(normalizeEvent(event, {'name': 'script'}));

} else if (StringWrapper.equals(name, 'RecalculateStyles') ||
StringWrapper.equals(name, 'Layout') ||
StringWrapper.equals(name, 'UpdateLayerTree') ||
StringWrapper.equals(name, 'Paint') || StringWrapper.equals(name, 'Rasterize') ||
StringWrapper.equals(name, 'CompositeLayers')) {
normalizedEvents.push(normalizeEvent(event, {'name': 'render'}));

} else if (StringWrapper.equals(name, 'GCEvent')) {
var normArgs = {
'usedHeapSize': isPresent(args['usedHeapSizeAfter']) ? args['usedHeapSizeAfter'] :
args['usedHeapSizeBefore']
};
if (StringWrapper.equals(event['ph'], 'E')) {
normArgs['majorGc'] = isPresent(majorGCPids[pid]) && majorGCPids[pid];
}
majorGCPids[pid] = false;
normalizedEvents.push(normalizeEvent(event, {'name': 'gc', 'args': normArgs}));
}

} else if (StringWrapper.equals(cat, 'blink.console')) {
if (this._isEvent(categories, name, ['blink.console'])) {
normalizedEvents.push(normalizeEvent(event, {'name': name}));

} else if (StringWrapper.equals(cat, 'v8')) {
if (StringWrapper.equals(name, 'majorGC')) {
if (StringWrapper.equals(ph, 'B')) {
majorGCPids[pid] = true;
}
}

} else if (StringWrapper.equals(cat, 'benchmark')) {
} else if (this._isEvent(categories, name, ['benchmark'],
'BenchmarkInstrumentation::ImplThreadRenderingStats')) {
// TODO(goderbauer): Instead of BenchmarkInstrumentation::ImplThreadRenderingStats the
// following events should be used (if available) for more accurate measurments:
// 1st choice: vsync_before - ground truth on Android
// 2nd choice: BenchmarkInstrumentation::DisplayRenderingStats - available on systems with
// new surfaces framework (not broadly enabled yet)
// 3rd choice: BenchmarkInstrumentation::ImplThreadRenderingStats - fallback event that is
// allways available if something is rendered
if (StringWrapper.equals(name, 'BenchmarkInstrumentation::ImplThreadRenderingStats')) {
var frameCount = event['args']['data']['frame_count'];
if (frameCount > 1) {
throw new BaseException('multi-frame render stats not supported');
}
if (frameCount == 1) {
normalizedEvents.push(normalizeEvent(event, {'name': 'frame'}));
}
var frameCount = event['args']['data']['frame_count'];
if (frameCount > 1) {
throw new BaseException('multi-frame render stats not supported');
}
if (frameCount == 1) {
normalizedEvents.push(normalizeEvent(event, {'name': 'frame'}));
}
} else if (this._isEvent(categories, name, ['disabled-by-default-devtools.timeline'],
'Rasterize') ||
this._isEvent(categories, name, ['disabled-by-default-devtools.timeline'],
'CompositeLayers')) {
normalizedEvents.push(normalizeEvent(event, {'name': 'render'}));
} else if (this._majorChromeVersion < 45) {
var normalizedEvent = this._processAsPreChrome45Event(event, categories, majorGCPids);
if (normalizedEvent != null) normalizedEvents.push(normalizedEvent);
} else {
var normalizedEvent = this._processAsPostChrome44Event(event, categories);
if (normalizedEvent != null) normalizedEvents.push(normalizedEvent);
}
});
return normalizedEvents;
}

private _processAsPreChrome45Event(event, categories, majorGCPids) {
var name = event['name'];
var args = event['args'];
var pid = event['pid'];
var ph = event['ph'];
if (this._isEvent(categories, name, ['disabled-by-default-devtools.timeline'],
'FunctionCall') &&
(isBlank(args) || isBlank(args['data']) ||
!StringWrapper.equals(args['data']['scriptName'], 'InjectedScript'))) {
return normalizeEvent(event, {'name': 'script'});
} else if (this._isEvent(categories, name, ['disabled-by-default-devtools.timeline'],
'RecalculateStyles') ||
this._isEvent(categories, name, ['disabled-by-default-devtools.timeline'],
'Layout') ||
this._isEvent(categories, name, ['disabled-by-default-devtools.timeline'],
'UpdateLayerTree') ||
this._isEvent(categories, name, ['disabled-by-default-devtools.timeline'],
'Paint')) {
return normalizeEvent(event, {'name': 'render'});
} else if (this._isEvent(categories, name, ['disabled-by-default-devtools.timeline'],
'GCEvent')) {
var normArgs = {
'usedHeapSize': isPresent(args['usedHeapSizeAfter']) ? args['usedHeapSizeAfter'] :
args['usedHeapSizeBefore']
};
if (StringWrapper.equals(ph, 'E')) {
normArgs['majorGc'] = isPresent(majorGCPids[pid]) && majorGCPids[pid];
}
majorGCPids[pid] = false;
return normalizeEvent(event, {'name': 'gc', 'args': normArgs});
} else if (this._isEvent(categories, name, ['v8'], 'majorGC') &&
StringWrapper.equals(ph, 'B')) {
majorGCPids[pid] = true;
}
return null; // nothing useful in this event
}

private _processAsPostChrome44Event(event, categories) {
var name = event['name'];
var args = event['args'];
if (this._isEvent(categories, name, ['devtools.timeline', 'v8'], 'MajorGC')) {
var normArgs = {
'majorGc': true,
'usedHeapSize': isPresent(args['usedHeapSizeAfter']) ? args['usedHeapSizeAfter'] :
args['usedHeapSizeBefore']
};
return normalizeEvent(event, {'name': 'gc', 'args': normArgs});
} else if (this._isEvent(categories, name, ['devtools.timeline', 'v8'], 'MinorGC')) {
var normArgs = {
'majorGc': false,
'usedHeapSize': isPresent(args['usedHeapSizeAfter']) ? args['usedHeapSizeAfter'] :
args['usedHeapSizeBefore']
};
return normalizeEvent(event, {'name': 'gc', 'args': normArgs});
} else if (this._isEvent(categories, name, ['devtools.timeline', 'v8'], 'FunctionCall') &&
(isBlank(args) || isBlank(args['data']) ||
!StringWrapper.equals(args['data']['scriptName'], 'InjectedScript'))) {
return normalizeEvent(event, {'name': 'script'});
} else if (this._isEvent(categories, name, ['devtools.timeline', 'blink'],
'UpdateLayoutTree')) {
return normalizeEvent(event, {'name': 'render'});
} else if (this._isEvent(categories, name, ['devtools.timeline'], 'UpdateLayerTree') ||
this._isEvent(categories, name, ['devtools.timeline'], 'Layout') ||
this._isEvent(categories, name, ['devtools.timeline'], 'Paint')) {
return normalizeEvent(event, {'name': 'render'});
}
return null; // nothing useful in this event
}

private _parseCategories(categories: string): string[] {
return StringWrapper.split(categories, /,/g);
}

private _isEvent(eventCategories: string[], eventName: string, expectedCategories: string[],
expectedName: string = null): boolean {
var hasCategories = ListWrapper.reduce(expectedCategories, (value, cat) => {
return value && ListWrapper.contains(eventCategories, cat);
}, true);
return isBlank(expectedName) ? hasCategories :
hasCategories && StringWrapper.equals(eventName, expectedName);
}

perfLogFeatures(): PerfLogFeatures {
return new PerfLogFeatures({render: true, gc: true, frameCapture: true});
}

supports(capabilities: StringMap<string, any>): boolean {
return StringWrapper.equals(capabilities['browserName'].toLowerCase(), 'chrome');
return this._majorChromeVersion != -1 &&
StringWrapper.equals(capabilities['browserName'].toLowerCase(), 'chrome');
}
}

Expand All @@ -164,5 +242,6 @@ function normalizeEvent(chromeEvent: StringMap<string, any>, data: StringMap<str

var _BINDINGS = [
bind(ChromeDriverExtension)
.toFactory((driver) => new ChromeDriverExtension(driver), [WebDriverAdapter])
.toFactory((driver, userAgent) => new ChromeDriverExtension(driver, userAgent),
[WebDriverAdapter, Options.USER_AGENT])
];
Loading

0 comments on commit 67b9414

Please sign in to comment.