UNPKG

@angular/benchpress

Version:

Benchpress - a framework for e2e performance tests

394 lines 55.6 kB
/** * @license * Copyright Google LLC All Rights Reserved. * * Use of this source code is governed by an MIT-style license that can be * found in the LICENSE file at https://angular.io/license */ var PerflogMetric_1; import { __decorate, __metadata, __param } from "tslib"; import { Inject, Injectable, InjectionToken } from '@angular/core'; import { Options } from '../common_options'; import { Metric } from '../metric'; import { WebDriverExtension } from '../web_driver_extension'; /** * A metric that reads out the performance log */ let PerflogMetric = PerflogMetric_1 = class PerflogMetric extends Metric { /** * @param driverExtension * @param setTimeout * @param microMetrics Name and description of metrics provided via console.time / console.timeEnd * @param ignoreNavigation If true, don't measure from navigationStart events. These events are * usually triggered by a page load, but can also be triggered when adding iframes to the DOM. **/ constructor(_driverExtension, _setTimeout, _microMetrics, _forceGc, _captureFrames, _receivedData, _requestCount, _ignoreNavigation) { super(); this._driverExtension = _driverExtension; this._setTimeout = _setTimeout; this._microMetrics = _microMetrics; this._forceGc = _forceGc; this._captureFrames = _captureFrames; this._receivedData = _receivedData; this._requestCount = _requestCount; this._ignoreNavigation = _ignoreNavigation; 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() { const 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) { const 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'; } } for (const name in this._microMetrics) { res[name] = this._microMetrics[name]; } return res; } beginMeasure() { let resultPromise = Promise.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); } } /** @internal */ _endPlainMeasureAndMeasureForceGc(restartMeasure) { return this._endMeasure(true).then((measureValues) => { // disable frame capture for measurements during forced gc const originalFrameCaptureValue = this._captureFrames; this._captureFrames = false; return this._driverExtension.gc() .then((_) => this._endMeasure(restartMeasure)) .then((forceGcMeasureValues) => { this._captureFrames = originalFrameCaptureValue; measureValues['forcedGcTime'] = forceGcMeasureValues['gcTime']; measureValues['forcedGcAmount'] = forceGcMeasureValues['gcAmount']; return measureValues; }); }); } _beginMeasure() { return this._driverExtension.timeBegin(this._markName(this._measureCount++)); } _endMeasure(restart) { const markName = this._markName(this._measureCount - 1); const 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 Error(`Tried too often to get the ending mark: ${loopCount}`); } return this._driverExtension.readPerfLog().then((events) => { this._addEvents(events); const result = this._aggregateEvents(this._remainingEvents, markName); if (result) { this._remainingEvents = events; return result; } let resolve; const promise = new Promise(res => { resolve = res; }); this._setTimeout(() => resolve(this._readUntilEndMark(markName, loopCount + 1)), 100); return promise; }); } _addEvents(events) { let needSort = false; events.forEach(event => { if (event['ph'] === 'X') { needSort = true; const startEvent = {}; const endEvent = {}; for (const prop in event) { startEvent[prop] = event[prop]; endEvent[prop] = event[prop]; } 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 this._remainingEvents.sort((a, b) => { const diff = a['ts'] - b['ts']; return diff > 0 ? 1 : diff < 0 ? -1 : 0; }); } } _aggregateEvents(events, markName) { const 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; } for (const name in this._microMetrics) { result[name] = 0; } if (this._receivedData) { result['receivedData'] = 0; } if (this._requestCount) { result['requestCount'] = 0; } let markStartEvent = null; let markEndEvent = null; events.forEach((event) => { const ph = event['ph']; const name = event['name']; // Here we are determining if this is the event signaling the start or end of our performance // testing (this is triggered by us calling #timeBegin and #timeEnd). // // Previously, this was done by checking that the event name matched our mark name and that // the phase was either "B" or "E" ("begin" or "end"). However, since Chrome v90 this is // showing up as "-bpstart" and "-bpend" ("benchpress start/end"), which is what one would // actually expect since that is the mark name used in ChromeDriverExtension - see the // #timeBegin and #timeEnd implementations in chrome_driver_extension.ts. For // backwards-compatibility with Chrome v89 (and older), we do both checks: the phase-based // one ("B" or "E") and event name-based (the "-bp(start/end)" suffix). const isStartEvent = (ph === 'B' && name === markName) || name === markName + '-bpstart'; const isEndEvent = (ph === 'E' && name === markName) || name === markName + '-bpend'; if (isStartEvent) { markStartEvent = event; } else if (ph === 'I' && name === 'navigationStart' && !this._ignoreNavigation) { // if a benchmark measures reload of a page, use the last // navigationStart as begin event markStartEvent = event; } else if (isEndEvent) { markEndEvent = event; } }); if (!markStartEvent || !markEndEvent) { // not all events have been received, no further processing for now return null; } if (markStartEvent.pid !== markEndEvent.pid) { result['invalid'] = 1; } let gcTimeInScript = 0; let renderTimeInScript = 0; const frameTimestamps = []; const frameTimes = []; let frameCaptureStartEvent = null; let frameCaptureEndEvent = null; const intervalStarts = {}; const intervalStartCount = {}; let inMeasureRange = false; events.forEach((event) => { const ph = event['ph']; let name = event['name']; let microIterations = 1; const microIterationsMatch = name.match(_MICRO_ITERATIONS_REGEX); if (microIterationsMatch) { name = microIterationsMatch[1]; microIterations = parseInt(microIterationsMatch[2], 10); } if (event === markStartEvent) { inMeasureRange = true; } else if (event === markEndEvent) { inMeasureRange = false; } if (!inMeasureRange || event['pid'] !== markStartEvent['pid']) { return; } if (this._requestCount && name === 'sendRequest') { result['requestCount'] += 1; } else if (this._receivedData && name === 'receivedData' && ph === 'I') { result['receivedData'] += event['args']['encodedDataLength']; } if (ph === 'B' && name === _MARK_NAME_FRAME_CAPTURE) { if (frameCaptureStartEvent) { throw new Error('can capture frames only once per benchmark run'); } if (!this._captureFrames) { throw new Error('found start event for frame capture, but frame capture was not requested in benchpress'); } frameCaptureStartEvent = event; } else if (ph === 'E' && name === _MARK_NAME_FRAME_CAPTURE) { if (!frameCaptureStartEvent) { throw new Error('missing start event for frame capture'); } frameCaptureEndEvent = event; } if (ph === 'I' && frameCaptureStartEvent && !frameCaptureEndEvent && name === 'frame') { frameTimestamps.push(event['ts']); if (frameTimestamps.length >= 2) { frameTimes.push(frameTimestamps[frameTimestamps.length - 1] - frameTimestamps[frameTimestamps.length - 2]); } } if (ph === 'B') { if (!intervalStarts[name]) { intervalStartCount[name] = 1; intervalStarts[name] = event; } else { intervalStartCount[name]++; } } else if ((ph === 'E') && intervalStarts[name]) { intervalStartCount[name]--; if (intervalStartCount[name] === 0) { const startEvent = intervalStarts[name]; const duration = (event['ts'] - startEvent['ts']); intervalStarts[name] = null; if (name === 'gc') { result['gcTime'] += duration; const amount = (startEvent['args']['usedHeapSize'] - event['args']['usedHeapSize']) / 1000; result['gcAmount'] += amount; const majorGc = event['args']['majorGc']; if (majorGc && majorGc) { result['majorGcTime'] += duration; } if (intervalStarts['script']) { gcTimeInScript += duration; } } else if (name === 'render') { result['renderTime'] += duration; if (intervalStarts['script']) { renderTimeInScript += duration; } } else if (name === 'script') { result['scriptTime'] += duration; } else if (this._microMetrics[name]) { result[name] += duration / microIterations; } } } }); if (frameCaptureStartEvent && !frameCaptureEndEvent) { throw new Error('missing end event for frame capture'); } if (this._captureFrames && !frameCaptureStartEvent) { throw new Error('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; const 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}`; } }; PerflogMetric.SET_TIMEOUT = new InjectionToken('PerflogMetric.setTimeout'); PerflogMetric.IGNORE_NAVIGATION = new InjectionToken('PerflogMetric.ignoreNavigation'); PerflogMetric.PROVIDERS = [ { provide: PerflogMetric_1, deps: [ WebDriverExtension, PerflogMetric_1.SET_TIMEOUT, Options.MICRO_METRICS, Options.FORCE_GC, Options.CAPTURE_FRAMES, Options.RECEIVED_DATA, Options.REQUEST_COUNT, PerflogMetric_1.IGNORE_NAVIGATION ] }, { provide: PerflogMetric_1.SET_TIMEOUT, useValue: (fn, millis) => setTimeout(fn, millis) }, { provide: PerflogMetric_1.IGNORE_NAVIGATION, useValue: false } ]; PerflogMetric = PerflogMetric_1 = __decorate([ Injectable(), __param(1, Inject(PerflogMetric_1.SET_TIMEOUT)), __param(2, Inject(Options.MICRO_METRICS)), __param(3, Inject(Options.FORCE_GC)), __param(4, Inject(Options.CAPTURE_FRAMES)), __param(5, Inject(Options.RECEIVED_DATA)), __param(6, Inject(Options.REQUEST_COUNT)), __param(7, Inject(PerflogMetric_1.IGNORE_NAVIGATION)), __metadata("design:paramtypes", [WebDriverExtension, Function, Object, Boolean, Boolean, Boolean, Boolean, Boolean]) ], PerflogMetric); export { PerflogMetric }; const _MICRO_ITERATIONS_REGEX = /(.+)\*(\d+)$/; const _MAX_RETRY_COUNT = 20; const _MARK_NAME_PREFIX = 'benchpress'; const _MARK_NAME_FRAME_CAPTURE = 'frameCapture'; // using 17ms as a somewhat looser threshold, instead of 16.6666ms const _FRAME_TIME_SMOOTH_THRESHOLD = 17; //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicGVyZmxvZ19tZXRyaWMuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi8uLi8uLi8uLi9wYWNrYWdlcy9iZW5jaHByZXNzL3NyYy9tZXRyaWMvcGVyZmxvZ19tZXRyaWMudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUE7Ozs7OztHQU1HOzs7QUFFSCxPQUFPLEVBQUMsTUFBTSxFQUFFLFVBQVUsRUFBRSxjQUFjLEVBQUMsTUFBTSxlQUFlLENBQUM7QUFFakUsT0FBTyxFQUFDLE9BQU8sRUFBQyxNQUFNLG1CQUFtQixDQUFDO0FBQzFDLE9BQU8sRUFBQyxNQUFNLEVBQUMsTUFBTSxXQUFXLENBQUM7QUFDakMsT0FBTyxFQUFnQyxrQkFBa0IsRUFBQyxNQUFNLHlCQUF5QixDQUFDO0FBRzFGOztHQUVHO0FBRUgsSUFBYSxhQUFhLHFCQUExQixNQUFhLGFBQWMsU0FBUSxNQUFNO0lBd0J2Qzs7Ozs7O1FBTUk7SUFDSixZQUNZLGdCQUFvQyxFQUNELFdBQXFCLEVBQ3pCLGFBQXNDLEVBQzNDLFFBQWlCLEVBQ1gsY0FBdUIsRUFDeEIsYUFBc0IsRUFDdEIsYUFBc0IsRUFDWixpQkFBMEI7UUFDN0UsS0FBSyxFQUFFLENBQUM7UUFSRSxxQkFBZ0IsR0FBaEIsZ0JBQWdCLENBQW9CO1FBQ0QsZ0JBQVcsR0FBWCxXQUFXLENBQVU7UUFDekIsa0JBQWEsR0FBYixhQUFhLENBQXlCO1FBQzNDLGFBQVEsR0FBUixRQUFRLENBQVM7UUFDWCxtQkFBYyxHQUFkLGNBQWMsQ0FBUztRQUN4QixrQkFBYSxHQUFiLGFBQWEsQ0FBUztRQUN0QixrQkFBYSxHQUFiLGFBQWEsQ0FBUztRQUNaLHNCQUFpQixHQUFqQixpQkFBaUIsQ0FBUztRQUc3RSxJQUFJLENBQUMsZ0JBQWdCLEdBQUcsRUFBRSxDQUFDO1FBQzNCLElBQUksQ0FBQyxhQUFhLEdBQUcsQ0FBQyxDQUFDO1FBQ3ZCLElBQUksQ0FBQyxnQkFBZ0IsR0FBRyxnQkFBZ0IsQ0FBQyxlQUFl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QUMsQ0FBQTtJQUN4QixXQUFBLE1BQU0sQ0FBQyxPQUFPLENBQUMsY0FBYyxDQUFDLENBQUE7SUFDOUIsV0FBQSxNQUFNLENBQUMsT0FBTyxDQUFDLGFBQWEsQ0FBQyxDQUFBO0lBQzdCLFdBQUEsTUFBTSxDQUFDLE9BQU8sQ0FBQyxhQUFhLENBQUMsQ0FBQTtJQUM3QixXQUFBLE1BQU0sQ0FBQyxlQUFhLENBQUMsaUJBQWlCLENBQUMsQ0FBQTtxQ0FQZCxrQkFBa0I7UUFDWSxRQUFRO0dBakN6RCxhQUFhLENBd1h6QjtTQXhYWSxhQUFhO0FBMFgxQixNQUFNLHVCQUF1QixHQUFHLGNBQWMsQ0FBQztBQUUvQyxNQUFNLGdCQUFnQixHQUFHLEVBQUUsQ0FBQztBQUM1QixNQUFNLGlCQUFpQixHQUFHLFlBQVksQ0FBQztBQUV2QyxNQUFNLHdCQUF3QixHQUFHLGNBQWMsQ0FBQztBQUNoRCxrRUFBa0U7QUFDbEUsTUFBTSw0QkFBNEIsR0FBRyxFQUFFLENBQUMiLCJzb3VyY2VzQ29udGVudCI6WyIvKipcbiAqIEBsaWNlbnNlXG4gKiBDb3B5cmlnaHQgR29vZ2xlIExMQyBBbGwgUmlnaHRzIFJlc2VydmVkLlxuICpcbiAqIFVzZSBvZiB0aGlzIHNvdXJjZSBjb2RlIGlzIGdvdmVybmVkIGJ5IGFuIE1JVC1zdHlsZSBsaWNlbnNlIHRoYXQgY2FuIGJlXG4gKiBmb3VuZCBpbiB0aGUgTElDRU5TRSBmaWxlIGF0IGh0dHBzOi8vYW5ndWxhci5pby9saWNlbnNlXG4gKi9cblxuaW1wb3J0IHtJbmplY3QsIEluamVjdGFibGUsIEluamVjdGlvblRva2VufSBmcm9tICdAYW5ndWxhci9jb3JlJztcblxuaW1wb3J0IHtPcHRpb25zfSBmcm9tICcuLi9jb21tb25fb3B0aW9ucyc7XG5pbXBvcnQge01ldHJpY30gZnJvbSAnLi4vbWV0cmljJztcbmltcG9ydCB7UGVyZkxvZ0V2ZW50LCBQZXJmTG9nRmVhdHVyZXMsIFdlYkRyaXZlckV4dGVuc2lvbn0gZnJvbSAnLi4vd2ViX2RyaXZlcl9leHRlbnNpb24nO1xuXG5cbi8qKlxuICogQSBtZXRyaWMgdGhhdCByZWFkcyBvdXQgdGhlIHBlcmZvcm1hbmNlIGxvZ1xuICovXG5ASW5qZWN0YWJsZSgpXG5leHBvcnQgY2xhc3MgUGVyZmxvZ01ldHJpYyBleHRlbmRzIE1ldHJpYyB7XG4gIHN0YXRpYyBTRVRfVElNRU9VVCA9IG5ldyBJbmplY3Rpb25Ub2tlbignUGVyZmxvZ01ldHJpYy5zZXRUaW1lb3V0Jyk7XG4gIHN0YXRpYyBJR05PUkVfTkFWSUdBVElPTiA9IG5ldyBJbmplY3Rpb25Ub2tlbignUGVyZmxvZ01ldHJpYy5pZ25vcmVOYXZpZ2F0aW9uJyk7XG4gIHN0YXRpYyBQUk9WSURFUlMgPSBbXG4gICAge1xuICAgICAgcHJvdmlkZTogUGVyZmxvZ01ldHJpYyxcbiAgICAgIGRlcHM6XG4gICAgICAgICAgW1xuICAgICAgICAgICAgV2ViRHJpdmVyRXh0ZW5zaW9uLCBQZXJmbG9nTWV0cmljLlNFVF9USU1FT1VULCBPcHRpb25zLk1JQ1JPX01FVFJJQ1MsIE9wdGlvbnMuRk9SQ0VfR0MsXG4gICAgICAgICAgICBPcHRpb25zLkNBUFRVUkVfRlJBTUVTLCBPcHRpb25zLlJFQ0VJVkVEX0RBVEEsIE9wdGlvbnMuUkVRVUVTVF9DT1VOVCxcbiAgICAgICAgICAgIFBlcmZsb2dNZXRyaWMuSUdOT1JFX05BVklHQVRJT05cbiAgICAgICAgICBdXG4gICAgfSxcbiAgICB7XG4gICAgICBwcm92aWRlOiBQZXJmbG9nTWV0cmljLlNFVF9USU1FT1VULFxuICAgICAgdXNlVmFsdWU6IChmbjogRnVuY3Rpb24sIG1pbGxpczogbnVtYmVyKSA9PiA8YW55PnNldFRpbWVvdXQoZm4sIG1pbGxpcylcbiAgICB9LFxuICAgIHtwcm92aWRlOiBQZXJmbG9nTWV0cmljLklHTk9SRV9OQVZJR0FUSU9OLCB1c2VWYWx1ZTogZmFsc2V9XG4gIF07XG5cbiAgcHJpdmF0ZSBfcmVtYWluaW5nRXZlbnRzOiBQZXJmTG9nRXZlbnRbXTtcbiAgcHJpdmF0ZSBfbWVhc3VyZUNvdW50OiBudW1iZXI7XG4gIHByaXZhdGUgX3BlcmZMb2dGZWF0dXJlczogUGVyZkxvZ0ZlYXR1cmVzO1xuXG4gIC8qKlxuICAgKiBAcGFyYW0gZHJpdmVyRXh0ZW5zaW9uXG4gICAqIEBwYXJhbSBzZXRUaW1lb3V0XG4gICAqIEBwYXJhbSBtaWNyb01ldHJpY3MgTmFtZSBhbmQgZGVzY3JpcHRpb24gb2YgbWV0cmljcyBwcm92aWRlZCB2aWEgY29uc29sZS50aW1lIC8gY29uc29sZS50aW1lRW5kXG4gICAqIEBwYXJhbSBpZ25vcmVOYXZpZ2F0aW9uIElmIHRydWUsIGRvbid0IG1lYXN1cmUgZnJvbSBuYXZpZ2F0aW9uU3RhcnQgZXZlbnRzLiBUaGVzZSBldmVudHMgYXJlXG4gICAqICAgdXN1YWxseSB0cmlnZ2VyZWQgYnkgYSBwYWdlIGxvYWQsIGJ1dCBjYW4gYWxzbyBiZSB0cmlnZ2VyZWQgd2hlbiBhZGRpbmcgaWZyYW1lcyB0byB0aGUgRE9NLlxuICAgKiovXG4gIGNvbnN0cnVjdG9yKFxuICAgICAgcHJpdmF0ZSBfZHJpdmVyRXh0ZW5zaW9uOiBXZWJEcml2ZXJFeHRlbnNpb24sXG4gICAgICBASW5qZWN0KFBlcmZsb2dNZXRyaWMuU0VUX1RJTUVPVVQpIHByaXZhdGUgX3NldFRpbWVvdXQ6IEZ1bmN0aW9uLFxuICAgICAgQEluamVjdChPcHRpb25zLk1JQ1JPX01FVFJJQ1MpIHByaXZhdGUgX21pY3JvTWV0cmljczoge1trZXk6IHN0cmluZ106IHN0cmluZ30sXG4gICAgICBASW5qZWN0KE9wdGlvbnMuRk9SQ0VfR0MpIHByaXZhdGUgX2ZvcmNlR2M6IGJvb2xlYW4sXG4gICAgICBASW5qZWN0KE9wdGlvbnMuQ0FQVFVSRV9GUkFNRVMpIHByaXZhdGUgX2NhcHR1cmVGcmFtZXM6IGJvb2xlYW4sXG4gICAgICBASW5qZWN0KE9wdGlvbnMuUkVDRUlWRURfREFUQSkgcHJpdmF0ZSBfcmVjZWl2ZWREYXRhOiBib29sZWFuLFxuICAgICAgQEluamVjdChPcHRpb25zLlJFUVVFU1RfQ09VTlQpIHByaXZhdGUgX3JlcXVlc3RDb3VudDogYm9vbGVhbixcbiAgICAgIEBJbmplY3QoUGVyZmxvZ01ldHJpYy5JR05PUkVfTkFWSUdBVElPTikgcHJpdmF0ZSBfaWdub3JlTmF2aWdhdGlvbjogYm9vbGVhbikge1xuICAgIHN1cGVyKCk7XG5cbiAgICB0aGlzLl9yZW1haW5pbmdFdmVudHMgPSBbXTtcbiAgICB0aGlzLl9tZWFzdXJlQ291bnQgPSAwO1xuICAgIHRoaXMuX3BlcmZMb2dGZWF0dXJlcyA9IF9kcml2ZXJFeHRlbnNpb24ucGVyZkxvZ0ZlYXR1cmVzKCk7XG4gICAgaWYgKCF0aGlzLl9wZXJmTG9nRmVhdHVyZXMudXNlclRpbWluZykge1xuICAgICAgLy8gVXNlciB0aW1pbmcgaXMgbmVlZGVkIGZvciBuYXZpZ2F0aW9uU3RhcnQuXG4gICAgICB0aGlzLl9yZWNlaXZlZERhdGEgPSBmYWxzZTtcbiAgICAgIHRoaXMuX3JlcXVlc3RDb3VudCA9IGZhbHNlO1xuICAgIH1cbiAgfVxuXG4gIG92ZXJyaWRlIGRlc2NyaWJlKCk6IHtba2V5OiBzdHJpbmddOiBzdHJpbmd9IHtcbiAgICBjb25zdCByZXM6IHtba2V5OiBzdHJpbmddOiBhbnl9ID0ge1xuICAgICAgJ3NjcmlwdFRpbWUnOiAnc2NyaXB0IGV4ZWN1dGlvbiB0aW1lIGluIG1zLCBpbmNsdWRpbmcgZ2MgYW5kIHJlbmRlcicsXG4gICAgICAncHVyZVNjcmlwdFRpbWUnOiAnc2NyaXB0IGV4ZWN1dGlvbiB0aW1lIGluIG1zLCB3aXRob3V0IGdjIG5vciByZW5kZXInXG4gICAgfTtcbiAgICBpZiAodGhpcy5fcGVyZkxvZ0ZlYXR1cmVzLnJlbmRlcikge1xuICAgICAgcmVzWydyZW5kZXJUaW1lJ10gPSAncmVuZGVyIHRpbWUgaW4gbXMnO1xuICAgIH1cbiAgICBpZiAodGhpcy5fcGVyZkxvZ0ZlYXR1cmVzLmdjKSB7XG4gICAgICByZXNbJ2djVGltZSddID0gJ2djIHRpbWUgaW4gbXMnO1xuICAgICAgcmVzWydnY0Ftb3VudCddID0gJ2djIGFtb3VudCBpbiBrYnl0ZXMnO1xuICAgICAgcmVzWydtYWpvckdjVGltZSddID0gJ3RpbWUgb2YgbWFqb3IgZ2NzIGluIG1zJztcbiAgICAgIGlmICh0aGlzLl9mb3JjZUdjKSB7XG4gICAgICAgIHJlc1snZm9yY2VkR2NUaW1lJ10gPSAnZm9yY2VkIGdjIHRpbWUgaW4gbXMnO1xuICAgICAgICByZXNbJ2ZvcmNlZEdjQW1vdW50J10gPSAnZm9yY2VkIGdjIGFtb3VudCBpbiBrYnl0ZXMnO1xuICAgICAgfVxuICAgIH1cbiAgICBpZiAodGhpcy5fcmVjZWl2ZWREYXRhKSB7XG4gICAgICByZXNbJ3JlY2VpdmVkRGF0YSddID0gJ2VuY29kZWQgYnl0ZXMgcmVjZWl2ZWQgc2luY2UgbmF2aWdhdGlvblN0YXJ0JztcbiAgICB9XG4gICAgaWYgKHRoaXMuX3JlcXVlc3RDb3VudCkge1xuICAgICAgcmVzWydyZXF1ZXN0Q291bnQnXSA9ICdjb3VudCBvZiByZXF1ZXN0cyBzZW50IHNpbmNlIG5hdmlnYXRpb25TdGFydCc7XG4gICAgfVxuICAgIGlmICh0aGlzLl9jYXB0dXJlRnJhbWVzKSB7XG4gICAgICBpZiAoIXRoaXMuX3BlcmZMb2dGZWF0dXJlcy5mcmFtZUNhcHR1cmUpIHtcbiAgICAgICAgY29uc3Qgd2FybmluZ01zZyA9ICdXQVJOSU5HOiBNZXRyaWMgcmVxdWVzdGVkLCBidXQgbm90IHN1cHBvcnRlZCBieSBkcml2ZXInO1xuICAgICAgICAvLyB1c2luZyBkb3Qgc3ludGF4IGZvciBtZXRyaWMgbmFtZSB0byBrZWVwIHRoZW0gZ3JvdXBlZCB0b2dldGhlciBpbiBjb25zb2xlIHJlcG9ydGVyXG4gICAgICAgIHJlc1snZnJhbWVUaW1lLm1lYW4nXSA9IHdhcm5pbmdNc2c7XG4gICAgICAgIHJlc1snZnJhbWVUaW1lLndvcnN0J10gPSB3YXJuaW5nTXNnO1xuICAgICAgICByZXNbJ2ZyYW1lVGltZS5iZXN0J10gPSB3YXJuaW5nTXNnO1xuICAgICAgICByZXNbJ2ZyYW1lVGltZS5zbW9vdGgnXSA9IHdhcm5pbmdNc2c7XG4gICAgICB9IGVsc2Uge1xuICAgICAgICByZXNbJ2ZyYW1lVGltZS5tZWFuJ10gPSAnbWVhbiBmcmFtZSB0aW1lIGluIG1zICh0YXJnZXQ6IDE2LjZtcyBmb3IgNjBmcHMpJztcbiAgICAgICAgcmVzWydmcmFtZVRpbWUud29yc3QnXSA9ICd3b3JzdCBmcmFtZSB0aW1lIGluIG1zJztcbiAgICAgICAgcmVzWydmcmFtZVRpbWUuYmVzdCddID0gJ2Jlc3QgZnJhbWUgdGltZSBpbiBtcyc7XG4gICAgICAgIHJlc1snZnJhbWVUaW1lLnNtb290aCddID0gJ3BlcmNlbnRhZ2Ugb2YgZnJhbWVzIHRoYXQgaGl0IDYwZnBzJztcbiAgICAgIH1cbiAgICB9XG4gICAgZm9yIChjb25zdCBuYW1lIGluIHRoaXMuX21pY3JvTWV0cmljcykge1xuICAgICAgcmVzW25hbWVdID0gdGhpcy5fbWljcm9NZXRyaWNzW25hbWVdO1xuICAgIH1cbiAgICByZXR1cm4gcmVzO1xuICB9XG5cbiAgb3ZlcnJpZGUgYmVnaW5NZWFzdXJlKCk6IFByb21pc2U8YW55PiB7XG4gICAgbGV0IHJlc3VsdFByb21pc2UgPSBQcm9taXNlLnJlc29sdmUobnVsbCk7XG4gICAgaWYgKHRoaXMuX2ZvcmNlR2MpIHtcbiAgICAgIHJlc3VsdFByb21pc2UgPSByZXN1bHRQcm9taXNlLnRoZW4oKF8pID0+IHRoaXMuX2RyaXZlckV4dGVuc2lvbi5nYygpKTtcbiAgICB9XG4gICAgcmV0dXJuIHJlc3VsdFByb21pc2UudGhlbigoXykgPT4gdGhpcy5fYmVnaW5NZWFzdXJlKCkpO1xuICB9XG5cbiAgb3ZlcnJpZGUgZW5kTWVhc3VyZShyZXN0YXJ0OiBib29sZWFuKTogUHJvbWlzZTx7W2tleTogc3RyaW5nXTogbnVtYmVyfT4ge1xuICAgIGlmICh0aGlzLl9mb3JjZUdjKSB7XG4gICAgICByZXR1cm4gdGhpcy5fZW5kUGxhaW5NZWFzdXJlQW5kTWVhc3VyZUZvcmNlR2MocmVzdGFydCk7XG4gICAgfSBlbHNlIHtcbiAgICAgIHJldHVybiB0aGlzLl9lbmRNZWFzdXJlKHJlc3RhcnQpO1xuICAgIH1cbiAgfVxuXG4gIC8qKiBAaW50ZXJuYWwgKi9cbiAgcHJpdmF0ZSBfZW5kUGxhaW5NZWFzdXJlQW5kTWVhc3VyZUZvcmNlR2MocmVzdGFydE1lYXN1cmU6IGJvb2xlYW4pIHtcbiAgICByZXR1cm4gdGhpcy5fZW5kTWVhc3VyZSh0cnVlKS50aGVuKChtZWFzdXJlVmFsdWVzKSA9PiB7XG4gICAgICAvLyBkaXNhYmxlIGZyYW1lIGNhcHR1cmUgZm9yIG1lYXN1cmVtZW50cyBkdXJpbmcgZm9yY2VkIGdjXG4gICAgICBjb25zdCBvcmlnaW5hbEZyYW1lQ2FwdHVyZVZhbHVlID0gdGhpcy5fY2FwdHVyZUZyYW1lcztcbiAgICAgIHRoaXMuX2NhcHR1cmVGcmFtZXMgPSBmYWxzZTtcbiAgICAgIHJldHVybiB0aGlzLl9kcml2ZXJFeHRlbnNpb24uZ2MoKVxuICAgICAgICAgIC50aGVuKChfKSA9PiB0aGlzLl9lbmRNZWFzdXJlKHJlc3RhcnRNZWFzdXJlKSlcbiAgICAgICAgICAudGhlbigoZm9yY2VHY01lYXN1cmVWYWx1ZXMpID0+IHtcbiAgICAgICAgICAgIHRoaXMuX2NhcHR1cmVGcmFtZXMgPSBvcmlnaW5hbEZyYW1lQ2FwdHVyZVZhbHVlO1xuICAgICAgICAgICAgbWVhc3VyZVZhbHVlc1snZm9yY2VkR2NUaW1lJ10gPSBmb3JjZUdjTWVhc3VyZVZhbHVlc1snZ2NUaW1lJ107XG4gICAgICAgICAgICBtZWFzdXJlVmFsdWVzWydmb3JjZWRHY0Ftb3VudCddID0gZm9yY2VHY01lYXN1cmVWYWx1ZXNbJ2djQW1vdW50J107XG4gICAgICAgICAgICByZXR1cm4gbWVhc3VyZVZhbHVlcztcbiAgICAgICAgICB9KTtcbiAgICB9KTtcbiAgfVxuXG4gIHByaXZhdGUgX2JlZ2luTWVhc3VyZSgpOiBQcm9taXNlPGFueT4ge1xuICAgIHJldHVybiB0aGlzLl9kcml2ZXJFeHRlbnNpb24udGltZUJlZ2luKHRoaXMuX21hcmtOYW1lKHRoaXMuX21lYXN1cmVDb3VudCsrKSk7XG4gIH1cblxuICBwcml2YXRlIF9lbmRNZWFzdXJlKHJlc3RhcnQ6IGJvb2xlYW4pOiBQcm9taXNlPHtba2V5OiBzdHJpbmddOiBudW1iZXJ9PiB7XG4gICAgY29uc3QgbWFya05hbWUgPSB0aGlzLl9tYXJrTmFtZSh0aGlzLl9tZWFzdXJlQ291bnQgLSAxKTtcbiAgICBjb25zdCBuZXh0TWFya05hbWUgPSByZXN0YXJ0ID8gdGhpcy5fbWFya05hbWUodGhpcy5fbWVhc3VyZUNvdW50KyspIDogbnVsbDtcbiAgICByZXR1cm4gdGhpcy5fZHJpdmVyRXh0ZW5zaW9uLnRpbWVFbmQobWFya05hbWUsIG5leHRNYXJrTmFtZSlcbiAgICAgICAgLnRoZW4oKF86IGFueSkgPT4gdGhpcy5fcmVhZFVudGlsRW5kTWFyayhtYXJrTmFtZSkpO1xuICB9XG5cbiAgcHJpdmF0ZSBfcmVhZFVudGlsRW5kTWFyayhcbiAgICAgIG1hcmtOYW1lOiBzdHJpbmcsIGxvb3BDb3VudDogbnVtYmVyID0gMCwgc3RhcnRFdmVudDogUGVyZkxvZ0V2ZW50fG51bGwgPSBudWxsKSB7XG4gICAgaWYgKGxvb3BDb3VudCA+IF9NQVhfUkVUUllfQ09VTlQpIHtcbiAgICAgIHRocm93IG5ldyBFcnJvcihgVHJpZWQgdG9vIG9mdGVuIHRvIGdldCB0aGUgZW5kaW5nIG1hcms6ICR7bG9vcENvdW50fWApO1xuICAgIH1cbiAgICByZXR1cm4gdGhpcy5fZHJpdmVyRXh0ZW5zaW9uLnJlYWRQZXJmTG9nKCkudGhlbigoZXZlbnRzKSA9PiB7XG4gICAgICB0aGlzLl9hZGRFdmVudHMoZXZlbnRzKTtcbiAgICAgIGNvbnN0IHJlc3VsdCA9IHRoaXMuX2FnZ3JlZ2F0ZUV2ZW50cyh0aGlzLl9yZW1haW5pbmdFdmVudHMsIG1hcmtOYW1lKTtcbiAgICAgIGlmIChyZXN1bHQpIHtcbiAgICAgICAgdGhpcy5fcmVtYWluaW5nRXZlbnRzID0gZXZlbnRzO1xuICAgICAgICByZXR1cm4gcmVzdWx0O1xuICAgICAgfVxuICAgICAgbGV0IHJlc29sdmU6IChyZXN1bHQ6IGFueSkgPT4gdm9pZDtcbiAgICAgIGNvbnN0IHByb21pc2UgPSBuZXcgUHJvbWlzZTx7W2tleTogc3RyaW5nXTogbnVtYmVyfT4ocmVzID0+IHtcbiAgICAgICAgcmVzb2x2ZSA9IHJlcztcbiAgICAgIH0pO1xuICAgICAgdGhpcy5fc2V0VGltZW91dCgoKSA9PiByZXNvbHZlKHRoaXMuX3JlYWRVbnRpbEVuZE1hcmsobWFya05hbWUsIGxvb3BDb3VudCArIDEpKSwgMTAwKTtcbiAgICAgIHJldHVybiBwcm9taXNlO1xuICAgIH0pO1xuICB9XG5cbiAgcHJpdmF0ZSBfYWRkRXZlbnRzKGV2ZW50czogUGVyZkxvZ0V2ZW50W10pIHtcbiAgICBsZXQgbmVlZFNvcnQgPSBmYWxzZTtcbiAgICBldmVudHMuZm9yRWFjaChldmVudCA9PiB7XG4gICAgICBpZiAoZXZlbnRbJ3BoJ10gPT09ICdYJykge1xuICAgICAgICBuZWVkU29ydCA9IHRydWU7XG4gICAgICAgIGNvbnN0IHN0YXJ0RXZlbnQ6IFBlcmZMb2dFdmVudCA9IHt9O1xuICAgICAgICBjb25zdCBlbmRFdmVudDogUGVyZkxvZ0V2ZW50ID0ge307XG4gICAgICAgIGZvciAoY29uc3QgcHJvcCBpbiBldmVudCkge1xuICAgICAgICAgIHN0YXJ0RXZlbnRbcHJvcF0gPSBldmVudFtwcm9wXTtcbiAgICAgICAgICBlbmRFdmVudFtwcm9wXSA9IGV2ZW50W3Byb3BdO1xuICAgICAgICB9XG4gICAgICAgIHN0YXJ0RXZlbnRbJ3BoJ10gPSAnQic7XG4gICAgICAgIGVuZEV2ZW50WydwaCddID0gJ0UnO1xuICAgICAgICBlbmRFdmVudFsndHMnXSA9IHN0YXJ0RXZlbnRbJ3RzJ10hICsgc3RhcnRFdmVudFsnZHVyJ10hO1xuICAgICAgICB0aGlzLl9yZW1haW5pbmdFdmVudHMucHVzaChzdGFydEV2ZW50KTtcbiAgICAgICAgdGhpcy5fcmVtYWluaW5nRXZlbnRzLnB1c2goZW5kRXZlbnQpO1xuICAgICAgfSBlbHNlIHtcbiAgICAgICAgdGhpcy5fcmVtYWluaW5nRXZlbnRzLnB1c2goZXZlbnQpO1xuICAgICAgfVxuICAgIH0pO1xuICAgIGlmIChuZWVkU29ydCkge1xuICAgICAgLy8gTmVlZCB0byBzb3J0IGJlY2F1c2Ugb2YgdGhlIHBoPT09J1gnIGV2ZW50c1xuICAgICAgdGhpcy5fcmVtYWluaW5nRXZlbnRzLnNvcnQoKGEsIGIpID0+IHtcbiAgICAgICAgY29uc3QgZGlmZiA9IGFbJ3RzJ10hIC0gYlsndHMnXSE7XG4gICAgICAgIHJldHVybiBkaWZmID4gMCA/IDEgOiBkaWZmIDwgMCA/IC0xIDogMDtcbiAgICAgIH0pO1xuICAgIH1cbiAgfVxuXG4gIHByaXZhdGUgX2FnZ3JlZ2F0ZUV2ZW50cyhldmVudHM6IFBlcmZMb2dFdmVudFtdLCBtYXJrTmFtZTogc3RyaW5nKToge1trZXk6IHN0cmluZ106IG51bWJlcn18bnVsbCB7XG4gICAgY29uc3QgcmVzdWx0OiB7W2tleTogc3RyaW5nXTogbnVtYmVyfSA9IHsnc2NyaXB0VGltZSc6IDAsICdwdXJlU2NyaXB0VGltZSc6IDB9O1xuICAgIGlmICh0aGlzLl9wZXJmTG9nRmVhdHVyZXMuZ2MpIHtcbiAgICAgIHJlc3VsdFsnZ2NUaW1lJ10gPSAwO1xuICAgICAgcmVzdWx0WydtYWpvckdjVGltZSddID0gMDtcbiAgICAgIHJlc3VsdFsnZ2NBbW91bnQnXSA9IDA7XG4gICAgfVxuICAgIGlmICh0aGlzLl9wZXJmTG9nRmVhdHVyZXMucmVuZGVyKSB7XG4gICAgICByZXN1bHRbJ3JlbmRlclRpbWUnXSA9IDA7XG4gICAgfVxuICAgIGlmICh0aGlzLl9jYXB0dXJlRnJhbWVzKSB7XG4gICAgICByZXN1bHRbJ2ZyYW1lVGltZS5tZWFuJ10gPSAwO1xuICAgICAgcmVzdWx0WydmcmFtZVRpbWUuYmVzdCddID0gMDtcbiAgICAgIHJlc3VsdFsnZnJhbWVUaW1lLndvcnN0J10gPSAwO1xuICAgICAgcmVzdWx0WydmcmFtZVRpbWUuc21vb3RoJ10gPSAwO1xuICAgIH1cbiAgICBmb3IgKGNvbnN0IG5hbWUgaW4gdGhpcy5fbWljcm9NZXRyaWNzKSB7XG4gICAgICByZXN1bHRbbmFtZV0gPSAwO1xuICAgIH1cbiAgICBpZiAodGhpcy5fcmVjZWl2ZWREYXRhKSB7XG4gICAgICByZXN1bHRbJ3JlY2VpdmVkRGF0YSddID0gMDtcbiAgICB9XG4gICAgaWYgKHRoaXMuX3JlcXVlc3RDb3VudCkge1xuICAgICAgcmVzdWx0WydyZXF1ZXN0Q291bnQnXSA9IDA7XG4gICAgfVxuXG4gICAgbGV0IG1hcmtTdGFydEV2ZW50OiBQZXJmTG9nRXZlbnQgPSBudWxsITtcbiAgICBsZXQgbWFya0VuZEV2ZW50OiBQZXJmTG9nRXZlbnQgPSBudWxsITtcbiAgICBldmVudHMuZm9yRWFjaCgoZXZlbnQpID0+IHtcbiAgICAgIGNvbnN0IHBoID0gZXZlbnRbJ3BoJ107XG4gICAgICBjb25zdCBuYW1lID0gZXZlbnRbJ25hbWUnXTtcblxuICAgICAgLy8gSGVyZSB3ZSBhcmUgZGV0ZXJtaW5pbmcgaWYgdGhpcyBpcyB0aGUgZXZlbnQgc2lnbmFsaW5nIHRoZSBzdGFydCBvciBlbmQgb2Ygb3VyIHBlcmZvcm1hbmNlXG4gICAgICAvLyB0ZXN0aW5nICh0aGlzIGlzIHRyaWdnZXJlZCBieSB1cyBjYWxsaW5nICN0aW1lQmVnaW4gYW5kICN0aW1lRW5kKS5cbiAgICAgIC8vXG4gICAgICAvLyBQcmV2aW91c2x5LCB0aGlzIHdhcyBkb25lIGJ5IGNoZWNraW5nIHRoYXQgdGhlIGV2ZW50IG5hbWUgbWF0Y2hlZCBvdXIgbWFyayBuYW1lIGFuZCB0aGF0XG4gICAgICAvLyB0aGUgcGhhc2Ugd2FzIGVpdGhlciBcIkJcIiBvciBcIkVcIiAoXCJiZWdpblwiIG9yIFwiZW5kXCIpLiBIb3dldmVyLCBzaW5jZSBDaHJvbWUgdjkwIHRoaXMgaXNcbiAgICAgIC8vIHNob3dpbmcgdXAgYXMgXCItYnBzdGFydFwiIGFuZCBcIi1icGVuZFwiIChcImJlbmNocHJlc3Mgc3RhcnQvZW5kXCIpLCB3aGljaCBpcyB3aGF0IG9uZSB3b3VsZFxuICAgICAgLy8gYWN0dWFsbHkgZXhwZWN0IHNpbmNlIHRoYXQgaXMgdGhlIG1hcmsgbmFtZSB1c2VkIGluIENocm9tZURyaXZlckV4dGVuc2lvbiAtIHNlZSB0aGVcbiAgICAgIC8vICN0aW1lQmVnaW4gYW5kICN0aW1lRW5kIGltcGxlbWVudGF0aW9ucyBpbiBjaHJvbWVfZHJpdmVyX2V4dGVuc2lvbi50cy4gRm9yXG4gICAgICAvLyBiYWNrd2FyZHMtY29tcGF0aWJpbGl0eSB3aXRoIENocm9tZSB2ODkgKGFuZCBvbGRlciksIHdlIGRvIGJvdGggY2hlY2tzOiB0aGUgcGhhc2UtYmFzZWRcbiAgICAgIC8vIG9uZSAoXCJCXCIgb3IgXCJFXCIpIGFuZCBldmVudCBuYW1lLWJhc2VkICh0aGUgXCItYnAoc3RhcnQvZW5kKVwiIHN1ZmZpeCkuXG4gICAgICBjb25zdCBpc1N0YXJ0RXZlbnQgPSAocGggPT09ICdCJyAmJiBuYW1lID09PSBtYXJrTmFtZSkgfHwgbmFtZSA9PT0gbWFya05hbWUgKyAnLWJwc3RhcnQnO1xuICAgICAgY29uc3QgaXNFbmRFdmVudCA9IChwaCA9PT0gJ0UnICYmIG5hbWUgPT09IG1hcmtOYW1lKSB8fCBuYW1lID09PSBtYXJrTmFtZSArICctYnBlbmQnO1xuICAgICAgaWYgKGlzU3RhcnRFdmVudCkge1xuICAgICAgICBtYXJrU3RhcnRFdmVudCA9IGV2ZW50O1xuICAgICAgfSBlbHNlIGlmIChwaCA9PT0gJ0knICYmIG5hbWUgPT09ICduYXZpZ2F0aW9uU3RhcnQnICYmICF0aGlzLl9pZ25vcmVOYXZpZ2F0aW9uKSB7XG4gICAgICAgIC8vIGlmIGEgYmVuY2htYXJrIG1lYXN1cmVzIHJlbG9hZCBvZiBhIHBhZ2UsIHVzZSB0aGUgbGFzdFxuICAgICAgICAvLyBuYXZpZ2F0aW9uU3RhcnQgYXMgYmVnaW4gZXZlbnRcbiAgICAgICAgbWFya1N0YXJ0RXZlbnQgPSBldmVudDtcbiAgICAgIH0gZWxzZSBpZiAoaXNFbmRFdmVudCkge1xuICAgICAgICBtYXJrRW5kRXZlbnQgPSBldmVudDtcbiAgICAgIH1cbiAgICB9KTtcbiAgICBpZiAoIW1hcmtTdGFydEV2ZW50IHx8ICFtYXJrRW5kRXZlbnQpIHtcbiAgICAgIC8vIG5vdCBhbGwgZXZlbnRzIGhhdmUgYmVlbiByZWNlaXZlZCwgbm8gZnVydGhlciBwcm9jZXNzaW5nIGZvciBub3dcbiAgICAgIHJldHVybiBudWxsO1xuICAgIH1cbiAgICBpZiAobWFya1N0YXJ0RXZlbnQucGlkICE9PSBtYXJrRW5kRXZlbnQucGlkKSB7XG4gICAgICByZXN1bHRbJ2ludmFsaWQnXSA9IDE7XG4gICAgfVxuXG4gICAgbGV0IGdjVGltZUluU2NyaXB0ID0gMDtcbiAgICBsZXQgcmVuZGVyVGltZUluU2NyaXB0ID0gMDtcblxuICAgIGNvbnN0IGZyYW1lVGltZXN0YW1wczogbnVtYmVyW10gPSBbXTtcbiAgICBjb25zdCBmcmFtZVRpbWVzOiBudW1iZXJbXSA9IFtdO1xuICAgIGxldCBmcmFtZUNhcHR1cmVTdGFydEV2ZW50OiBQZXJmTG9nRXZlbnR8bnVsbCA9IG51bGw7XG4gICAgbGV0IGZyYW1lQ2FwdHVyZUVuZEV2ZW50OiBQZXJmTG9nRXZlbnR8bnVsbCA9IG51bGw7XG5cbiAgICBjb25zdCBpbnRlcnZhbFN0YXJ0czoge1trZXk6IHN0cmluZ106IFBlcmZMb2dFdmVudH0gPSB7fTtcbiAgICBjb25zdCBpbnRlcnZhbFN0YXJ0Q291bnQ6IHtba2V5OiBzdHJpbmddOiBudW1iZXJ9ID0ge307XG5cbiAgICBsZXQgaW5NZWFzdXJlUmFuZ2UgPSBmYWxzZTtcbiAgICBldmVudHMuZm9yRWFjaCgoZXZlbnQpID0+IHtcbiAgICAgIGNvbnN0IHBoID0gZXZlbnRbJ3BoJ107XG4gICAgICBsZXQgbmFtZSA9IGV2ZW50WyduYW1lJ10hO1xuICAgICAgbGV0IG1pY3JvSXRlcmF0aW9ucyA9IDE7XG4gICAgICBjb25zdCBtaWNyb0l0ZXJhdGlvbnNNYXRjaCA9IG5hbWUubWF0Y2goX01JQ1JPX0lURVJBVElPTlNfUkVHRVgpO1xuICAgICAgaWYgKG1pY3JvSXRlcmF0aW9uc01hdGNoKSB7XG4gICAgICAgIG5hbWUgPSBtaWNyb0l0ZXJhdGlvbnNNYXRjaFsxXTtcbiAgICAgICAgbWljcm9JdGVyYXRpb25zID0gcGFyc2VJbnQobWljcm9JdGVyYXRpb25zTWF0Y2hbMl0sIDEwKTtcbiAgICAgIH1cbiAgICAgIGlmIChldmVudCA9PT0gbWFya1N0YXJ0RXZlbnQpIHtcbiAgICAgICAgaW5NZWFzdXJlUmFuZ2UgPSB0cnVlO1xuICAgICAgfSBlbHNlIGlmIChldmVudCA9PT0gbWFya0VuZEV2ZW50KSB7XG4gICAgICAgIGluTWVhc3VyZVJhbmdlID0gZmFsc2U7XG4gICAgICB9XG4gICAgICBpZiAoIWluTWVhc3VyZVJhbmdlIHx8