UNPKG

56.6 kBJavaScriptView Raw
1import { PromiseWrapper, TimerWrapper } from 'angular2/src/facade/async';
2import { isPresent, isBlank, StringWrapper, RegExpWrapper, NumberWrapper } from 'angular2/src/facade/lang';
3import { BaseException } from 'angular2/src/facade/exceptions';
4import { ListWrapper, StringMapWrapper } from 'angular2/src/facade/collection';
5import { bind, provide, OpaqueToken } from 'angular2/src/core/di';
6import { WebDriverExtension } from '../web_driver_extension';
7import { Metric } from '../metric';
8import { Options } from '../common_options';
9/**
10 * A metric that reads out the performance log
11 */
12export class PerflogMetric extends Metric {
13 /**
14 * @param driverExtension
15 * @param setTimeout
16 * @param microMetrics Name and description of metrics provided via console.time / console.timeEnd
17 **/
18 constructor(_driverExtension, _setTimeout, _microMetrics, _forceGc, _captureFrames, _receivedData, _requestCount) {
19 super();
20 this._driverExtension = _driverExtension;
21 this._setTimeout = _setTimeout;
22 this._microMetrics = _microMetrics;
23 this._forceGc = _forceGc;
24 this._captureFrames = _captureFrames;
25 this._receivedData = _receivedData;
26 this._requestCount = _requestCount;
27 this._remainingEvents = [];
28 this._measureCount = 0;
29 this._perfLogFeatures = _driverExtension.perfLogFeatures();
30 if (!this._perfLogFeatures.userTiming) {
31 // User timing is needed for navigationStart.
32 this._receivedData = false;
33 this._requestCount = false;
34 }
35 }
36 // TODO(tbosch): use static values when our transpiler supports them
37 static get BINDINGS() { return _PROVIDERS; }
38 // TODO(tbosch): use static values when our transpiler supports them
39 static get SET_TIMEOUT() { return _SET_TIMEOUT; }
40 describe() {
41 var res = {
42 'scriptTime': 'script execution time in ms, including gc and render',
43 'pureScriptTime': 'script execution time in ms, without gc nor render'
44 };
45 if (this._perfLogFeatures.render) {
46 res['renderTime'] = 'render time in ms';
47 }
48 if (this._perfLogFeatures.gc) {
49 res['gcTime'] = 'gc time in ms';
50 res['gcAmount'] = 'gc amount in kbytes';
51 res['majorGcTime'] = 'time of major gcs in ms';
52 if (this._forceGc) {
53 res['forcedGcTime'] = 'forced gc time in ms';
54 res['forcedGcAmount'] = 'forced gc amount in kbytes';
55 }
56 }
57 if (this._receivedData) {
58 res['receivedData'] = 'encoded bytes received since navigationStart';
59 }
60 if (this._requestCount) {
61 res['requestCount'] = 'count of requests sent since navigationStart';
62 }
63 if (this._captureFrames) {
64 if (!this._perfLogFeatures.frameCapture) {
65 var warningMsg = 'WARNING: Metric requested, but not supported by driver';
66 // using dot syntax for metric name to keep them grouped together in console reporter
67 res['frameTime.mean'] = warningMsg;
68 res['frameTime.worst'] = warningMsg;
69 res['frameTime.best'] = warningMsg;
70 res['frameTime.smooth'] = warningMsg;
71 }
72 else {
73 res['frameTime.mean'] = 'mean frame time in ms (target: 16.6ms for 60fps)';
74 res['frameTime.worst'] = 'worst frame time in ms';
75 res['frameTime.best'] = 'best frame time in ms';
76 res['frameTime.smooth'] = 'percentage of frames that hit 60fps';
77 }
78 }
79 StringMapWrapper.forEach(this._microMetrics, (desc, name) => { StringMapWrapper.set(res, name, desc); });
80 return res;
81 }
82 beginMeasure() {
83 var resultPromise = PromiseWrapper.resolve(null);
84 if (this._forceGc) {
85 resultPromise = resultPromise.then((_) => this._driverExtension.gc());
86 }
87 return resultPromise.then((_) => this._beginMeasure());
88 }
89 endMeasure(restart) {
90 if (this._forceGc) {
91 return this._endPlainMeasureAndMeasureForceGc(restart);
92 }
93 else {
94 return this._endMeasure(restart);
95 }
96 }
97 _endPlainMeasureAndMeasureForceGc(restartMeasure) {
98 return this._endMeasure(true).then((measureValues) => {
99 // disable frame capture for measurements during forced gc
100 var originalFrameCaptureValue = this._captureFrames;
101 this._captureFrames = false;
102 return this._driverExtension.gc()
103 .then((_) => this._endMeasure(restartMeasure))
104 .then((forceGcMeasureValues) => {
105 this._captureFrames = originalFrameCaptureValue;
106 StringMapWrapper.set(measureValues, 'forcedGcTime', forceGcMeasureValues['gcTime']);
107 StringMapWrapper.set(measureValues, 'forcedGcAmount', forceGcMeasureValues['gcAmount']);
108 return measureValues;
109 });
110 });
111 }
112 _beginMeasure() {
113 return this._driverExtension.timeBegin(this._markName(this._measureCount++));
114 }
115 _endMeasure(restart) {
116 var markName = this._markName(this._measureCount - 1);
117 var nextMarkName = restart ? this._markName(this._measureCount++) : null;
118 return this._driverExtension.timeEnd(markName, nextMarkName)
119 .then((_) => this._readUntilEndMark(markName));
120 }
121 _readUntilEndMark(markName, loopCount = 0, startEvent = null) {
122 if (loopCount > _MAX_RETRY_COUNT) {
123 throw new BaseException(`Tried too often to get the ending mark: ${loopCount}`);
124 }
125 return this._driverExtension.readPerfLog().then((events) => {
126 this._addEvents(events);
127 var result = this._aggregateEvents(this._remainingEvents, markName);
128 if (isPresent(result)) {
129 this._remainingEvents = events;
130 return result;
131 }
132 var completer = PromiseWrapper.completer();
133 this._setTimeout(() => completer.resolve(this._readUntilEndMark(markName, loopCount + 1)), 100);
134 return completer.promise;
135 });
136 }
137 _addEvents(events) {
138 var needSort = false;
139 events.forEach(event => {
140 if (StringWrapper.equals(event['ph'], 'X')) {
141 needSort = true;
142 var startEvent = {};
143 var endEvent = {};
144 StringMapWrapper.forEach(event, (value, prop) => {
145 startEvent[prop] = value;
146 endEvent[prop] = value;
147 });
148 startEvent['ph'] = 'B';
149 endEvent['ph'] = 'E';
150 endEvent['ts'] = startEvent['ts'] + startEvent['dur'];
151 this._remainingEvents.push(startEvent);
152 this._remainingEvents.push(endEvent);
153 }
154 else {
155 this._remainingEvents.push(event);
156 }
157 });
158 if (needSort) {
159 // Need to sort because of the ph==='X' events
160 ListWrapper.sort(this._remainingEvents, (a, b) => {
161 var diff = a['ts'] - b['ts'];
162 return diff > 0 ? 1 : diff < 0 ? -1 : 0;
163 });
164 }
165 }
166 _aggregateEvents(events, markName) {
167 var result = { 'scriptTime': 0, 'pureScriptTime': 0 };
168 if (this._perfLogFeatures.gc) {
169 result['gcTime'] = 0;
170 result['majorGcTime'] = 0;
171 result['gcAmount'] = 0;
172 }
173 if (this._perfLogFeatures.render) {
174 result['renderTime'] = 0;
175 }
176 if (this._captureFrames) {
177 result['frameTime.mean'] = 0;
178 result['frameTime.best'] = 0;
179 result['frameTime.worst'] = 0;
180 result['frameTime.smooth'] = 0;
181 }
182 StringMapWrapper.forEach(this._microMetrics, (desc, name) => { result[name] = 0; });
183 if (this._receivedData) {
184 result['receivedData'] = 0;
185 }
186 if (this._requestCount) {
187 result['requestCount'] = 0;
188 }
189 var markStartEvent = null;
190 var markEndEvent = null;
191 var gcTimeInScript = 0;
192 var renderTimeInScript = 0;
193 var frameTimestamps = [];
194 var frameTimes = [];
195 var frameCaptureStartEvent = null;
196 var frameCaptureEndEvent = null;
197 var intervalStarts = {};
198 var intervalStartCount = {};
199 events.forEach((event) => {
200 var ph = event['ph'];
201 var name = event['name'];
202 var microIterations = 1;
203 var microIterationsMatch = RegExpWrapper.firstMatch(_MICRO_ITERATIONS_REGEX, name);
204 if (isPresent(microIterationsMatch)) {
205 name = microIterationsMatch[1];
206 microIterations = NumberWrapper.parseInt(microIterationsMatch[2], 10);
207 }
208 if (StringWrapper.equals(ph, 'b') && StringWrapper.equals(name, markName)) {
209 markStartEvent = event;
210 }
211 else if (StringWrapper.equals(ph, 'e') && StringWrapper.equals(name, markName)) {
212 markEndEvent = event;
213 }
214 let isInstant = StringWrapper.equals(ph, 'I') || StringWrapper.equals(ph, 'i');
215 if (this._requestCount && StringWrapper.equals(name, 'sendRequest')) {
216 result['requestCount'] += 1;
217 }
218 else if (this._receivedData && StringWrapper.equals(name, 'receivedData') && isInstant) {
219 result['receivedData'] += event['args']['encodedDataLength'];
220 }
221 else if (StringWrapper.equals(name, 'navigationStart')) {
222 // We count data + requests since the last navigationStart
223 // (there might be chrome extensions loaded by selenium before our page, so there
224 // will likely be more than one navigationStart).
225 if (this._receivedData) {
226 result['receivedData'] = 0;
227 }
228 if (this._requestCount) {
229 result['requestCount'] = 0;
230 }
231 }
232 if (isPresent(markStartEvent) && isBlank(markEndEvent) &&
233 event['pid'] === markStartEvent['pid']) {
234 if (StringWrapper.equals(ph, 'b') && StringWrapper.equals(name, _MARK_NAME_FRAME_CAPUTRE)) {
235 if (isPresent(frameCaptureStartEvent)) {
236 throw new BaseException('can capture frames only once per benchmark run');
237 }
238 if (!this._captureFrames) {
239 throw new BaseException('found start event for frame capture, but frame capture was not requested in benchpress');
240 }
241 frameCaptureStartEvent = event;
242 }
243 else if (StringWrapper.equals(ph, 'e') &&
244 StringWrapper.equals(name, _MARK_NAME_FRAME_CAPUTRE)) {
245 if (isBlank(frameCaptureStartEvent)) {
246 throw new BaseException('missing start event for frame capture');
247 }
248 frameCaptureEndEvent = event;
249 }
250 if (isInstant) {
251 if (isPresent(frameCaptureStartEvent) && isBlank(frameCaptureEndEvent) &&
252 StringWrapper.equals(name, 'frame')) {
253 frameTimestamps.push(event['ts']);
254 if (frameTimestamps.length >= 2) {
255 frameTimes.push(frameTimestamps[frameTimestamps.length - 1] -
256 frameTimestamps[frameTimestamps.length - 2]);
257 }
258 }
259 }
260 if (StringWrapper.equals(ph, 'B') || StringWrapper.equals(ph, 'b')) {
261 if (isBlank(intervalStarts[name])) {
262 intervalStartCount[name] = 1;
263 intervalStarts[name] = event;
264 }
265 else {
266 intervalStartCount[name]++;
267 }
268 }
269 else if ((StringWrapper.equals(ph, 'E') || StringWrapper.equals(ph, 'e')) &&
270 isPresent(intervalStarts[name])) {
271 intervalStartCount[name]--;
272 if (intervalStartCount[name] === 0) {
273 var startEvent = intervalStarts[name];
274 var duration = (event['ts'] - startEvent['ts']);
275 intervalStarts[name] = null;
276 if (StringWrapper.equals(name, 'gc')) {
277 result['gcTime'] += duration;
278 var amount = (startEvent['args']['usedHeapSize'] - event['args']['usedHeapSize']) / 1000;
279 result['gcAmount'] += amount;
280 var majorGc = event['args']['majorGc'];
281 if (isPresent(majorGc) && majorGc) {
282 result['majorGcTime'] += duration;
283 }
284 if (isPresent(intervalStarts['script'])) {
285 gcTimeInScript += duration;
286 }
287 }
288 else if (StringWrapper.equals(name, 'render')) {
289 result['renderTime'] += duration;
290 if (isPresent(intervalStarts['script'])) {
291 renderTimeInScript += duration;
292 }
293 }
294 else if (StringWrapper.equals(name, 'script')) {
295 result['scriptTime'] += duration;
296 }
297 else if (isPresent(this._microMetrics[name])) {
298 result[name] += duration / microIterations;
299 }
300 }
301 }
302 }
303 });
304 if (!isPresent(markStartEvent) || !isPresent(markEndEvent)) {
305 // not all events have been received, no further processing for now
306 return null;
307 }
308 if (isPresent(markEndEvent) && isPresent(frameCaptureStartEvent) &&
309 isBlank(frameCaptureEndEvent)) {
310 throw new BaseException('missing end event for frame capture');
311 }
312 if (this._captureFrames && isBlank(frameCaptureStartEvent)) {
313 throw new BaseException('frame capture requested in benchpress, but no start event was found');
314 }
315 if (frameTimes.length > 0) {
316 this._addFrameMetrics(result, frameTimes);
317 }
318 result['pureScriptTime'] = result['scriptTime'] - gcTimeInScript - renderTimeInScript;
319 return result;
320 }
321 _addFrameMetrics(result, frameTimes) {
322 result['frameTime.mean'] = frameTimes.reduce((a, b) => a + b, 0) / frameTimes.length;
323 var firstFrame = frameTimes[0];
324 result['frameTime.worst'] = frameTimes.reduce((a, b) => a > b ? a : b, firstFrame);
325 result['frameTime.best'] = frameTimes.reduce((a, b) => a < b ? a : b, firstFrame);
326 result['frameTime.smooth'] =
327 frameTimes.filter(t => t < _FRAME_TIME_SMOOTH_THRESHOLD).length / frameTimes.length;
328 }
329 _markName(index) { return `${_MARK_NAME_PREFIX}${index}`; }
330}
331var _MICRO_ITERATIONS_REGEX = /(.+)\*(\d+)$/g;
332var _MAX_RETRY_COUNT = 20;
333var _MARK_NAME_PREFIX = 'benchpress';
334var _SET_TIMEOUT = new OpaqueToken('PerflogMetric.setTimeout');
335var _MARK_NAME_FRAME_CAPUTRE = 'frameCapture';
336// using 17ms as a somewhat looser threshold, instead of 16.6666ms
337var _FRAME_TIME_SMOOTH_THRESHOLD = 17;
338var _PROVIDERS = [
339 bind(PerflogMetric)
340 .toFactory((driverExtension, setTimeout, microMetrics, forceGc, captureFrames, receivedData, requestCount) => new PerflogMetric(driverExtension, setTimeout, microMetrics, forceGc, captureFrames, receivedData, requestCount), [
341 WebDriverExtension,
342 _SET_TIMEOUT,
343 Options.MICRO_METRICS,
344 Options.FORCE_GC,
345 Options.CAPTURE_FRAMES,
346 Options.RECEIVED_DATA,
347 Options.REQUEST_COUNT
348 ]),
349 provide(_SET_TIMEOUT, { useValue: (fn, millis) => TimerWrapper.setTimeout(fn, millis) })
350];
351//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicGVyZmxvZ19tZXRyaWMuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyJkaWZmaW5nX3BsdWdpbl93cmFwcGVyLW91dHB1dF9wYXRoLXhCTElCclZSLnRtcC9iZW5jaHByZXNzL3NyYy9tZXRyaWMvcGVyZmxvZ19tZXRyaWMudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ik9BQU8sRUFBQyxjQUFjLEVBQUUsWUFBWSxFQUFDLE1BQU0sMkJBQTJCO09BQy9ELEVBQ0wsU0FBUyxFQUNULE9BQU8sRUFDUCxhQUFhLEVBRWIsYUFBYSxFQUNiLGFBQWEsRUFDZCxNQUFNLDBCQUEwQjtPQUMxQixFQUFDLGFBQWEsRUFBbUIsTUFBTSxnQ0FBZ0M7T0FDdkUsRUFBQyxXQUFXLEVBQUUsZ0JBQWdCLEVBQUMsTUFBTSxnQ0FBZ0M7T0FDckUsRUFBQyxJQUFJLEVBQUUsT0FBTyxFQUFZLFdBQVcsRUFBQyxNQUFNLHNCQUFzQjtPQUVsRSxFQUFDLGtCQUFrQixFQUFrQixNQUFNLHlCQUF5QjtPQUNwRSxFQUFDLE1BQU0sRUFBQyxNQUFNLFdBQVc7T0FDekIsRUFBQyxPQUFPLEVBQUMsTUFBTSxtQkFBbUI7QUFFekM7O0dBRUc7QUFDSCxtQ0FBbUMsTUFBTTtJQVd2Qzs7OztRQUlJO0lBQ0osWUFBb0IsZ0JBQW9DLEVBQVUsV0FBcUIsRUFDbkUsYUFBbUMsRUFBVSxRQUFpQixFQUM5RCxjQUF1QixFQUFVLGFBQXNCLEVBQ3ZELGFBQXNCO1FBQ3hDLE9BQU8sQ0FBQztRQUpVLHFCQUFnQixHQUFoQixnQkFBZ0IsQ0FBb0I7UUFBVSxnQkFBVyxHQUFYLFdBQVcsQ0FBVTtRQUNuRSxrQkFBYSxHQUFiLGFBQWEsQ0FBc0I7UUFBVSxhQUFRLEdBQVIsUUFBUSxDQUFTO1FBQzlELG1CQUFjLEdBQWQsY0FBYyxDQUFTO1FBQVUsa0JBQWEsR0FBYixhQUFhLENBQVM7UUFDdkQsa0JBQWEsR0FBYixhQUFhLENBQVM7UUFHeEMsSUFBSSxDQUFDLGdCQUFnQixHQUFHLEVBQUUsQ0FB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QUMsYUFBYTtRQUNyQixPQUFPLENBQUMsYUFBYTtLQUN0QixDQUFDO0lBQ1YsT0FBTyxDQUFDLFlBQVksRUFBRSxFQUFDLFFBQVEsRUFBRSxDQUFDLEVBQUUsRUFBRSxNQUFNLEtBQUssWUFBWSxDQUFDLFVBQVUsQ0FBQyxFQUFFLEVBQUUsTUFBTSxDQUFDLEVBQUMsQ0FBQztDQUN2RixDQUFDIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHtQcm9taXNlV3JhcHBlciwgVGltZXJXcmFwcGVyfSBmcm9tICdhbmd1bGFyMi9zcmMvZmFjYWRlL2FzeW5jJztcbmltcG9ydCB7XG4gIGlzUHJlc2VudCxcbiAgaXNCbGFuayxcbiAgU3RyaW5nV3JhcHBlcixcbiAgTWF0aCxcbiAgUmVnRXhwV3JhcHBlcixcbiAgTnVtYmVyV3JhcHBlclxufSBmcm9tICdhbmd1bGFyMi9zcmMvZmFjYWRlL2xhbmcnO1xuaW1wb3J0IHtCYXNlRXhjZXB0aW9uLCBXcmFwcGVkRXhjZXB0aW9ufSBmcm9tICdhbmd1bGFyMi9zcmMvZmFjYWRlL2V4Y2VwdGlvbnMnO1xuaW1wb3J0IHtMaXN0V3JhcHBlciwgU3RyaW5nTWFwV3JhcHBlcn0gZnJvbSAnYW5ndWxhcjIvc3JjL2ZhY2FkZS9jb2xsZWN0aW9uJztcbmltcG9ydCB7YmluZCwgcHJvdmlkZSwgUHJvdmlkZXIsIE9wYXF1ZVRva2VufSBmcm9tICdhbmd1bGFyMi9zcmMvY29yZS9kaSc7XG5cbmltcG9ydCB7V2ViRHJpdmVyRXh0ZW5zaW9uLCBQZXJmTG9nRmVhdHVyZXN9IGZyb20gJy4uL3dlYl9kcml2ZXJfZXh0ZW5zaW9uJztcbmltcG9ydCB7TWV0cmljfSBmcm9tICcuLi9tZXRyaWMnO1xuaW1wb3J0IHtPcHRpb25zfSBmcm9tICcuLi9jb21tb25fb3B0aW9ucyc7XG5cbi8qKlxuICogQSBtZXRyaWMgdGhhdCByZWFkcyBvdXQgdGhlIHBlcmZvcm1hbmNlIGxvZ1xuICovXG5leHBvcnQgY2xhc3MgUGVyZmxvZ01ldHJpYyBleHRlbmRzIE1ldHJpYyB7XG4gIC8vIFRPRE8odGJvc2NoKTogdXNlIHN0YXRpYyB2YWx1ZXMgd2hlbiBvdXIgdHJhbnNwaWxlciBzdXBwb3J0cyB0aGVtXG4gIHN0YXRpYyBnZXQgQklORElOR1MoKTogUHJvdmlkZXJbXSB7IHJldHVybiBfUFJPVklERVJTOyB9XG4gIC8vIFRPRE8odGJvc2NoKTogdXNlIHN0YXRpYyB2YWx1ZXMgd2hlbiBvdXIgdHJhbnNwaWxlciBzdXBwb3J0cyB0aGVtXG4gIHN0YXRpYyBnZXQgU0VUX1RJTUVPVVQoKTogT3BhcXVlVG9rZW4geyByZXR1cm4gX1NFVF9USU1FT1VUOyB9XG5cbiAgcHJpdmF0ZSBfcmVtYWluaW5nRXZlbnRzOiBBcnJheTx7W2tleTogc3RyaW5nXTogYW55fT47XG4gIHByaXZhdGUgX21lYXN1cmVDb3VudDogbnVtYmVyO1xuICBfcGVyZkxvZ0ZlYXR1cmVzOiBQZXJmTG9nRmVhdHVyZXM7XG5cblxuICAvKipcbiAgICogQHBhcmFtIGRyaXZlckV4dGVuc2lvblxuICAgKiBAcGFyYW0gc2V0VGltZW91dFxuICAgKiBAcGFyYW0gbWljcm9NZXRyaWNzIE5hbWUgYW5kIGRlc2NyaXB0aW9uIG9mIG1ldHJpY3MgcHJvdmlkZWQgdmlhIGNvbnNvbGUudGltZSAvIGNvbnNvbGUudGltZUVuZFxuICAgKiovXG4gIGNvbnN0cnVjdG9yKHByaXZhdGUgX2RyaXZlckV4dGVuc2lvbjogV2ViRHJpdmVyRXh0ZW5zaW9uLCBwcml2YXRlIF9zZXRUaW1lb3V0OiBGdW5jdGlvbixcbiAgICAgICAgICAgICAgcHJpdmF0ZSBfbWljcm9NZXRyaWNzOiB7W2tleTogc3RyaW5nXTogYW55fSwgcHJpdmF0ZSBfZm9yY2VHYzogYm9vbGVhbixcbiAgICAgICAgICAgICAgcHJpdmF0ZSBfY2FwdHVyZUZyYW1lczogYm9vbGVhbiwgcHJpdmF0ZSBfcmVjZWl2ZWREYXRhOiBib29sZWFuLFxuICAgICAgICAgICAgICBwcml2YXRlIF9yZXF1ZXN0Q291bnQ6IGJvb2xlYW4pIHtcbiAgICBzdXBlcigpO1xuXG4gICAgdGhpcy5fcmVtYWluaW5nRXZlbnRzID0gW107XG4gICAgdGhpcy5fbWVhc3VyZUNvdW50ID0gMDtcbiAgICB0aGlzLl9wZXJmTG9nRmVhdHVyZXMgPSBfZHJpdmVyRXh0ZW5zaW9uLnBlcmZMb2dGZWF0dXJlcygpO1xuICAgIGlmICghdGhpcy5fcGVyZkxvZ0ZlYXR1cmVzLnVzZXJUaW1pbmcpIHtcbiAgICAgIC8vIFVzZXIgdGltaW5nIGlzIG5lZWRlZCBmb3IgbmF2aWdhdGlvblN0YXJ0LlxuICAgICAgdGhpcy5fcmVjZWl2ZWREYXRhID0gZmFsc2U7XG4gICAgICB0aGlzLl9yZXF1ZXN0Q291bnQgPSBmYWxzZTtcbiAgICB9XG4gIH1cblxuICBkZXNjcmliZSgpOiB7W2tleTogc3RyaW5nXTogYW55fSB7XG4gICAgdmFyIHJlcyA9IHtcbiAgICAgICdzY3JpcHRUaW1lJzogJ3NjcmlwdCBleGVjdXRpb24gdGltZSBpbiBtcywgaW5jbHVkaW5nIGdjIGFuZCByZW5kZXInLFxuICAgICAgJ3B1cmVTY3JpcHRUaW1lJzogJ3NjcmlwdCBleGVjdXRpb24gdGltZSBpbiBtcywgd2l0aG91dCBnYyBub3IgcmVuZGVyJ1xuICAgIH07XG4gICAgaWYgKHRoaXMuX3BlcmZMb2dGZWF0dXJlcy5yZW5kZXIpIHtcbiAgICAgIHJlc1sncmVuZGVyVGltZSddID0gJ3JlbmRlciB0aW1lIGluIG1zJztcbiAgICB9XG4gICAgaWYgKHRoaXMuX3BlcmZMb2dGZWF0dXJlcy5nYykge1xuICAgICAgcmVzWydnY1RpbWUnXSA9ICdnYyB0aW1lIGluIG1zJztcbiAgICAgIHJlc1snZ2NBbW91bnQnXSA9ICdnYyBhbW91bnQgaW4ga2J5dGVzJztcbiAgICAgIHJlc1snbWFqb3JHY1RpbWUnXSA9ICd0aW1lIG9mIG1ham9yIGdjcyBpbiBtcyc7XG4gICAgICBpZiAodGhpcy5fZm9yY2VHYykge1xuICAgICAgICByZXNbJ2ZvcmNlZEdjVGltZSddID0gJ2ZvcmNlZCBnYyB0aW1lIGluIG1zJztcbiAgICAgICAgcmVzWydmb3JjZWRHY0Ftb3VudCddID0gJ2ZvcmNlZCBnYyBhbW91bnQgaW4ga2J5dGVzJztcbiAgICAgIH1cbiAgICB9XG4gICAgaWYgKHRoaXMuX3JlY2VpdmVkRGF0YSkge1xuICAgICAgcmVzWydyZWNlaXZlZERhdGEnXSA9ICdlbmNvZGVkIGJ5dGVzIHJlY2VpdmVkIHNpbmNlIG5hdmlnYXRpb25TdGFydCc7XG4gICAgfVxuICAgIGlmICh0aGlzLl9yZXF1ZXN0Q291bnQpIHtcbiAgICAgIHJlc1sncmVxdWVzdENvdW50J10gPSAnY291bnQgb2YgcmVxdWVzdHMgc2VudCBzaW5jZSBuYXZpZ2F0aW9uU3RhcnQnO1xuICAgIH1cbiAgICBpZiAodGhpcy5fY2FwdHVyZUZyYW1lcykge1xuICAgICAgaWYgKCF0aGlzLl9wZXJmTG9nRmVhdHVyZXMuZnJhbWVDYXB0dXJlKSB7XG4gICAgICAgIHZhciB3YXJuaW5nTXNnID0gJ1dBUk5JTkc6IE1ldHJpYyByZXF1ZXN0ZWQsIGJ1dCBub3Qgc3VwcG9ydGVkIGJ5IGRyaXZlcic7XG4gICAgICAgIC8vIHVzaW5nIGRvdCBzeW50YXggZm9yIG1ldHJpYyBuYW1lIHRvIGtlZXAgdGhlbSBncm91cGVkIHRvZ2V0aGVyIGluIGNvbnNvbGUgcmVwb3J0ZXJcbiAgICAgICAgcmVzWydmcmFtZVRpbWUubWVhbiddID0gd2FybmluZ01zZztcbiAgICAgICAgcmVzWydmcmFtZVRpbWUud29yc3QnXSA9IHdhcm5pbmdNc2c7XG4gICAgICAgIHJlc1snZnJhbWVUaW1lLmJlc3QnXSA9IHdhcm5pbmdNc2c7XG4gICAgICAgIHJlc1snZnJhbWVUaW1lLnNtb290aCddID0gd2FybmluZ01zZztcbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIHJlc1snZnJhbWVUaW1lLm1lYW4nXSA9ICdtZWFuIGZyYW1lIHRpbWUgaW4gbXMgKHRhcmdldDogMTYuNm1zIGZvciA2MGZwcyknO1xuICAgICAgICByZXNbJ2ZyYW1lVGltZS53b3JzdCddID0gJ3dvcnN0IGZyYW1lIHRpbWUgaW4gbXMnO1xuICAgICAgICByZXNbJ2ZyYW1lVGltZS5iZXN0J10gPSAnYmVzdCBmcmFtZSB0aW1lIGluIG1zJztcbiAgICAgICAgcmVzWydmcmFtZVRpbWUuc21vb3RoJ10gPSAncGVyY2VudGFnZSBvZiBmcmFtZXMgdGhhdCBoaXQgNjBmcHMnO1xuICAgICAgfVxuICAgIH1cbiAgICBTdHJpbmdNYXBXcmFwcGVyLmZvckVhY2godGhpcy5fbWljcm9NZXRyaWNzLFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAoZGVzYywgbmFtZSkgPT4geyBTdHJpbmdNYXBXcmFwcGVyLnNldChyZXMsIG5hbWUsIGRlc2MpOyB9KTtcbiAgICByZXR1cm4gcmVzO1xuICB9XG5cbiAgYmVnaW5NZWFzdXJlKCk6IFByb21pc2U8YW55PiB7XG4gICAgdmFyIHJlc3VsdFByb21pc2UgPSBQcm9taXNlV3JhcHBlci5yZXNvbHZlKG51bGwpO1xuICAgIGlmICh0aGlzLl9mb3JjZUdjKSB7XG4gICAgICByZXN1bHRQcm9taXNlID0gcmVzdWx0UHJvbWlzZS50aGVuKChfKSA9PiB0aGlzLl9kcml2ZXJFeHRlbnNpb24uZ2MoKSk7XG4gICAgfVxuICAgIHJldHVybiByZXN1bHRQcm9taXNlLnRoZW4oKF8pID0+IHRoaXMuX2JlZ2luTWVhc3VyZSgpKTtcbiAgfVxuXG4gIGVuZE1lYXN1cmUocmVzdGFydDogYm9vbGVhbik6IFByb21pc2U8e1trZXk6IHN0cmluZ106IGFueX0+IHtcbiAgICBpZiAodGhpcy5fZm9yY2VHYykge1xuICAgICAgcmV0dXJuIHRoaXMuX2VuZFBsYWluTWVhc3VyZUFuZE1lYXN1cmVGb3JjZUdjKHJlc3RhcnQpO1xuICAgIH0gZWxzZSB7XG4gICAgICByZXR1cm4gdGhpcy5fZW5kTWVhc3VyZShyZXN0YXJ0KTtcbiAgICB9XG4gIH1cblxuICBfZW5kUGxhaW5NZWFzdXJlQW5kTWVhc3VyZUZvcmNlR2MocmVzdGFydE1lYXN1cmU6IGJvb2xlYW4pIHtcbiAgICByZXR1cm4gdGhpcy5fZW5kTWVhc3VyZSh0cnVlKS50aGVuKChtZWFzdXJlVmFsdWVzKSA9PiB7XG4gICAgICAvLyBkaXNhYmxlIGZyYW1lIGNhcHR1cmUgZm9yIG1lYXN1cmVtZW50cyBkdXJpbmcgZm9yY2VkIGdjXG4gICAgICB2YXIgb3JpZ2luYWxGcmFtZUNhcHR1cmVWYWx1ZSA9IHRoaXMuX2NhcHR1cmVGcmFtZXM7XG4gICAgICB0aGlzLl9jYXB0dXJlRnJhbWVzID0gZmFsc2U7XG4gICAgICByZXR1cm4gdGhpcy5fZHJpdmVyRXh0ZW5zaW9uLmdjKClcbiAgICAgICAgICAudGhlbigoXykgPT4gdGhpcy5fZW5kTWVhc3VyZShyZXN0YXJ0TWVhc3VyZSkpXG4gICAgICAgICAgLnRoZW4oKGZvcmNlR2NNZWFzdXJlVmFsdWVzKSA9PiB7XG4gICAgICAgICAgICB0aGlzLl9jYXB0dXJlRnJhbWVzID0gb3JpZ2luYWxGcmFtZUNhcHR1cmVWYWx1ZTtcbiAgICAgICAgICAgIFN0cmluZ01hcFdyYXBwZXIuc2V0KG1lYXN1cmVWYWx1ZXMsICdmb3JjZWRHY1RpbWUnLCBmb3JjZUdjTWVhc3VyZVZhbHVlc1snZ2NUaW1lJ10pO1xuICAgICAgICAgICAgU3RyaW5nTWFwV3JhcHBlci5zZXQobWVhc3VyZVZhbHVlcywgJ2ZvcmNlZEdjQW1vdW50JywgZm9yY2VHY01lYXN1cmVWYWx1ZXNbJ2djQW1vdW50J10pO1xuICAgICAgICAgICAgcmV0dXJuIG1lYXN1cmVWYWx1ZXM7XG4gICAgICAgICAgfSk7XG4gICAgfSk7XG4gIH1cblxuICBfYmVnaW5NZWFzdXJlKCk6IFByb21pc2U8YW55PiB7XG4gICAgcmV0dXJuIHRoaXMuX2RyaXZlckV4dGVuc2lvbi50aW1lQmVnaW4odGhpcy5fbWFya05hbWUodGhpcy5fbWVhc3VyZUNvdW50KyspKTtcbiAgfVxuXG4gIF9lbmRNZWFzdXJlKHJlc3RhcnQ6IGJvb2xlYW4pOiBQcm9taXNlPHtba2V5OiBzdHJpbmddOiBhbnl9PiB7XG4gICAgdmFyIG1hcmtOYW1lID0gdGhpcy5fbWFya05hbWUodGhpcy5fbWVhc3VyZUNvdW50IC0gMSk7XG4gICAgdmFyIG5leHRNYXJrTmFtZSA9IHJlc3RhcnQgPyB0aGlzLl9tYXJrTmFtZSh0aGlzLl9tZWFzdXJlQ291bnQrKykgOiBudWxsO1xuICAgIHJldHVybiB0aGlzLl9kcml2ZXJFeHRlbnNpb24udGltZUVuZChtYXJrTmFtZSwgbmV4dE1hcmtOYW1lKVxuICAgICAgICAudGhlbigoXykgPT4gdGhpcy5fcmVhZFVudGlsRW5kTWFyayhtYXJrTmFtZSkpO1xuICB9XG5cbiAgX3JlYWRVbnRpbEVuZE1hcmsobWFya05hbWU6IHN0cmluZywgbG9vcENvdW50OiBudW1iZXIgPSAwLCBzdGFydEV2ZW50ID0gbnVsbCkge1xuICAgIGlmIChsb29wQ291bnQgPiBfTUFYX1JFVFJZX0NPVU5UKSB7XG4gICAgICB0aHJvdyBuZXcgQmFzZUV4Y2VwdGlvbihgVHJpZWQgdG9vIG9mdGVuIHRvIGdldCB0aGUgZW5kaW5nIG1hcms6ICR7bG9vcENvdW50fWApO1xuICAgIH1cbiAgICByZXR1cm4gdGhpcy5fZHJpdmVyRXh0ZW5zaW9uLnJlYWRQZXJmTG9nKCkudGhlbigoZXZlbnRzKSA9PiB7XG4gICAgICB0aGlzLl9hZGRFdmVudHMoZXZlbnRzKTtcbiAgICAgIHZhciByZXN1bHQgPSB0aGlzLl9hZ2dyZWdhdGVFdmVudHModGhpcy5fcmVtYWluaW5nRXZlbnRzLCBtYXJrTmFtZSk7XG4gICAgICBpZiAoaXNQcmVzZW50KHJlc3VsdCkpIHtcbiAgICAgICAgdGhpcy5fcmVtYWluaW5nRXZlbnRzID0gZXZlbnRzO1xuICAgICAgICByZXR1cm4gcmVzdWx0O1xuICAgICAgfVxuICAgICAgdmFyIGNvbXBsZXRlciA9IFByb21pc2VXcmFwcGVyLmNvbXBsZXRlcigpO1xuICAgICAgdGhpcy5fc2V0VGltZW91dCgoKSA9PiBjb21wbGV0ZXIucmVzb2x2ZSh0aGlzLl9yZWFkVW50aWxFbmRNYXJrKG1hcmtOYW1lLCBsb29wQ291bnQgKyAxKSksXG4gICAgICAgICAgICAgICAgICAgICAgIDEwMCk7XG4gICAgICByZXR1cm4gY29tcGxldGVyLnByb21pc2U7XG4gICAgfSk7XG4gIH1cblxuICBfYWRkRXZlbnRzKGV2ZW50czogeyBba2V5OiBzdHJpbmddOiBzdHJpbmcgfVtdKSB7XG4gICAgdmFyIG5lZWRTb3J0ID0gZmFsc2U7XG4gICAgZXZlbnRzLmZvckVhY2goZXZlbnQgPT4ge1xuICAgICAgaWYgKFN0cmluZ1dyYXBwZXIuZXF1YWxzKGV2ZW50WydwaCddLCAnWCcpKSB7XG4gICAgICAgIG5lZWRTb3J0ID0gdHJ1ZTtcbiAgICAgICAgdmFyIHN0YXJ0RXZlbnQgPSB7fTtcbiAgICAgICAgdmFyIGVuZEV2ZW50ID0ge307XG4gICAgICAgIFN0cmluZ01hcFdyYXBwZXIuZm9yRWFjaChldmVudCwgKHZhbHVlLCBwcm9wKSA9PiB7XG4gICAgICAgICAgc3RhcnRFdmVudFtwcm9wXSA9IHZhbHVlO1xuICAgICAgICAgIGVuZEV2ZW50W3Byb3BdID0gdmFsdWU7XG4gICAgICAgIH0pO1xuICAgICAgICBzdGFydEV2ZW50WydwaCddID0gJ0InO1xuICAgICAgICBlbmRFdmVudFsncGgnXSA9ICdFJztcbiAgICAgICAgZW5kRXZlbnRbJ3RzJ10gPSBzdGFydEV2ZW50Wyd0cyddICsgc3RhcnRFdmVudFsnZHVyJ107XG4gICAgICAgIHRoaXMuX3JlbWFpbmluZ0V2ZW50cy5wdXNoKHN0YXJ0RXZlbnQpO1xuICAgICAgICB0aGlzLl9yZW1haW5pbmdFdmVudHMucHVzaChlbmRFdmVudCk7XG4gICAgICB9IGVsc2Uge1xuICAgICAgICB0aGlzLl9yZW1haW5pbmdFdmVudHMucHVzaChldmVudCk7XG4gICAgICB9XG4gICAgfSk7XG4gICAgaWYgKG5lZWRTb3J0KSB7XG4gICAgICAvLyBOZWVkIHRvIHNvcnQgYmVjYXVzZSBvZiB0aGUgcGg9PT0nWCcgZXZlbnRzXG4gICAgICBMaXN0V3JhcHBlci5zb3J0KHRoaXMuX3JlbWFpbmluZ0V2ZW50cywgKGEsIGIpID0+IHtcbiAgICAgICAgdmFyIGRpZmYgPSBhWyd0cyddIC0gYlsndHMnXTtcbiAgICAgICAgcmV0dXJuIGRpZmYgPiAwID8gMSA6IGRpZmYgPCAwID8gLTEgOiAwO1xuICAgICAgfSk7XG4gICAgfVxuICB9XG5cbiAgX2FnZ3JlZ2F0ZUV2ZW50cyhldmVudHM6IEFycmF5PHtba2V5OiBzdHJpbmddOiBhbnl9PiwgbWFya05hbWUpOiB7W2tleTogc3RyaW5nXTogYW55fSB7XG4gICAgdmFyIHJlc3VsdCA9IHsnc2NyaXB0VGltZSc6IDAsICdwdXJlU2NyaXB0VGltZSc6IDB9O1xuICAgIGlmICh0aGlzLl9wZXJmTG9nRmVhdHVyZXMuZ2MpIHtcbiAgICAgIHJlc3VsdFsnZ2NUaW1lJ10gPSAwO1xuICAgICAgcmVzdWx0WydtYWpvckdjVGltZSddID0gMDtcbiAgICAgIHJlc3VsdFsnZ2NBbW91bnQnXSA9IDA7XG4gICAgfVxuICAgIGlmICh0aGlzLl9wZXJmTG9nRmVhdHVyZXMucmVuZGVyKSB7XG4gICAgICByZXN1bHRbJ3JlbmRlclRpbWUnXSA9IDA7XG4gICAgfVxuICAgIGlmICh0aGlzLl9jYXB0dXJlRnJhbWVzKSB7XG4gICAgICByZXN1bHRbJ2ZyYW1lVGltZS5tZWFuJ10gPSAwO1xuICAgICAgcmVzdWx0WydmcmFtZVRpbWUuYmVzdCddID0gMDtcbiAgICAgIHJlc3VsdFsnZnJhbWVUaW1lLndvcnN0J10gPSAwO1xuICAgICAgcmVzdWx0WydmcmFtZVRpbWUuc21vb3RoJ10gPSAwO1xuICAgIH1cbiAgICBTdHJpbmdNYXBXcmFwcGVyLmZvckVhY2godGhpcy5fbWljcm9NZXRyaWNzLCAoZGVzYywgbmFtZSkgPT4geyByZXN1bHRbbmFtZV0gPSAwOyB9KTtcbiAgICBpZiAodGhpcy5fcmVjZWl2ZWREYXRhKSB7XG4gICAgICByZXN1bHRbJ3JlY2VpdmVkRGF0YSddID0gMDtcbiAgICB9XG4gICAgaWYgKHRoaXMuX3JlcXVlc3RDb3VudCkge1xuICAgICAgcmVzdWx0WydyZXF1ZXN0Q291bnQnXSA9IDA7XG4gICAgfVxuXG4gICAgdmFyIG1hcmtTdGFydEV2ZW50ID0gbnVsbDtcbiAgICB2YXIgbWFya0VuZEV2ZW50ID0gbnVsbDtcbiAgICB2YXIgZ2NUaW1lSW5TY3JpcHQgPSAwO1xuICAgIHZhciByZW5kZXJUaW1lSW5TY3JpcHQgPSAwO1xuXG4gICAgdmFyIGZyYW1lVGltZXN0YW1wcyA9IFtdO1xuICAgIHZhciBmcmFtZVRpbWVzID0gW107XG4gICAgdmFyIGZyYW1lQ2FwdHVyZVN0YXJ0RXZlbnQgPSBudWxsO1xuICAgIHZhciBmcmFtZUNhcHR1cmVFbmRFdmVudCA9IG51bGw7XG5cbiAgICB2YXIgaW50ZXJ2YWxTdGFydHM6IHtba2V5OiBzdHJpbmddOiBhbnl9ID0ge307XG4gICAgdmFyIGludGVydmFsU3RhcnRDb3VudDoge1trZXk6IHN0cmluZ106IG51bWJlcn0gPSB7fTtcbiAgICBldmVudHMuZm9yRWFjaCgoZXZlbnQpID0+IHtcbiAgICAgIHZhciBwaCA9IGV2ZW50WydwaCddO1xuICAgICAgdmFyIG5hbWUgPSBldmVudFsnbmFtZSddO1xuICAgICAgdmFyIG1pY3JvSXRlcmF0aW9ucyA9IDE7XG4gICAgICB2YXIgbWljcm9JdGVyYXRpb25zTWF0Y2ggPSBSZWdFeHBXcmFwcGVyLmZpcnN0TWF0Y2goX01JQ1JPX0lURVJBVElPTlNfUkVHRVgsIG5hbWUpO1xuICAgICAgaWYgKGlzUHJlc2VudChtaWNyb0l0ZXJhdGlvbnNNYXRjaCkpIHtcbiAgICAgICAgbmFtZSA9IG1pY3JvSXRlcmF0aW9uc01hdGNoWzFdO1xuICAgICAgICBtaWNyb0l0ZXJhdGlvbnMgPSBOdW1iZXJXcmFwcGVyLnBhcnNlSW50KG1pY3JvSXRlcmF0aW9uc01hdGNoWzJdLCAxMCk7XG4gICAgICB9XG5cbiAgICAgIGlmIChTdHJpbmdXcmFwcGVyLmVxdWFscyhwaCwgJ2InKSAmJiBTdHJpbmdXcmFwcGVyLmVxdWFscyhuYW1lLCBtYXJrTmFtZSkpIHtcbiAgICAgICAgbWFya1N0YXJ0RXZlbnQgPSBldmVudDtcbiAgICAgIH0gZWxzZSBpZiAoU3RyaW5nV3JhcHBlci5lcXVhbHMocGgsICdlJykgJiYgU3RyaW5nV3JhcHBlci5lcXVhbHMobmFtZSwgbWFya05hbWUpKSB7XG4gICAgICAgIG1hcmtFbmRFdmVudCA9IGV2ZW50O1xuICAgICAgfVxuXG4gICAgICBsZXQgaXNJbnN0YW50ID0gU3RyaW5nV3JhcHBlci5lcXVhbHMocGgsICdJJykgfHwgU3RyaW5nV3JhcHBlci5lcXVhbHMocGgsICdpJyk7XG4gICAgICBpZiAodGhpcy5fcmVxdWVzdENvdW50ICYmIFN0cmluZ1dyYXBwZXIuZXF1YWxzKG5hbWUsICdzZW5kUmVxdWVzdCcpKSB7XG4gICAgICAgIHJlc3VsdFsncmVxdWVzdENvdW50J10gKz0gMTtcbiAgICAgIH0gZWxzZSBpZiAodGhpcy5fcmVjZWl2ZWREYXRhICYmIFN0cmluZ1dyYXBwZXIuZXF1YWxzKG5hbWUsICdyZWNlaXZlZERhdGEnKSAmJiBpc0luc3RhbnQpIHtcbiAgICAgICAgcmVzdWx0WydyZWNlaXZlZERhdGEnXSArPSBldmVudFsnYXJncyddWydlbmNvZGVkRGF0YUxlbmd0aCddO1xuICAgICAgfSBlbHNlIGlmIChTdHJpbmdXcmFwcGVyLmVxdWFscyhuYW1lLCAnbmF2aWdhdGlvblN0YXJ0JykpIHtcbiAgICAgICAgLy8gV2UgY291bnQgZGF0YSArIHJlcXVlc3RzIHNpbmNlIHRoZSBsYXN0IG5hdmlnYXRpb25TdGFydFxuICAgICAgICAvLyAodGhlcmUgbWlnaHQgYmUgY2hyb21lIGV4dGVuc2lvbnMgbG9hZGVkIGJ5IHNlbGVuaXVtIGJlZm9yZSBvdXIgcGFnZSwgc28gdGhlcmVcbiAgICAgICAgLy8gd2lsbCBsaWtlbHkgYmUgbW9yZSB0aGFuIG9uZSBuYXZpZ2F0aW9uU3RhcnQpLlxuICAgICAgICBpZiAodGhpcy5fcmVjZWl2ZWREYXRhKSB7XG4gICAgICAgICAgcmVzdWx0WydyZWNlaXZlZERhdGEnXSA9IDA7XG4gICAgICAgIH1cbiAgICAgICAgaWYgKHRoaXMuX3JlcXVlc3RDb3VudCkge1xuICAgICAgICAgIHJlc3VsdFsncmVxdWVzdENvdW50J10gPSAwO1xuICAgICAgICB9XG4gICAgICB9XG4gICAgICBpZiAoaXNQcmVzZW50KG1hcmtTdGFydEV2ZW50KSAmJiBpc0JsYW5rKG1hcmtFbmRFdmVudCkgJiZcbiAgICAgICAgICBldmVudFsncGlkJ10gPT09IG1hcmtTdGFydEV2ZW50WydwaWQnXSkge1xuICAgICAgICBpZiAoU3RyaW5nV3JhcHBlci5lcXVhbHMocGgsICdiJykgJiYgU3RyaW5nV3JhcHBlci5lcXVhbHMobmFtZSwgX01BUktfTkFNRV9GUkFNRV9DQVBVVFJFKSkge1xuICAgICAgICAgIGlmIChpc1ByZXNlbnQoZnJhbWVDYXB0dXJlU3RhcnRFdmVudCkpIHtcbiAgICAgICAgICAgIHRocm93IG5ldyBCYXNlRXhjZXB0aW9uKCdjYW4gY2FwdHVyZSBmcmFtZXMgb25seSBvbmNlIHBlciBiZW5jaG1hcmsgcnVuJyk7XG4gICAgICAgICAgfVxuICAgICAgICAgIGlmICghdGhpcy5fY2FwdHVyZUZyYW1lcykge1xuICAgICAgICAgICAgdGhyb3cgbmV3IEJhc2VFeGNlcHRpb24oXG4gICAgICAgICAgICAgICAgJ2ZvdW5kIHN0YXJ0IGV2ZW50IGZvciBmcmFtZSBjYXB0dXJlLCBidXQgZnJhbWUgY2FwdHVyZSB3YXMgbm90IHJlcXVlc3RlZCBpbiBiZW5jaHByZXNzJylcbiAgICAgICAgICB9XG4gICAgICAgICAgZnJhbWVDYXB0dXJlU3RhcnRFdmVudCA9IGV2ZW50O1xuICAgICAgICB9IGVsc2UgaWYgKFN0cmluZ1dyYXBwZXIuZXF1YWxzKHBoLCAnZScpICYmXG4gICAgICAgICAgICAgICAgICAgU3RyaW5nV3JhcHBlci5lcXVhbHMobmFtZSwgX01BUktfTkFNRV9GUkFNRV9DQVBVVFJFKSkge1xuICAgICAgICAgIGlmIChpc0JsYW5rKGZyYW1lQ2FwdHVyZVN0YXJ0RXZlbnQpKSB7XG4gICAgICAgICAgICB0aHJvdyBuZXcgQmFzZUV4Y2VwdGlvbignbWlzc2luZyBzdGFydCBldmVudCBmb3IgZnJhbWUgY2FwdHVyZScpO1xuICAgICAgICAgIH1cbiAgICAgICAgICBmcmFtZUNhcHR1cmVFbmRFdmVudCA9IGV2ZW50O1xuICAgICAgICB9XG5cbiAgICAgICAgaWYgKGlzSW5zdGFudCkge1xuICAgICAgICAgIGlmIChpc1ByZXNlbnQoZnJhbWVDYXB0dXJlU3RhcnRFdmVudCkgJiYgaXNCbGFuayhmcmFtZUNhcHR1cmVFbmRFdmVudCkgJiZcbiAgICAgICAgICAgICAgU3RyaW5nV3JhcHBlci5lcXVhbHMobmFtZSwgJ2ZyYW1lJykpIHtcbiAgICAgICAgICAgIGZyYW1lVGltZXN0YW1wcy5wdXNoKGV2ZW50Wyd0cyddKTtcbiAgICAgICAgICAgIGlmIChmcmFtZVRpbWVzdGFtcHMubGVuZ3RoID49IDIpIHtcbiAgICAgICAgICAgICAgZnJhbWVUaW1lcy5wdXNoKGZyYW1lVGltZXN0YW1wc1tmcmFtZVRpbWVzdGFtcHMubGVuZ3RoIC0gMV0gLVxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZnJhbWVUaW1lc3RhbXBzW2ZyYW1lVGltZXN0YW1wcy5sZW5ndGggLSAyXSk7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgfVxuICAgICAgICB9XG5cbiAgICAgICAgaWYgKFN0cmluZ1dyYXBwZXIuZXF1YWxzKHBoLCAnQicpIHx8IFN0cmluZ1dyYXBwZXIuZXF1YWxzKHBoLCAnYicpKSB7XG4gICAgICAgICAgaWYgKGlzQmxhbmsoaW50ZXJ2YWxTdGFydHNbbmFtZV0pKSB7XG4gICAgICAgICAgICBpbnRlcnZhbFN0YXJ0Q291bnRbbmFtZV0gPSAxO1xuICAgICAgICAgICAgaW50ZXJ2YWxTdGFydHNbbmFtZV0gPSBldmVudDtcbiAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgaW50ZXJ2YWxTdGFydENvdW50W25hbWVdKys7XG4gICAgICAgICAgfVxuICAgICAgICB9IGVsc2UgaWYgKChTdHJpbmdXcmFwcGVyLmVxdWFscyhwaCwgJ0UnKSB8fCBTdHJpbmdXcmFwcGVyLmVxdWFscyhwaCwgJ2UnKSkgJiZcbiAgICAgICAgICAgICAgICAgICBpc1ByZXNlbnQoaW50ZXJ2YWxTdGFydHNbbmFtZV0pKSB7XG4gICAgICAgICAgaW50ZXJ2YWxTdGFydENvdW50W25hbWVdLS07XG4gICAgICAgICAgaWYgKGludGVydmFsU3RhcnRDb3VudFtuYW1lXSA9PT0gMCkge1xuICAgICAgICAgICAgdmFyIHN0YXJ0RXZlbnQgPSBpbnRlcnZhbFN0YXJ0c1tuYW1lXTtcbiAgICAgICAgICAgIHZhciBkdXJhdGlvbiA9IChldmVudFsndHMnXSAtIHN0YXJ0RXZlbnRbJ3RzJ10pO1xuICAgICAgICAgICAgaW50ZXJ2YWxTdGFydHNbbmFtZV0gPSBudWxsO1xuICAgICAgICAgICAgaWYgKFN0cmluZ1dyYXBwZXIuZXF1YWxzKG5hbWUsICdnYycpKSB7XG4gICAgICAgICAgICAgIHJlc3VsdFsnZ2NUaW1lJ10gKz0gZHVyYXRpb247XG4gICAgICAgICAgICAgIHZhciBhbW91bnQgPVxuICAgICAgICAgICAgICAgICAgKHN0YXJ0RXZlbnRbJ2FyZ3MnXVsndXNlZEhlYXBTaXplJ10gLSBldmVudFsnYXJncyddWyd1c2VkSGVhcFNpemUnXSkgLyAxMDAwO1xuICAgICAgICAgICAgICByZXN1bHRbJ2djQW1vdW50J10gKz0gYW1vdW50O1xuICAgICAgICAgICAgICB2YXIgbWFqb3JHYyA9IGV2ZW50WydhcmdzJ11bJ21ham9yR2MnXTtcbiAgICAgICAgICAgICAgaWYgKGlzUHJlc2VudChtYWpvckdjKSAmJiBtYWpvckdjKSB7XG4gICAgICAgICAgICAgICAgcmVzdWx0WydtYWpvckdjVGltZSddICs9IGR1cmF0aW9uO1xuICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgIGlmIChpc1ByZXNlbnQoaW50ZXJ2YWxTdGFydHNbJ3NjcmlwdCddKSkge1xuICAgICAgICAgICAgICAgIGdjVGltZUluU2NyaXB0ICs9IGR1cmF0aW9uO1xuICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9IGVsc2UgaWYgKFN0cmluZ1dyYXBwZXIuZXF1YWxzKG5hbWUsICdyZW5kZXInKSkge1xuICAgICAgICAgICAgICByZXN1bHRbJ3JlbmRlclRpbWUnXSArPSBkdXJhdGlvbjtcbiAgICAgICAgICAgICAgaWYgKGlzUHJlc2VudChpbnRlcnZhbFN0YXJ0c1snc2NyaXB0J10pKSB7XG4gICAgICAgICAgICAgICAgcmVuZGVyVGltZUluU2NyaXB0ICs9IGR1cmF0aW9uO1xuICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9IGVsc2UgaWYgKFN0cmluZ1dyYXBwZXIuZXF1YWxzKG5hbWUsICdzY3JpcHQnKSkge1xuICAgICAgICAgICAgICByZXN1bHRbJ3NjcmlwdFRpbWUnXSArPSBkdXJhdGlvbjtcbiAgICAgICAgICAgIH0gZWxzZSBpZiAoaXNQcmVzZW50KHRoaXMuX21pY3JvTWV0cmljc1tuYW1lXSkpIHtcbiAgICAgICAgICAgICAgcmVzdWx0W25hbWVdICs9IGR1cmF0aW9uIC8gbWljcm9JdGVyYXRpb25zO1xuICAgICAgICAgICAgfVxuICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgICAgfVxuICAgIH0pO1xuICAgIGlmICghaXNQcmVzZW50KG1hcmtTdGFydEV2ZW50KSB8fCAhaXNQcmVzZW50KG1hcmtFbmRFdmVudCkpIHtcbiAgICAgIC8vIG5vdCBhbGwgZXZlbnRzIGhhdmUgYmVlbiByZWNlaXZlZCwgbm8gZnVydGhlciBwcm9jZXNzaW5nIGZvciBub3dcbiAgICAgIHJldHVybiBudWxsO1xuICAgIH1cblxuICAgIGlmIChpc1ByZXNlbnQobWFya0VuZEV2ZW50KSAmJiBpc1ByZXNlbnQoZnJhbWVDYXB0dXJlU3RhcnRFdmVudCkgJiZcbiAgICAgICAgaXNCbGFuayhmcmFtZUNhcHR1cmVFbmRFdmVudCkpIHtcbiAgICAgIHRocm93IG5ldyBCYXNlRXhjZXB0aW9uKCdtaXNzaW5nIGVuZCBldmVudCBmb3IgZnJhbWUgY2FwdHVyZScpO1xuICAgIH1cbiAgICBpZiAodGhpcy5fY2FwdHVyZUZyYW1lcyAmJiBpc0JsYW5rKGZyYW1lQ2FwdHVyZVN0YXJ0RXZlbnQpKSB7XG4gICAgICB0aHJvdyBuZXcgQmFzZUV4Y2VwdGlvbihcbiAgICAgICAgICAnZnJhbWUgY2FwdHVyZSByZXF1ZXN0ZWQgaW4gYmVuY2hwcmVzcywgYnV0IG5vIHN0YXJ0IGV2ZW50IHdhcyBmb3VuZCcpO1xuICAgIH1cbiAgICBpZiAoZnJhbWVUaW1lcy5sZW5ndGggPiAwKSB7XG4gICAgICB0aGlzLl9hZGRGcmFtZU1ldHJpY3MocmVzdWx0LCBmcmFtZVRpbWVzKTtcbiAgICB9XG4gICAgcmVzdWx0WydwdXJlU2NyaXB0VGltZSddID0gcmVzdWx0WydzY3JpcHRUaW1lJ10gLSBnY1RpbWVJblNjcmlwdCAtIHJlbmRlclRpbWVJblNjcmlwdDtcbiAgICByZXR1cm4gcmVzdWx0O1xuICB9XG5cbiAgX2FkZEZyYW1lTWV0cmljcyhyZXN1bHQ6IHtba2V5OiBzdHJpbmddOiBhbnl9LCBmcmFtZVRpbWVzOiBhbnlbXSkge1xuICAgIHJlc3VsdFsnZnJhbWVUaW1lLm1lYW4nXSA9IGZyYW1lVGltZXMucmVkdWNlKChhLCBiKSA9PiBhICsgYiwgMCkgLyBmcmFtZVRpbWVzLmxlbmd0aDtcbiAgICB2YXIgZmlyc3RGcmFtZSA9IGZyYW1lVGltZXNbMF07XG4gICAgcmVzdWx0WydmcmFtZVRpbWUud29yc3QnXSA9IGZyYW1lVGltZXMucmVkdWNlKChhLCBiKSA9PiBhID4gYiA/IGEgOiBiLCBmaXJzdEZyYW1lKTtcbiAgICByZXN1bHRbJ2ZyYW1lVGltZS5iZXN0J10gPSBmcmFtZVRpbWVzLnJlZHVjZSgoYSwgYikgPT4gYSA8IGIgPyBhIDogYiwgZmlyc3RGcmFtZSk7XG4gICAgcmVzdWx0WydmcmFtZVRpbWUuc21vb3RoJ10gPVxuICAgICAgICBmcmFtZVRpbWVzLmZpbHRlcih0ID0+IHQgPCBfRlJBTUVfVElNRV9TTU9PVEhfVEhSRVNIT0xEKS5sZW5ndGggLyBmcmFtZVRpbWVzLmxlbmd0aDtcbiAgfVxuXG4gIF9tYXJrTmFtZShpbmRleCkgeyByZXR1cm4gYCR7X01BUktfTkFNRV9QUkVGSVh9JHtpbmRleH1gOyB9XG59XG5cbnZhciBfTUlDUk9fSVRFUkFUSU9OU19SRUdFWCA9IC8oLispXFwqKFxcZCspJC9nO1xuXG52YXIgX01BWF9SRVRSWV9DT1VOVCA9IDIwO1xudmFyIF9NQVJLX05BTUVfUFJFRklYID0gJ2JlbmNocHJlc3MnO1xudmFyIF9TRVRfVElNRU9VVCA9IG5ldyBPcGFxdWVUb2tlbignUGVyZmxvZ01ldHJpYy5zZXRUaW1lb3V0Jyk7XG5cbnZhciBfTUFSS19OQU1FX0ZSQU1FX0NBUFVUUkUgPSAnZnJhbWVDYXB0dXJlJztcbi8vIHVzaW5nIDE3bXMgYXMgYSBzb21ld2hhdCBsb29zZXIgdGhyZXNob2xkLCBpbnN0ZWFkIG9mIDE2LjY2NjZtc1xudmFyIF9GUkFNRV9USU1FX1NNT09USF9USFJFU0hPTEQgPSAxNztcblxudmFyIF9QUk9WSURFUlMgPSBbXG4gIGJpbmQoUGVyZmxvZ01ldHJpYylcbiAgICAgIC50b0ZhY3RvcnkoXG4gICAgICAgICAgKGRyaXZlckV4dGVuc2lvbiwgc2V0VGltZW91dCwgbWljcm9NZXRyaWNzLCBmb3JjZUdjLCBjYXB0dXJlRnJhbWVzLCByZWNlaXZlZERhdGEsXG4gICAgICAgICAgIHJlcXVlc3RDb3VudCkgPT4gbmV3IFBlcmZsb2dNZXRyaWMoZHJpdmVyRXh0ZW5zaW9uLCBzZXRUaW1lb3V0LCBtaWNyb01ldHJpY3MsIGZvcmNlR2MsXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY2FwdHVyZUZyYW1lcywgcmVjZWl2ZWREYXRhLCByZXF1ZXN0Q291bnQpLFxuICAgICAgICAgIFtcbiAgICAgICAgICAgIFdlYkRyaXZlckV4dGVuc2lvbixcbiAgICAgICAgICAgIF9TRVRfVElNRU9VVCxcbiAgICAgICAgICAgIE9wdGlvbnMuTUlDUk9fTUVUUklDUyxcbiAgICAgICAgICAgIE9wdGlvbnMuRk9SQ0VfR0MsXG4gICAgICAgICAgICBPcHRpb25zLkNBUFRVUkVfRlJBTUVTLFxuICAgICAgICAgICAgT3B0aW9ucy5SRUNFSVZFRF9EQVRBLFxuICAgICAgICAgICAgT3B0aW9ucy5SRVFVRVNUX0NPVU5UXG4gICAgICAgICAgXSksXG4gIHByb3ZpZGUoX1NFVF9USU1FT1VULCB7dXNlVmFsdWU6IChmbiwgbWlsbGlzKSA9PiBUaW1lcldyYXBwZXIuc2V0VGltZW91dChmbiwgbWlsbGlzKX0pXG5dO1xuIl19
\No newline at end of file