Skip to content

Commit

Permalink
feat(benchpress): add receivedData + requestCount to PerflogMetric
Browse files Browse the repository at this point in the history
Closes #5750
  • Loading branch information
ochafik committed Dec 10, 2015
1 parent 24dcd26 commit fe1dd77
Show file tree
Hide file tree
Showing 7 changed files with 234 additions and 51 deletions.
14 changes: 14 additions & 0 deletions modules/benchpress/docs/index.md
Expand Up @@ -175,6 +175,20 @@ In addition to that, one extra binding needs to be passed to benchpress in tests

benchpress.sample(bindings: [bp.bind(bp.Options.CAPTURE_FRAMES).toValue(true)], ... )

# Requests Metrics

Benchpress can also record the number of requests sent and count the received "encoded" bytes since [window.performance.timing.navigationStart](http://www.w3.org/TR/navigation-timing/#dom-performancetiming-navigationstart):

- `receivedData`: number of bytes received since the last navigation start
- `requestCount`: number of requests sent since the last navigation start

To collect these metrics, you need the following corresponding extra bindings:

benchpress.sample(bindings: [
bp.bind(bp.Options.RECEIVED_DATA).toValue(true),
bp.bind(bp.Options.REQUEST_COUNT).toValue(true)
], ... )

# Best practices

* Use normalized environments
Expand Down
8 changes: 8 additions & 0 deletions modules/benchpress/src/common_options.ts
Expand Up @@ -26,6 +26,10 @@ export class Options {
// TODO(tbosch): use static values when our transpiler supports them
static get MICRO_METRICS() { return _MICRO_METRICS; }
// TODO(tbosch): use static values when our transpiler supports them
static get RECEIVED_DATA() { return _RECEIVED_DATA; }
// TODO(tbosch): use static values when our transpiler supports them
static get REQUEST_COUNT() { return _REQUEST_COUNT; }
// TODO(tbosch): use static values when our transpiler supports them
static get CAPTURE_FRAMES() { return _CAPTURE_FRAMES; }
}

Expand All @@ -40,6 +44,8 @@ var _USER_AGENT = new OpaqueToken('Options.userAgent');
var _MICRO_METRICS = new OpaqueToken('Options.microMetrics');
var _NOW = new OpaqueToken('Options.now');
var _WRITE_FILE = new OpaqueToken('Options.writeFile');
var _RECEIVED_DATA = new OpaqueToken('Options.receivedData');
var _REQUEST_COUNT = new OpaqueToken('Options.requestCount');
var _CAPTURE_FRAMES = new OpaqueToken('Options.frameCapture');

var _DEFAULT_PROVIDERS = [
Expand All @@ -50,5 +56,7 @@ var _DEFAULT_PROVIDERS = [
provide(_PREPARE, {useValue: false}),
provide(_MICRO_METRICS, {useValue: {}}),
provide(_NOW, {useValue: () => DateWrapper.now()}),
provide(_RECEIVED_DATA, {useValue: false}),
provide(_REQUEST_COUNT, {useValue: false}),
provide(_CAPTURE_FRAMES, {useValue: false})
];
47 changes: 42 additions & 5 deletions modules/benchpress/src/metric/perflog_metric.ts
Expand Up @@ -36,12 +36,18 @@ export class PerflogMetric extends Metric {
**/
constructor(private _driverExtension: WebDriverExtension, private _setTimeout: Function,
private _microMetrics: {[key: string]: any}, private _forceGc: boolean,
private _captureFrames: boolean) {
private _captureFrames: boolean, private _receivedData: boolean,
private _requestCount: boolean) {
super();

this._remainingEvents = [];
this._measureCount = 0;
this._perfLogFeatures = _driverExtension.perfLogFeatures();
if (!this._perfLogFeatures.userTiming) {
// User timing is needed for navigationStart.
this._receivedData = false;
this._requestCount = false;
}
}

describe(): {[key: string]: any} {
Expand All @@ -61,6 +67,12 @@ export class PerflogMetric extends Metric {
res['forcedGcAmount'] = 'forced gc amount in kbytes';
}
}
if (this._receivedData) {
res['receivedData'] = 'encoded bytes received since navigationStart';
}
if (this._requestCount) {
res['requestCount'] = 'count of requests sent since navigationStart';
}
if (this._captureFrames) {
if (!this._perfLogFeatures.frameCapture) {
var warningMsg = 'WARNING: Metric requested, but not supported by driver';
Expand Down Expand Up @@ -188,6 +200,12 @@ export class PerflogMetric extends Metric {
result['frameTime.smooth'] = 0;
}
StringMapWrapper.forEach(this._microMetrics, (desc, name) => { result[name] = 0; });
if (this._receivedData) {
result['receivedData'] = 0;
}
if (this._requestCount) {
result['requestCount'] = 0;
}

var markStartEvent = null;
var markEndEvent = null;
Expand Down Expand Up @@ -217,6 +235,22 @@ export class PerflogMetric extends Metric {
markEndEvent = event;
}

let isInstant = StringWrapper.equals(ph, 'I') || StringWrapper.equals(ph, 'i');
if (this._requestCount && StringWrapper.equals(name, 'sendRequest')) {
result['requestCount'] += 1;
} else if (this._receivedData && StringWrapper.equals(name, 'receivedData') && isInstant) {
result['receivedData'] += event['args']['encodedDataLength'];
} else if (StringWrapper.equals(name, 'navigationStart')) {
// We count data + requests since the last navigationStart
// (there might be chrome extensions loaded by selenium before our page, so there
// will likely be more than one navigationStart).
if (this._receivedData) {
result['receivedData'] = 0;
}
if (this._requestCount) {
result['requestCount'] = 0;
}
}
if (isPresent(markStartEvent) && isBlank(markEndEvent) &&
event['pid'] === markStartEvent['pid']) {
if (StringWrapper.equals(ph, 'b') && StringWrapper.equals(name, _MARK_NAME_FRAME_CAPUTRE)) {
Expand All @@ -236,7 +270,7 @@ export class PerflogMetric extends Metric {
frameCaptureEndEvent = event;
}

if (StringWrapper.equals(ph, 'I') || StringWrapper.equals(ph, 'i')) {
if (isInstant) {
if (isPresent(frameCaptureStartEvent) && isBlank(frameCaptureEndEvent) &&
StringWrapper.equals(name, 'frame')) {
frameTimestamps.push(event['ts']);
Expand Down Expand Up @@ -332,14 +366,17 @@ var _FRAME_TIME_SMOOTH_THRESHOLD = 17;
var _PROVIDERS = [
bind(PerflogMetric)
.toFactory(
(driverExtension, setTimeout, microMetrics, forceGc, captureFrames) =>
new PerflogMetric(driverExtension, setTimeout, microMetrics, forceGc, captureFrames),
(driverExtension, setTimeout, microMetrics, forceGc, captureFrames, receivedData,
requestCount) => new PerflogMetric(driverExtension, setTimeout, microMetrics, forceGc,
captureFrames, receivedData, requestCount),
[
WebDriverExtension,
_SET_TIMEOUT,
Options.MICRO_METRICS,
Options.FORCE_GC,
Options.CAPTURE_FRAMES
Options.CAPTURE_FRAMES,
Options.RECEIVED_DATA,
Options.REQUEST_COUNT
]),
provide(_SET_TIMEOUT, {useValue: (fn, millis) => TimerWrapper.setTimeout(fn, millis)})
];
7 changes: 5 additions & 2 deletions modules/benchpress/src/web_driver_extension.ts
Expand Up @@ -66,12 +66,15 @@ export class PerfLogFeatures {
render: boolean;
gc: boolean;
frameCapture: boolean;
userTiming: boolean;

constructor({render = false, gc = false, frameCapture = false}:
{render?: boolean, gc?: boolean, frameCapture?: boolean} = {}) {
constructor(
{render = false, gc = false, frameCapture = false, userTiming = false}:
{render?: boolean, gc?: boolean, frameCapture?: boolean, userTiming?: boolean} = {}) {
this.render = render;
this.gc = gc;
this.frameCapture = frameCapture;
this.userTiming = userTiming;
}
}

Expand Down
11 changes: 10 additions & 1 deletion modules/benchpress/src/webdriver/chrome_driver_extension.ts
Expand Up @@ -193,6 +193,15 @@ export class ChromeDriverExtension extends WebDriverExtension {
this._isEvent(categories, name, ['devtools.timeline'], 'Layout') ||
this._isEvent(categories, name, ['devtools.timeline'], 'Paint')) {
return normalizeEvent(event, {'name': 'render'});
} else if (this._isEvent(categories, name, ['devtools.timeline'], 'ResourceReceivedData')) {
let normArgs = {'encodedDataLength': args['data']['encodedDataLength']};
return normalizeEvent(event, {'name': 'receivedData', 'args': normArgs});
} else if (this._isEvent(categories, name, ['devtools.timeline'], 'ResourceSendRequest')) {
let data = args['data'];
let normArgs = {'url': data['url'], 'method': data['requestMethod']};
return normalizeEvent(event, {'name': 'sendRequest', 'args': normArgs});
} else if (this._isEvent(categories, name, ['blink.user_timing'], 'navigationStart')) {
return normalizeEvent(event, {'name': name});
}
return null; // nothing useful in this event
}
Expand All @@ -208,7 +217,7 @@ export class ChromeDriverExtension extends WebDriverExtension {
}

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

supports(capabilities: {[key: string]: any}): boolean {
Expand Down

0 comments on commit fe1dd77

Please sign in to comment.