1 | import { PromiseWrapper, TimerWrapper } from 'angular2/src/facade/async';
|
2 | import { isPresent, isBlank, StringWrapper, RegExpWrapper, NumberWrapper } from 'angular2/src/facade/lang';
|
3 | import { BaseException } from 'angular2/src/facade/exceptions';
|
4 | import { ListWrapper, StringMapWrapper } from 'angular2/src/facade/collection';
|
5 | import { bind, provide, OpaqueToken } from 'angular2/src/core/di';
|
6 | import { WebDriverExtension } from '../web_driver_extension';
|
7 | import { Metric } from '../metric';
|
8 | import { Options } from '../common_options';
|
9 | /**
|
10 | * A metric that reads out the performance log
|
11 | */
|
12 | export 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 | }
|
331 | var _MICRO_ITERATIONS_REGEX = /(.+)\*(\d+)$/g;
|
332 | var _MAX_RETRY_COUNT = 20;
|
333 | var _MARK_NAME_PREFIX = 'benchpress';
|
334 | var _SET_TIMEOUT = new OpaqueToken('PerflogMetric.setTimeout');
|
335 | var _MARK_NAME_FRAME_CAPUTRE = 'frameCapture';
|
336 | // using 17ms as a somewhat looser threshold, instead of 16.6666ms
|
337 | var _FRAME_TIME_SMOOTH_THRESHOLD = 17;
|
338 | var _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 |