benchpress
Version:
Benchpress - a framework for e2e performance tests
351 lines • 56.6 kB
JavaScript
import { PromiseWrapper, TimerWrapper } from 'angular2/src/facade/async';
import { isPresent, isBlank, StringWrapper, RegExpWrapper, NumberWrapper } from 'angular2/src/facade/lang';
import { BaseException } from 'angular2/src/facade/exceptions';
import { ListWrapper, StringMapWrapper } from 'angular2/src/facade/collection';
import { bind, provide, OpaqueToken } from 'angular2/src/core/di';
import { WebDriverExtension } from '../web_driver_extension';
import { Metric } from '../metric';
import { Options } from '../common_options';
/**
* A metric that reads out the performance log
*/
export class PerflogMetric extends Metric {
/**
* @param driverExtension
* @param setTimeout
* @param microMetrics Name and description of metrics provided via console.time / console.timeEnd
**/
constructor(_driverExtension, _setTimeout, _microMetrics, _forceGc, _captureFrames, _receivedData, _requestCount) {
super();
this._driverExtension = _driverExtension;
this._setTimeout = _setTimeout;
this._microMetrics = _microMetrics;
this._forceGc = _forceGc;
this._captureFrames = _captureFrames;
this._receivedData = _receivedData;
this._requestCount = _requestCount;
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;
}
}
// TODO(tbosch): use static values when our transpiler supports them
static get BINDINGS() { return _PROVIDERS; }
// TODO(tbosch): use static values when our transpiler supports them
static get SET_TIMEOUT() { return _SET_TIMEOUT; }
describe() {
var res = {
'scriptTime': 'script execution time in ms, including gc and render',
'pureScriptTime': 'script execution time in ms, without gc nor render'
};
if (this._perfLogFeatures.render) {
res['renderTime'] = 'render time in ms';
}
if (this._perfLogFeatures.gc) {
res['gcTime'] = 'gc time in ms';
res['gcAmount'] = 'gc amount in kbytes';
res['majorGcTime'] = 'time of major gcs in ms';
if (this._forceGc) {
res['forcedGcTime'] = 'forced gc time in ms';
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';
// using dot syntax for metric name to keep them grouped together in console reporter
res['frameTime.mean'] = warningMsg;
res['frameTime.worst'] = warningMsg;
res['frameTime.best'] = warningMsg;
res['frameTime.smooth'] = warningMsg;
}
else {
res['frameTime.mean'] = 'mean frame time in ms (target: 16.6ms for 60fps)';
res['frameTime.worst'] = 'worst frame time in ms';
res['frameTime.best'] = 'best frame time in ms';
res['frameTime.smooth'] = 'percentage of frames that hit 60fps';
}
}
StringMapWrapper.forEach(this._microMetrics, (desc, name) => { StringMapWrapper.set(res, name, desc); });
return res;
}
beginMeasure() {
var resultPromise = PromiseWrapper.resolve(null);
if (this._forceGc) {
resultPromise = resultPromise.then((_) => this._driverExtension.gc());
}
return resultPromise.then((_) => this._beginMeasure());
}
endMeasure(restart) {
if (this._forceGc) {
return this._endPlainMeasureAndMeasureForceGc(restart);
}
else {
return this._endMeasure(restart);
}
}
_endPlainMeasureAndMeasureForceGc(restartMeasure) {
return this._endMeasure(true).then((measureValues) => {
// disable frame capture for measurements during forced gc
var originalFrameCaptureValue = this._captureFrames;
this._captureFrames = false;
return this._driverExtension.gc()
.then((_) => this._endMeasure(restartMeasure))
.then((forceGcMeasureValues) => {
this._captureFrames = originalFrameCaptureValue;
StringMapWrapper.set(measureValues, 'forcedGcTime', forceGcMeasureValues['gcTime']);
StringMapWrapper.set(measureValues, 'forcedGcAmount', forceGcMeasureValues['gcAmount']);
return measureValues;
});
});
}
_beginMeasure() {
return this._driverExtension.timeBegin(this._markName(this._measureCount++));
}
_endMeasure(restart) {
var markName = this._markName(this._measureCount - 1);
var nextMarkName = restart ? this._markName(this._measureCount++) : null;
return this._driverExtension.timeEnd(markName, nextMarkName)
.then((_) => this._readUntilEndMark(markName));
}
_readUntilEndMark(markName, loopCount = 0, startEvent = null) {
if (loopCount > _MAX_RETRY_COUNT) {
throw new BaseException(`Tried too often to get the ending mark: ${loopCount}`);
}
return this._driverExtension.readPerfLog().then((events) => {
this._addEvents(events);
var result = this._aggregateEvents(this._remainingEvents, markName);
if (isPresent(result)) {
this._remainingEvents = events;
return result;
}
var completer = PromiseWrapper.completer();
this._setTimeout(() => completer.resolve(this._readUntilEndMark(markName, loopCount + 1)), 100);
return completer.promise;
});
}
_addEvents(events) {
var needSort = false;
events.forEach(event => {
if (StringWrapper.equals(event['ph'], 'X')) {
needSort = true;
var startEvent = {};
var endEvent = {};
StringMapWrapper.forEach(event, (value, prop) => {
startEvent[prop] = value;
endEvent[prop] = value;
});
startEvent['ph'] = 'B';
endEvent['ph'] = 'E';
endEvent['ts'] = startEvent['ts'] + startEvent['dur'];
this._remainingEvents.push(startEvent);
this._remainingEvents.push(endEvent);
}
else {
this._remainingEvents.push(event);
}
});
if (needSort) {
// Need to sort because of the ph==='X' events
ListWrapper.sort(this._remainingEvents, (a, b) => {
var diff = a['ts'] - b['ts'];
return diff > 0 ? 1 : diff < 0 ? -1 : 0;
});
}
}
_aggregateEvents(events, markName) {
var result = { 'scriptTime': 0, 'pureScriptTime': 0 };
if (this._perfLogFeatures.gc) {
result['gcTime'] = 0;
result['majorGcTime'] = 0;
result['gcAmount'] = 0;
}
if (this._perfLogFeatures.render) {
result['renderTime'] = 0;
}
if (this._captureFrames) {
result['frameTime.mean'] = 0;
result['frameTime.best'] = 0;
result['frameTime.worst'] = 0;
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;
var gcTimeInScript = 0;
var renderTimeInScript = 0;
var frameTimestamps = [];
var frameTimes = [];
var frameCaptureStartEvent = null;
var frameCaptureEndEvent = null;
var intervalStarts = {};
var intervalStartCount = {};
events.forEach((event) => {
var ph = event['ph'];
var name = event['name'];
var microIterations = 1;
var microIterationsMatch = RegExpWrapper.firstMatch(_MICRO_ITERATIONS_REGEX, name);
if (isPresent(microIterationsMatch)) {
name = microIterationsMatch[1];
microIterations = NumberWrapper.parseInt(microIterationsMatch[2], 10);
}
if (StringWrapper.equals(ph, 'b') && StringWrapper.equals(name, markName)) {
markStartEvent = event;
}
else if (StringWrapper.equals(ph, 'e') && StringWrapper.equals(name, markName)) {
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)) {
if (isPresent(frameCaptureStartEvent)) {
throw new BaseException('can capture frames only once per benchmark run');
}
if (!this._captureFrames) {
throw new BaseException('found start event for frame capture, but frame capture was not requested in benchpress');
}
frameCaptureStartEvent = event;
}
else if (StringWrapper.equals(ph, 'e') &&
StringWrapper.equals(name, _MARK_NAME_FRAME_CAPUTRE)) {
if (isBlank(frameCaptureStartEvent)) {
throw new BaseException('missing start event for frame capture');
}
frameCaptureEndEvent = event;
}
if (isInstant) {
if (isPresent(frameCaptureStartEvent) && isBlank(frameCaptureEndEvent) &&
StringWrapper.equals(name, 'frame')) {
frameTimestamps.push(event['ts']);
if (frameTimestamps.length >= 2) {
frameTimes.push(frameTimestamps[frameTimestamps.length - 1] -
frameTimestamps[frameTimestamps.length - 2]);
}
}
}
if (StringWrapper.equals(ph, 'B') || StringWrapper.equals(ph, 'b')) {
if (isBlank(intervalStarts[name])) {
intervalStartCount[name] = 1;
intervalStarts[name] = event;
}
else {
intervalStartCount[name]++;
}
}
else if ((StringWrapper.equals(ph, 'E') || StringWrapper.equals(ph, 'e')) &&
isPresent(intervalStarts[name])) {
intervalStartCount[name]--;
if (intervalStartCount[name] === 0) {
var startEvent = intervalStarts[name];
var duration = (event['ts'] - startEvent['ts']);
intervalStarts[name] = null;
if (StringWrapper.equals(name, 'gc')) {
result['gcTime'] += duration;
var amount = (startEvent['args']['usedHeapSize'] - event['args']['usedHeapSize']) / 1000;
result['gcAmount'] += amount;
var majorGc = event['args']['majorGc'];
if (isPresent(majorGc) && majorGc) {
result['majorGcTime'] += duration;
}
if (isPresent(intervalStarts['script'])) {
gcTimeInScript += duration;
}
}
else if (StringWrapper.equals(name, 'render')) {
result['renderTime'] += duration;
if (isPresent(intervalStarts['script'])) {
renderTimeInScript += duration;
}
}
else if (StringWrapper.equals(name, 'script')) {
result['scriptTime'] += duration;
}
else if (isPresent(this._microMetrics[name])) {
result[name] += duration / microIterations;
}
}
}
}
});
if (!isPresent(markStartEvent) || !isPresent(markEndEvent)) {
// not all events have been received, no further processing for now
return null;
}
if (isPresent(markEndEvent) && isPresent(frameCaptureStartEvent) &&
isBlank(frameCaptureEndEvent)) {
throw new BaseException('missing end event for frame capture');
}
if (this._captureFrames && isBlank(frameCaptureStartEvent)) {
throw new BaseException('frame capture requested in benchpress, but no start event was found');
}
if (frameTimes.length > 0) {
this._addFrameMetrics(result, frameTimes);
}
result['pureScriptTime'] = result['scriptTime'] - gcTimeInScript - renderTimeInScript;
return result;
}
_addFrameMetrics(result, frameTimes) {
result['frameTime.mean'] = frameTimes.reduce((a, b) => a + b, 0) / frameTimes.length;
var firstFrame = frameTimes[0];
result['frameTime.worst'] = frameTimes.reduce((a, b) => a > b ? a : b, firstFrame);
result['frameTime.best'] = frameTimes.reduce((a, b) => a < b ? a : b, firstFrame);
result['frameTime.smooth'] =
frameTimes.filter(t => t < _FRAME_TIME_SMOOTH_THRESHOLD).length / frameTimes.length;
}
_markName(index) { return `${_MARK_NAME_PREFIX}${index}`; }
}
var _MICRO_ITERATIONS_REGEX = /(.+)\*(\d+)$/g;
var _MAX_RETRY_COUNT = 20;
var _MARK_NAME_PREFIX = 'benchpress';
var _SET_TIMEOUT = new OpaqueToken('PerflogMetric.setTimeout');
var _MARK_NAME_FRAME_CAPUTRE = 'frameCapture';
// using 17ms as a somewhat looser threshold, instead of 16.6666ms
var _FRAME_TIME_SMOOTH_THRESHOLD = 17;
var _PROVIDERS = [
bind(PerflogMetric)
.toFactory((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.RECEIVED_DATA,
Options.REQUEST_COUNT
]),
provide(_SET_TIMEOUT, { useValue: (fn, millis) => TimerWrapper.setTimeout(fn, millis) })
];
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicGVyZmxvZ19tZXRyaWMuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyJkaWZmaW5nX3BsdWdpbl93cmFwcGVyLW91dHB1dF9wYXRoLXhCTElCclZSLnRtcC9iZW5jaHByZXNzL3NyYy9tZXRyaWMvcGVyZmxvZ19tZXRyaWMudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ik9BQU8sRUFBQyxjQUFjLEVBQUUsWUFBWSxFQUFDLE1BQU0sMkJBQTJCO09BQy9ELEVBQ0wsU0FBUyxFQUNULE9BQU8sRUFDUCxhQUFhLEVBRWIsYUFBYSxFQUNiLGFBQWEsRUFDZCxNQUFNLDBCQUEwQjtPQUMxQixFQUFDLGFBQWEsRUFBbUIsTUFBTSxnQ0FBZ0M7T0FDdkUsRUFBQyxXQUFXLEVBQUUsZ0JBQWdCLEVBQUMsTUFBTSxnQ0FBZ0M7T0FDckUsRUFBQyxJQUFJLEVBQUUsT0FBTyxFQUFZLFdBQVcsRUFBQyxNQUFNLHNCQUFzQjtPQUVsRSxFQUFDLGtCQUFrQixFQUFrQixNQUFNLHlCQUF5QjtPQUNwRSxFQUFDLE1BQU0sRUFBQyxNQUFNLFdBQVc7T0FDekIsRUFBQyxPQUFPLEVBQUMsTUFBTSxtQkFBbUI7QUFFekM7O0dBRUc7QUFDSCxtQ0FBbUMsTUFBTTtJQVd2Qzs7OztRQUlJO0lBQ0osWUFBb0IsZ0JBQW9DLEVBQVUsV0FBcUIsRUFDbkUsYUFBbUMsRUFBVSxRQUFpQixFQUM5RCxjQUF1QixFQUFVLGFBQXNCLEVBQ3ZELGFBQXNCO1FBQ3hDLE9BQU8sQ0FBQztRQUpVLHFCQUFnQixHQUFoQixnQkFBZ0IsQ0FBb0I7UUFBVSxnQkFBVyxHQUFYLFdBQVcsQ0FBVTtRQUNuRSxrQkFBYSxHQUFiLGFBQWEsQ0FBc0I7UUFBVSxhQUFRLEdBQVIsUUFBUSxDQUFTO1FBQzlELG1CQUFjLEdBQWQsY0FBYyxDQUFTO1FBQVUsa0JBQWEsR0FBYixhQUFhLENBQVM7UUFDdkQsa0JBQWEsR0FBYixhQUFhLENBQVM7UUFHeEMsSUFBSSxDQUFDLGdCQUFnQixHQUFHLEVBQUUsQ0FB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QUMsYUFBYTtRQUNyQixPQUFPLENBQUMsYUFBYTtLQUN0QixDQUFDO0lBQ1YsT0FBTyxDQUFDLFlBQVksRUFBRSxFQUFDLFFBQVEsRUFBRSxDQUFDLEVBQUUsRUFBRSxNQUFNLEtBQUssWUFBWSxDQUFDLFVBQVUsQ0FBQyxFQUFFLEVBQUUsTUFBTSxDQUFDLEVBQUMsQ0FBQztDQUN2RixDQUFDIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHtQcm9taXNlV3JhcHBlciwgVGltZXJXcmFwcGVyfSBmcm9tICdhbmd1bGFyMi9zcmMvZmFjYWRlL2FzeW5jJztcbmltcG9ydCB7XG4gIGlzUHJlc2VudCxcbiAgaXNCbGFuayxcbiAgU3RyaW5nV3JhcHBlcixcbiAgTWF0aCxcbiAgUmVnRXhwV3JhcHBlcixcbiAgTnVtYmVyV3JhcHBlclxufSBmcm9tICdhbmd1bGFyMi9zcmMvZmFjYWRlL2xhbmcnO1xuaW1wb3J0IHtCYXNlRXhjZXB0aW9uLCBXcmFwcGVkRXhjZXB0aW9ufSBmcm9tICdhbmd1bGFyMi9zcmMvZmFjYWRlL2V4Y2VwdGlvbnMnO1xuaW1wb3J0IHtMaXN0V3JhcHBlciwgU3RyaW5nTWFwV3JhcHBlcn0gZnJvbSAnYW5ndWxhcjIvc3JjL2ZhY2FkZS9jb2xsZWN0aW9uJztcbmltcG9ydCB7YmluZCwgcHJvdmlkZSwgUHJvdmlkZXIsIE9wYXF1ZVRva2VufSBmcm9tICdhbmd1bGFyMi9zcmMvY29yZS9kaSc7XG5cbmltcG9ydCB7V2ViRHJpdmVyRXh0ZW5zaW9uLCBQZXJmTG9nRmVhdHVyZXN9IGZyb20gJy4uL3dlYl9kcml2ZXJfZXh0ZW5zaW9uJztcbmltcG9ydCB7TWV0cmljfSBmcm9tICcuLi9tZXRyaWMnO1xuaW1wb3J0IHtPcHRpb25zfSBmcm9tICcuLi9jb21tb25fb3B0aW9ucyc7XG5cbi8qKlxuICogQSBtZXRyaWMgdGhhdCByZWFkcyBvdXQgdGhlIHBlcmZvcm1hbmNlIGxvZ1xuICovXG5leHBvcnQgY2xhc3MgUGVyZmxvZ01ldHJpYyBleHRlbmRzIE1ldHJpYyB7XG4gIC8vIFRPRE8odGJvc2NoKTogdXNlIHN0YXRpYyB2YWx1ZXMgd2hlbiBvdXIgdHJhbnNwaWxlciBzdXBwb3J0cyB0aGVtXG4gIHN0YXRpYyBnZXQgQklORElOR1MoKTogUHJvdmlkZXJbXSB7IHJldHVybiBfUFJPVklERVJTOyB9XG4gIC8vIFRPRE8odGJvc2NoKTogdXNlIHN0YXRpYyB2YWx1ZXMgd2hlbiBvdXIgdHJhbnNwaWxlciBzdXBwb3J0cyB0aGVtXG4gIHN0YXRpYyBnZXQgU0VUX1RJTUVPVVQoKTogT3BhcXVlVG9rZW4geyByZXR1cm4gX1NFVF9USU1FT1VUOyB9XG5cbiAgcHJpdmF0ZSBfcmVtYWluaW5nRXZlbnRzOiBBcnJheTx7W2tleTogc3RyaW5nXTogYW55fT47XG4gIHByaXZhdGUgX21lYXN1cmVDb3VudDogbnVtYmVyO1xuICBfcGVyZkxvZ0ZlYXR1cmVzOiBQZXJmTG9nRmVhdHVyZXM7XG5cblxuICAvKipcbiAgICogQHBhcmFtIGRyaXZlckV4dGVuc2lvblxuICAgKiBAcGFyYW0gc2V0VGltZW91dFxuICAgKiBAcGFyYW0gbWljcm9NZXRyaWNzIE5hbWUgYW5kIGRlc2NyaXB0aW9uIG9mIG1ldHJpY3MgcHJvdmlkZWQgdmlhIGNvbnNvbGUudGltZSAvIGNvbnNvbGUudGltZUVuZFxuICAgKiovXG4gIGNvbnN0cnVjdG9yKHByaXZhdGUgX2RyaXZlckV4dGVuc2lvbjogV2ViRHJpdmVyRXh0ZW5zaW9uLCBwcml2YXRlIF9zZXRUaW1lb3V0OiBGdW5jdGlvbixcbiAgICAgICAgICAgICAgcHJpdmF0ZSBfbWljcm9NZXRyaWNzOiB7W2tleTogc3RyaW5nXTogYW55fSwgcHJpdmF0ZSBfZm9yY2VHYzogYm9vbGVhbixcbiAgICAgICAgICAgICAgcHJpdmF0ZSBfY2FwdHVyZUZyYW1lczogYm9vbGVhbiwgcHJpdmF0ZSBfcmVjZWl2ZWREYXRhOiBib29sZWFuLFxuICAgICAgICAgICAgICBwcml2YXRlIF9yZXF1ZXN0Q291bnQ6IGJvb2xlYW4pIHtcbiAgICBzdXBlcigpO1xuXG4gICAgdGhpcy5fcmVtYWluaW5nRXZlbnRzID0gW107XG4gICAgdGhpcy5fbWVhc3VyZUNvdW50ID0gMDtcbiAgICB0aGlzLl9wZXJmTG9nRmVhdHVyZXMgPSBfZHJpdmVyRXh0ZW5zaW9uLnBlcmZMb2dGZWF0dXJlcygpO1xuICAgIGlmICghdGhpcy5fcGVyZkxvZ0ZlYXR1cmVzLnVzZXJUaW1pbmcpIHtcbiAgICAgIC8vIFVzZXIgdGltaW5nIGlzIG5lZWRlZCBmb3IgbmF2aWdhdGlvblN0YXJ0LlxuICAgICAgdGhpcy5fcmVjZWl2ZWREYXRhID0gZmFsc2U7XG4gICAgICB0aGlzLl9yZXF1ZXN0Q291bnQgPSBmYWxzZTtcbiAgICB9XG4gIH1cblxuICBkZXNjcmliZSgpOiB7W2tleTogc3RyaW5nXTogYW55fSB7XG4gICAgdmFyIHJlcyA9IHtcbiAgICAgICdzY3JpcHRUaW1lJzogJ3NjcmlwdCBleGVjdXRpb24gdGltZSBpbiBtcywgaW5jbHVkaW5nIGdjIGFuZCByZW5kZXInLFxuICAgICAgJ3B1cmVTY3JpcHRUaW1lJzogJ3NjcmlwdCBleGVjdXRpb24gdGltZSBpbiBtcywgd2l0aG91dCBnYyBub3IgcmVuZGVyJ1xuICAgIH07XG4gICAgaWYgKHRoaXMuX3BlcmZMb2dGZWF0dXJlcy5yZW5kZXIpIHtcbiAgICAgIHJlc1sncmVuZGVyVGltZSddID0gJ3JlbmRlciB0aW1lIGluIG1zJztcbiAgICB9XG4gICAgaWYgKHRoaXMuX3BlcmZMb2dGZWF0dXJlcy5nYykge1xuICAgICAgcmVzWydnY1RpbWUnXSA9ICdnYyB0aW1lIGluIG1zJztcbiAgICAgIHJlc1snZ2NBbW91bnQnXSA9ICdnYyBhbW91bnQgaW4ga2J5dGVzJztcbiAgICAgIHJlc1snbWFqb3JHY1RpbWUnXSA9ICd0aW1lIG9mIG1ham9yIGdjcyBpbiBtcyc7XG4gICAgICBpZiAodGhpcy5fZm9yY2VHYykge1xuICAgICAgICByZXNbJ2ZvcmNlZEdjVGltZSddID0gJ2ZvcmNlZCBnYyB0aW1lIGluIG1zJztcbiAgICAgICAgcmVzWydmb3JjZWRHY0Ftb3VudCddID0gJ2ZvcmNlZCBnYyBhbW91bnQgaW4ga2J5dGVzJztcbiAgICAgIH1cbiAgICB9XG4gICAgaWYgKHRoaXMuX3JlY2VpdmVkRGF0YSkge1xuICAgICAgcmVzWydyZWNlaXZlZERhdGEnXSA9ICdlbmNvZGVkIGJ5dGVzIHJlY2VpdmVkIHNpbmNlIG5hdmlnYXRpb25TdGFydCc7XG4gICAgfVxuICAgIGlmICh0aGlzLl9yZXF1ZXN0Q291bnQpIHtcbiAgICAgIHJlc1sncmVxdWVzdENvdW50J10gPSAnY291bnQgb2YgcmVxdWVzdHMgc2VudCBzaW5jZSBuYXZpZ2F0aW9uU3RhcnQnO1xuICAgIH1cbiAgICBpZiAodGhpcy5fY2FwdHVyZUZyYW1lcykge1xuICAgICAgaWYgKCF0aGlzLl9wZXJmTG9nRmVhdHVyZXMuZnJhbWVDYXB0dXJlKSB7XG4gICAgICAgIHZhciB3YXJuaW5nTXNnID0gJ1dBUk5JTkc6IE1ldHJpYyByZXF1ZXN0ZWQsIGJ1dCBub3Qgc3VwcG9ydGVkIGJ5IGRyaXZlcic7XG4gICAgICAgIC8vIHVzaW5nIGRvdCBzeW50YXggZm9yIG1ldHJpYyBuYW1lIHRvIGtlZXAgdGhlbSBncm91cGVkIHRvZ2V0aGVyIGluIGNvbnNvbGUgcmVwb3J0ZXJcbiAgICAgICAgcmVzWydmcmFtZVRpbWUubWVhbiddID0gd2FybmluZ01zZztcbiAgICAgICAgcmVzWydmcmFtZVRpbWUud29yc3QnXSA9IHdhcm5pbmdNc2c7XG4gICAgICAgIHJlc1snZnJhbWVUaW1lLmJlc3QnXSA9IHdhcm5pbmdNc2c7XG4gICAgICAgIHJlc1snZnJhbWVUaW1lLnNtb290aCddID0gd2FybmluZ01zZztcbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIHJlc1snZnJhbWVUaW1lLm1lYW4nXSA9ICdtZWFuIGZyYW1lIHRpbWUgaW4gbXMgKHRhcmdldDogMTYuNm1zIGZvciA2MGZwcyknO1xuICAgICAgICByZXNbJ2ZyYW1lVGltZS53b3JzdCddID0gJ3dvcnN0IGZyYW1lIHRpbWUgaW4gbXMnO1xuICAgICAgICByZXNbJ2ZyYW1lVGltZS5iZXN0J10gPSAnYmVzdCBmcmFtZSB0aW1lIGluIG1zJztcbiAgICAgICAgcmVzWydmcmFtZVRpbWUuc21vb3RoJ10gPSAncGVyY2VudGFnZSBvZiBmcmFtZXMgdGhhdCBoaXQgNjBmcHMnO1xuICAgICAgfVxuICAgIH1cbiAgICBTdHJpbmdNYXBXcmFwcGVyLmZvckVhY2godGhpcy5fbWljcm9NZXRyaWNzLFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAoZGVzYywgbmFtZSkgPT4geyBTdHJpbmdNYXBXcmFwcGVyLnNldChyZXMsIG5hbWUsIGRlc2MpOyB9KTtcbiAgICByZXR1cm4gcmVzO1xuICB9XG5cbiAgYmVnaW5NZWFzdXJlKCk6IFByb21pc2U8YW55PiB7XG4gICAgdmFyIHJlc3VsdFByb21pc2UgPSBQcm9taXNlV3JhcHBlci5yZXNvbHZlKG51bGwpO1xuICAgIGlmICh0aGlzLl9mb3JjZUdjKSB7XG4gICAgICByZXN1bHRQcm9taXNlID0gcmVzdWx0UHJvbWlzZS50aGVuKChfKSA9PiB0aGlzLl9kcml2ZXJFeHRlbnNpb24uZ2MoKSk7XG4gICAgfVxuICAgIHJldHVybiByZXN1bHRQcm9taXNlLnRoZW4oKF8pID0+IHRoaXMuX2JlZ2luTWVhc3VyZSgpKTtcbiAgfVxuXG4gIGVuZE1lYXN1cmUocmVzdGFydDogYm9vbGVhbik6IFByb21pc2U8e1trZXk6IHN0cmluZ106IGFueX0+IHtcbiAgICBpZiAodGhpcy5fZm9yY2VHYykge1xuICAgICAgcmV0dXJuIHRoaXMuX2VuZFBsYWluTWVhc3VyZUFuZE1lYXN1cmVGb3JjZUdjKHJlc3RhcnQpO1xuICAgIH0gZWxzZSB7XG4gICAgICByZXR1cm4gdGhpcy5fZW5kTWVhc3VyZShyZXN0YXJ0KTtcbiAgICB9XG4gIH1cblxuICBfZW5kUGxhaW5NZWFzdXJlQW5kTWVhc3VyZUZvcmNlR2MocmVzdGFydE1lYXN1cmU6IGJvb2xlYW4pIHtcbiAgICByZXR1cm4gdGhpcy5fZW5kTWVhc3VyZSh0cnVlKS50aGVuKChtZWFzdXJlVmFsdWVzKSA9PiB7XG4gICAgICAvLyBkaXNhYmxlIGZyYW1lIGNhcHR1cmUgZm9yIG1lYXN1cmVtZW50cyBkdXJpbmcgZm9yY2VkIGdjXG4gICAgICB2YXIgb3JpZ2luYWxGcmFtZUNhcHR1cmVWYWx1ZSA9IHRoaXMuX2NhcHR1cmVGcmFtZXM7XG4gICAgICB0aGlzLl9jYXB0dXJlRnJhbWVzID0gZmFsc2U7XG4gICAgICByZXR1cm4gdGhpcy5fZHJpdmVyRXh0ZW5zaW9uLmdjKClcbiAgICAgICAgICAudGhlbigoXykgPT4gdGhpcy5fZW5kTWVhc3VyZShyZXN0YXJ0TWVhc3VyZSkpXG4gICAgICAgICAgLnRoZW4oKGZvcmNlR2NNZWFzdXJlVmFsdWVzKSA9PiB7XG4gICAgICAgICAgICB0aGlzLl9jYXB0dXJlRnJhbWVzID0gb3JpZ2luYWxGcmFtZUNhcHR1cmVWYWx1ZTtcbiAgICAgICAgICAgIFN0cmluZ01hcFdyYXBwZXIuc2V0KG1lYXN1cmVWYWx1ZXMsICdmb3JjZWRHY1RpbWUnLCBmb3JjZUdjTWVhc3VyZVZhbHVlc1snZ2NUaW1lJ10pO1xuICAgICAgICAgICAgU3RyaW5nTWFwV3JhcHBlci5zZXQobWVhc3VyZVZhbHVlcywgJ2ZvcmNlZEdjQW1vdW50JywgZm9yY2VHY01lYXN1cmVWYWx1ZXNbJ2djQW1vdW50J10pO1xuICAgICAgICAgICAgcmV0dXJuIG1lYXN1cmVWYWx1ZXM7XG4gICAgICAgICAgfSk7XG4gICAgfSk7XG4gIH1cblxuICBfYmVnaW5NZWFzdXJlKCk6IFByb21pc2U8YW55PiB7XG4gICAgcmV0dXJuIHRoaXMuX2RyaXZlckV4dGVuc2lvbi50aW1lQmVnaW4odGhpcy5fbWFya05hbWUodGhpcy5fbWVhc3VyZUNvdW50KyspKTtcbiAgfVxuXG4gIF9lbmRNZWFzdXJlKHJlc3RhcnQ6IGJvb2xlYW4pOiBQcm9taXNlPHtba2V5OiBzdHJpbmddOiBhbnl9PiB7XG4gICAgdmFyIG1hcmtOYW1lID0gdGhpcy5fbWFya05hbWUodGhpcy5fbWVhc3VyZUNvdW50IC0gMSk7XG4gICAgdmFyIG5leHRNYXJrTmFtZSA9IHJlc3RhcnQgPyB0aGlzLl9tYXJrTmFtZSh0aGlzLl9tZWFzdXJlQ291bnQrKykgOiBudWxsO1xuICAgIHJldHVybiB0aGlzLl9kcml2ZXJFeHRlbnNpb24udGltZUVuZChtYXJrTmFtZSwgbmV4dE1hcmtOYW1lKVxuICAgICAgICAudGhlbigoXykgPT4gdGhpcy5fcmVhZFVudGlsRW5kTWFyayhtYXJrTmFtZSkpO1xuICB9XG5cbiAgX3JlYWRVbnRpbEVuZE1hcmsobWFya05hbWU6IHN0cmluZywgbG9vcENvdW50OiBudW1iZXIgPSAwLCBzdGFydEV2ZW50ID0gbnVsbCkge1xuICAgIGlmIChsb29wQ291bnQgPiBfTUFYX1JFVFJZX0NPVU5UKSB7XG4gICAgICB0aHJvdyBuZXcgQmFzZUV4Y2VwdGlvbihgVHJpZWQgdG9vIG9mdGVuIHRvIGdldCB0aGUgZW5kaW5nIG1hcms6ICR7bG9vcENvdW50fWApO1xuICAgIH1cbiAgICByZXR1cm4gdGhpcy5fZHJpdmVyRXh0ZW5zaW9uLnJlYWRQZXJmTG9nKCkudGhlbigoZXZlbnRzKSA9PiB7XG4gICAgICB0aGlzLl9hZGRFdmVudHMoZXZlbnRzKTtcbiAgICAgIHZhciByZXN1bHQgPSB0aGlzLl9hZ2dyZWdhdGVFdmVudHModGhpcy5fcmVtYWluaW5nRXZlbnRzLCBtYXJrTmFtZSk7XG4gICAgICBpZiAoaXNQcmVzZW50KHJlc3VsdCkpIHtcbiAgICAgICAgdGhpcy5fcmVtYWluaW5nRXZlbnRzID0gZXZlbnRzO1xuICAgICAgICByZXR1cm4gcmVzdWx0O1xuICAgICAgfVxuICAgICAgdmFyIGNvbXBsZXRlciA9IFByb21pc2VXcmFwcGVyLmNvbXBsZXRlcigpO1xuICAgICAgdGhpcy5fc2V0VGltZW91dCgoKSA9PiBjb21wbGV0ZXIucmVzb2x2ZSh0aGlzLl9yZWFkVW50aWxFbmRNYXJrKG1hcmtOYW1lLCBsb29wQ291bnQgKyAxKSksXG4gICAgICAgICAgICAgICAgICAgICAgIDEwMCk7XG4gICAgICByZXR1cm4gY29tcGxldGVyLnByb21pc2U7XG4gICAgfSk7XG4gIH1cblxuICBfYWRkRXZlbnRzKGV2ZW50czogeyBba2V5OiBzdHJpbmddOiBzdHJpbmcgfVtdKSB7XG4gICAgdmFyIG5lZWRTb3J0ID0gZmFsc2U7XG4gICAgZXZlbnRzLmZvckVhY2goZXZlbnQgPT4ge1xuICAgICAgaWYgKFN0cmluZ1dyYXBwZXIuZXF1YWxzKGV2ZW50WydwaCddLCAnWCcpKSB7XG4gICAgICAgIG5lZWRTb3J0ID0gdHJ1ZTtcbiAgICAgICAgdmFyIHN0YXJ0RXZlbnQgPSB7fTtcbiAgICAgICAgdmFyIGVuZEV2ZW50ID0ge307XG4gICAgICAgIFN0cmluZ01hcFdyYXBwZXIuZm9yRWFjaChldmVudCwgKHZhbHVlLCBwcm9wKSA9PiB7XG4gICAgICAgICAgc3RhcnRFdmVudFtwcm9wXSA9IHZhbHVlO1xuICAgICAgICAgIGVuZEV2ZW50W3Byb3BdID0gdmFsdWU7XG4gICAgICAgIH0pO1xuICAgICAgICBzdGFydEV2ZW50WydwaCddID0gJ0InO1xuICAgICAgICBlbmRFdmVudFsncGgnXSA9ICdFJztcbiAgICAgICAgZW5kRXZlbnRbJ3RzJ10gPSBzdGFydEV2ZW50Wyd0cyddICsgc3RhcnRFdmVudFsnZHVyJ107XG4gICAgICAgIHRoaXMuX3JlbWFpbmluZ0V2ZW50cy5wdXNoKHN0YXJ0RXZlbnQpO1xuICAgICAgICB0aGlzLl9yZW1haW5pbmdFdmVudHMucHVzaChlbmRFdmVudCk7XG4gICAgICB9IGVsc2Uge1xuICAgICAgICB0aGlzLl9yZW1haW5pbmdFdmVudHMucHVzaChldmVudCk7XG4gICAgICB9XG4gICAgfSk7XG4gICAgaWYgKG5lZWRTb3J0KSB7XG4gICAgICAvLyBOZWVkIHRvIHNvcnQgYmVjYXVzZSBvZiB0aGUgcGg9PT0nWCcgZXZlbnRzXG4gICAgICBMaXN0V3JhcHBlci5zb3J0KHRoaXMuX3JlbWFpbmluZ0V2ZW50cywgKGEsIGIpID0+IHtcbiAgICAgICAgdmFyIGRpZmYgPSBhWyd0cyddIC0gYlsndHMnXTtcbiAgICAgICAgcmV0dXJuIGRpZmYgPiAwID8gMSA6IGRpZmYgPCAwID8gLTEgOiAwO1xuICAgICAgfSk7XG4gICAgfVxuICB9XG5cbiAgX2FnZ3JlZ2F0ZUV2ZW50cyhldmVudHM6IEFycmF5PHtba2V5OiBzdHJpbmddOiBhbnl9PiwgbWFya05hbWUpOiB7W2tleTogc3RyaW5nXTogYW55fSB7XG4gICAgdmFyIHJlc3VsdCA9IHsnc2NyaXB0VGltZSc6IDAsICdwdXJlU2NyaXB0VGltZSc6IDB9O1xuICAgIGlmICh0aGlzLl9wZXJmTG9nRmVhdHVyZXMuZ2MpIHtcbiAgICAgIHJlc3VsdFsnZ2NUaW1lJ10gPSAwO1xuICAgICAgcmVzdWx0WydtYWpvckdjVGltZSddID0gMDtcbiAgICAgIHJlc3VsdFsnZ2NBbW91bnQnXSA9IDA7XG4gICAgfVxuICAgIGlmICh0aGlzLl9wZXJmTG9nRmVhdHVyZXMucmVuZGVyKSB7XG4gICAgICByZXN1bHRbJ3JlbmRlclRpbWUnXSA9IDA7XG4gICAgfVxuICAgIGlmICh0aGlzLl9jYXB0dXJlRnJhbWVzKSB7XG4gICAgICByZXN1bHRbJ2ZyYW1lVGltZS5tZWFuJ10gPSAwO1xuICAgICAgcmVzdWx0WydmcmFtZVRpbWUuYmVzdCddID0gMDtcbiAgICAgIHJlc3VsdFsnZnJhbWVUaW1lLndvcnN0J10gPSAwO1xuICAgICAgcmVzdWx0WydmcmFtZVRpbWUuc21vb3RoJ10gPSAwO1xuICAgIH1cbiAgICBTdHJpbmdNYXBXcmFwcGVyLmZvckVhY2godGhpcy5fbWljcm9NZXRyaWNzLCAoZGVzYywgbmFtZSkgPT4geyByZXN1bHRbbmFtZV0gPSAwOyB9KTtcbiAgICBpZiAodGhpcy5fcmVjZWl2ZWREYXRhKSB7XG4gICAgICByZXN1bHRbJ3JlY2VpdmVkRGF0YSddID0gMDtcbiAgICB9XG4gICAgaWYgKHRoaXMuX3JlcXVlc3RDb3VudCkge1xuICAgICAgcmVzdWx0WydyZXF1ZXN0Q291bnQnXSA9IDA7XG4gICAgfVxuXG4gICAgdmFyIG1hcmtTdGFydEV2ZW50ID0gbnVsbDtcbiAgICB2YXIgbWFya0VuZEV2ZW50ID0gbnVsbDtcbiAgICB2YXIgZ2NUaW1lSW5TY3JpcHQgPSAwO1xuICAgIHZhciByZW5kZXJUaW1lSW5TY3JpcHQgPSAwO1xuXG4gICAgdmFyIGZyYW1lVGltZXN0YW1wcyA9IFtdO1xuICAgIHZhciBmcmFtZVRpbWVzID0gW107XG4gICAgdmFyIGZyYW1lQ2FwdHVyZVN0YXJ0RXZlbnQgPSBudWxsO1xuICAgIHZhciBmcmFtZUNhcHR1cmVFbmRFdmVudCA9IG51bGw7XG5cbiAgICB2YXIgaW50ZXJ2YWxTdGFydHM6IHtba2V5OiBzdHJpbmddOiBhbnl9ID0ge307XG4gICAgdmFyIGludGVydmFsU3RhcnRDb3VudDoge1trZXk6IHN0cmluZ106IG51bWJlcn0gPSB7fTtcbiAgICBldmVudHMuZm9yRWFjaCgoZXZlbnQpID0+IHtcbiAgICAgIHZhciBwaCA9IGV2ZW50WydwaCddO1xuICAgICAgdmFyIG5hbWUgPSBldmVudFsnbmFtZSddO1xuICAgICAgdmFyIG1pY3JvSXRlcmF0aW9ucyA9IDE7XG4gICAgICB2YXIgbWljcm9JdGVyYXRpb25zTWF0Y2ggPSBSZWdFeHBXcmFwcGVyLmZpcnN0TWF0Y2goX01JQ1JPX0lURVJBVElPTlNfUkVHRVgsIG5hbWUpO1xuICAgICAgaWYgKGlzUHJlc2VudChtaWNyb0l0ZXJhdGlvbnNNYXRjaCkpIHtcbiAgICAgICAgbmFtZSA9IG1pY3JvSXRlcmF0aW9uc01hdGNoWzFdO1xuICAgICAgICBtaWNyb0l0ZXJhdGlvbnMgPSBOdW1iZXJXcmFwcGVyLnBhcnNlSW50KG1pY3JvSXRlcmF0aW9uc01hdGNoWzJdLCAxMCk7XG4gICAgICB9XG5cbiAgICAgIGlmIChTdHJpbmdXcmFwcGVyLmVxdWFscyhwaCwgJ2InKSAmJiBTdHJpbmdXcmFwcGVyLmVxdWFscyhuYW1lLCBtYXJrTmFtZSkpIHtcbiAgICAgICAgbWFya1N0YXJ0RXZlbnQgPSBldmVudDtcbiAgICAgIH0gZWxzZSBpZiAoU3RyaW5nV3JhcHBlci5lcXVhbHMocGgsICdlJykgJiYgU3RyaW5nV3JhcHBlci5lcXVhbHMobmFtZSwgbWFya05hbWUpKSB7XG4gICAgICAgIG1hcmtFbmRFdmVudCA9IGV2ZW50O1xuICAgICAgfVxuXG4gICAgICBsZXQgaXNJbnN0YW50ID0gU3RyaW5nV3JhcHBlci5lcXVhbHMocGgsICdJJykgfHwgU3RyaW5nV3JhcHBlci5lcXVhbHMocGgsICdpJyk7XG4gICAgICBpZiAodGhpcy5fcmVxdWVzdENvdW50ICYmIFN0cmluZ1dyYXBwZXIuZXF1YWxzKG5hbWUsICdzZW5kUmVxdWVzdCcpKSB7XG4gICAgICAgIHJlc3VsdFsncmVxdWVzdENvdW50J10gKz0gMTtcbiAgICAgIH0gZWxzZSBpZiAodGhpcy5fcmVjZWl2ZWREYXRhICYmIFN0cmluZ1dyYXBwZXIuZXF1YWxzKG5hbWUsICdyZWNlaXZlZERhdGEnKSAmJiBpc0luc3RhbnQpIHtcbiAgICAgICAgcmVzdWx0WydyZWNlaXZlZERhdGEnXSArPSBldmVudFsnYXJncyddWydlbmNvZGVkRGF0YUxlbmd0aCddO1xuICAgICAgfSBlbHNlIGlmIChTdHJpbmdXcmFwcGVyLmVxdWFscyhuYW1lLCAnbmF2aWdhdGlvblN0YXJ0JykpIHtcbiAgICAgICAgLy8gV2UgY291bnQgZGF0YSArIHJlcXVlc3RzIHNpbmNlIHRoZSBsYXN0IG5hdmlnYXRpb25TdGFydFxuICAgICAgICAvLyAodGhlcmUgbWlnaHQgYmUgY2hyb21lIGV4dGVuc2lvbnMgbG9hZGVkIGJ5IHNlbGVuaXVtIGJlZm9yZSBvdXIgcGFnZSwgc28gdGhlcmVcbiAgICAgICAgLy8gd2lsbCBsaWtlbHkgYmUgbW9yZSB0aGFuIG9uZSBuYXZpZ2F0aW9uU3RhcnQpLlxuICAgICAgICBpZiAodGhpcy5fcmVjZWl2ZWREYXRhKSB7XG4gICAgICAgICAgcmVzdWx0WydyZWNlaXZlZERhdGEnXSA9IDA7XG4gICAgICAgIH1cbiAgICAgICAgaWYgKHRoaXMuX3JlcXVlc3RDb3VudCkge1xuICAgICAgICAgIHJlc3VsdFsncmVxdWVzdENvdW50J10gPSAwO1xuICAgICAgICB9XG4gICAgICB9XG4gICAgICBpZiAoaXNQcmVzZW50KG1hcmtTdGFydEV2ZW50KSAmJiBpc0JsYW5rKG1hcmtFbmRFdmVudCkgJiZcbiAgICAgICAgICBldmVudFsncGlkJ10gPT09IG1hcmtTdGFydEV2ZW50WydwaWQnXSkge1xuICAgICAgICBpZiAoU3RyaW5nV3JhcHBlci5lcXVhbHMocGgsICdiJykgJiYgU3RyaW5nV3JhcHBlci5lcXVhbHMobmFtZSwgX01BUktfTkFNRV9GUkFNRV9DQVBVVFJFKSkge1xuICAgICAgICAgIGlmIChpc1ByZXNlbnQoZnJhbWVDYXB0dXJlU3RhcnRFdmVudCkpIHtcbiAgICAgICAgICAgIHRocm93IG5ldyBCYXNlRXhjZXB0aW9uKCdjYW4gY2FwdHVyZSBmcmFtZXMgb25seSBvbmNlIHBlciBiZW5jaG1hcmsgcnVuJyk7XG4gICAgICAgICAgfVxuICAgICAgICAgIGlmICghdGhpcy5fY2FwdHVyZUZyYW1lcykge1xuICAgICAgICAgICAgdGhyb3cgbmV3IEJhc2VFeGNlcHRpb24oXG4gICAgICAgICAgICAgICAgJ2ZvdW5kIHN0YXJ0IGV2ZW50IGZvciBmcmFtZSBjYXB0dXJlLCBidXQgZnJhbWUgY2FwdHVyZSB3YXMgbm90IHJlcXVlc3RlZCBpbiBiZW5jaHByZXNzJylcbiAgICAgICAgICB9XG4gICAgICA