X Tutup
Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
X Tutup