/**
|
* @license
|
* Copyright 2018 Google LLC. All Rights Reserved.
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
* you may not use this file except in compliance with the License.
|
* You may obtain a copy of the License at
|
*
|
* http://www.apache.org/licenses/LICENSE-2.0
|
*
|
* Unless required by applicable law or agreed to in writing, software
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
* See the License for the specific language governing permissions and
|
* limitations under the License.
|
* =============================================================================
|
*/
|
import * as tf from './index';
|
import { ALL_ENVS, describeWithFlags, SYNC_BACKEND_ENVS } from './jasmine_util';
|
import { checkComputationForErrors, Logger, Profiler } from './profiler';
|
class TestBackendTimer {
|
constructor(delayMs, queryTimeMs, extraInfo) {
|
this.delayMs = delayMs;
|
this.queryTimeMs = queryTimeMs;
|
this.extraInfo = extraInfo;
|
this.counter = 1;
|
}
|
timerAvailable() {
|
return true;
|
}
|
async time(query) {
|
query();
|
const kernelMs = await new Promise(resolve => setTimeout(() => resolve(this.queryTimeMs * this.counter++), this.delayMs));
|
return { kernelMs, getExtraProfileInfo: () => this.extraInfo };
|
}
|
}
|
class TestLogger extends Logger {
|
logKernelProfile(name, result, vals, timeMs) { }
|
}
|
function promiseCheckWrapper(acturalValPromise, truthVal) {
|
return acturalValPromise.then(acturalVal => {
|
expect(acturalVal).toEqual(truthVal);
|
});
|
}
|
function checkKernelProfile(acturalVal, truthVal) {
|
expect(acturalVal.kernelName).toBe(truthVal.kernelName);
|
expect(acturalVal.inputs).toBe(truthVal.inputs);
|
acturalVal.outputs.forEach((output, index) => {
|
expect(output).toBe(truthVal.outputs[index]);
|
});
|
const promiseContainer = [
|
promiseCheckWrapper(acturalVal.timeMs, truthVal.timeMs),
|
promiseCheckWrapper(acturalVal.extraInfo, truthVal.extraInfo),
|
];
|
return Promise.all(promiseContainer);
|
}
|
describeWithFlags('profiler.Profiler', SYNC_BACKEND_ENVS, () => {
|
it('profiles simple function', doneFn => {
|
const delayMs = 5;
|
const queryTimeMs = 10;
|
const inputs = { 'x': tf.tensor1d([1]) };
|
const extraInfo = '';
|
const timer = new TestBackendTimer(delayMs, queryTimeMs, extraInfo);
|
const logger = new TestLogger();
|
const profiler = new Profiler(timer, logger);
|
spyOn(timer, 'time').and.callThrough();
|
spyOn(logger, 'logKernelProfile').and.callThrough();
|
const timeSpy = timer.time;
|
let kernelCalled = false;
|
const result = 1;
|
const resultScalar = tf.scalar(result);
|
const kernelProfile = profiler.profileKernel('MatMul', inputs, () => {
|
kernelCalled = true;
|
return [resultScalar];
|
});
|
setTimeout(() => {
|
expect(timeSpy.calls.count()).toBe(1);
|
expect(kernelCalled).toBe(true);
|
checkKernelProfile(kernelProfile, {
|
kernelName: 'MatMul',
|
outputs: [resultScalar],
|
timeMs: queryTimeMs,
|
inputs,
|
extraInfo,
|
}).then(() => doneFn());
|
}, delayMs * 2);
|
});
|
it('profiles nested kernel with optional inputs', doneFn => {
|
const delayMs = 5;
|
const queryTimeMs = 10;
|
const inputs = { 'x': tf.tensor1d([1]), 'bias': null };
|
const extraInfo = '';
|
const timer = new TestBackendTimer(delayMs, queryTimeMs, extraInfo);
|
const logger = new TestLogger();
|
const profiler = new Profiler(timer, logger);
|
spyOn(timer, 'time').and.callThrough();
|
spyOn(logger, 'logKernelProfile').and.callThrough();
|
const timeSpy = timer.time;
|
let matmulKernelCalled = false;
|
let maxKernelCalled = false;
|
const result = 1;
|
const resultScalar = tf.scalar(result);
|
let innerKernelProfile;
|
const outerKernelProfile = profiler.profileKernel('MatMul', inputs, () => {
|
innerKernelProfile = profiler.profileKernel('Max', inputs, () => {
|
maxKernelCalled = true;
|
return [resultScalar];
|
});
|
matmulKernelCalled = true;
|
return innerKernelProfile.outputs;
|
});
|
setTimeout(() => {
|
expect(timeSpy.calls.count()).toBe(2);
|
expect(matmulKernelCalled).toBe(true);
|
expect(maxKernelCalled).toBe(true);
|
const checkInnerKernelProfile = checkKernelProfile(innerKernelProfile, {
|
kernelName: 'Max',
|
outputs: [resultScalar],
|
timeMs: queryTimeMs,
|
inputs,
|
extraInfo
|
});
|
const checkOuterKernelProfile = checkKernelProfile(outerKernelProfile, {
|
kernelName: 'MatMul',
|
outputs: [resultScalar],
|
timeMs: queryTimeMs * 2,
|
inputs,
|
extraInfo
|
});
|
Promise.all([checkInnerKernelProfile, checkOuterKernelProfile])
|
.then(() => doneFn());
|
}, delayMs * 2);
|
});
|
it('log kernelProfile', doneFn => {
|
const delayMs = 5;
|
const queryTimeMs = 10;
|
const inputs = { 'x': tf.tensor1d([1]) };
|
const extraInfo = '';
|
const timer = new TestBackendTimer(delayMs, queryTimeMs, extraInfo);
|
const logger = new TestLogger();
|
const profiler = new Profiler(timer, logger);
|
spyOn(logger, 'logKernelProfile').and.callThrough();
|
const logKernelProfileSpy = logger.logKernelProfile;
|
const result = 1;
|
const resultScalar = tf.scalar(result);
|
const kernelProfiles = profiler.profileKernel('MatMul', inputs, () => {
|
return [resultScalar];
|
});
|
profiler.logKernelProfile(kernelProfiles);
|
setTimeout(() => {
|
expect(logKernelProfileSpy.calls.count()).toBe(1);
|
expect(logKernelProfileSpy.calls.first().args).toEqual([
|
'MatMul', resultScalar, new Float32Array([result]), queryTimeMs, inputs,
|
extraInfo
|
]);
|
doneFn();
|
}, delayMs * 2);
|
});
|
});
|
describe('profiler.checkComputationForErrors', () => {
|
beforeAll(() => {
|
// Silence warnings.
|
spyOn(console, 'warn');
|
});
|
it('Float32Array has NaN', () => {
|
expect(checkComputationForErrors(new Float32Array([1, 2, 3, NaN, 4, 255]), 'float32', 'test'))
|
.toBe(true);
|
});
|
it('Float32Array has Infinity', () => {
|
expect(checkComputationForErrors(new Float32Array([1, 2, 3, Infinity, 4, 255]), 'float32', 'test'))
|
.toBe(true);
|
});
|
it('Float32Array no NaN', () => {
|
// Int32 and Bool NaNs should not trigger an error.
|
expect(checkComputationForErrors(new Float32Array([1, 2, 3, -1, 4, 255]), 'float32', 'test'))
|
.toBe(false);
|
});
|
});
|
describeWithFlags('profiler.Logger', ALL_ENVS, () => {
|
it('skips logging for undefined input node in input tensor map', () => {
|
const kernelName = 'FusedConv2D';
|
const vals = new Float32Array(1);
|
const outputs = tf.tensor1d([1]);
|
const timeMs = 10;
|
const inputs = {
|
'x': tf.tensor1d([1]),
|
'filter': tf.tensor1d([1]),
|
'bias': tf.tensor1d([1]),
|
'preluActivationWeights': undefined
|
};
|
const extraInfo = '';
|
const logger = new Logger();
|
spyOn(console, 'log');
|
const consoleLogSpy = console.log;
|
logger.logKernelProfile(kernelName, outputs, vals, timeMs, inputs, extraInfo);
|
expect(consoleLogSpy.calls.first().args)
|
.not.toContain('preluActivationWeights');
|
});
|
});
|
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicHJvZmlsZXJfdGVzdC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uLy4uL3RmanMtY29yZS9zcmMvcHJvZmlsZXJfdGVzdC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQTs7Ozs7Ozs7Ozs7Ozs7O0dBZUc7QUFHSCxPQUFPLEtBQUssRUFBRSxNQUFNLFNBQVMsQ0FBQztBQUM5QixPQUFPLEVBQUMsUUFBUSxFQUFFLGlCQUFpQixFQUFFLGlCQUFpQixFQUFDLE1BQU0sZ0JBQWdCLENBQUM7QUFDOUUsT0FBTyxFQUFDLHlCQUF5QixFQUFpQixNQUFNLEVBQUUsUUFBUSxFQUFDLE1BQU0sWUFBWSxDQUFDO0FBS3RGLE1BQU0sZ0JBQWdCO0lBRXBCLFlBQ1ksT0FBZSxFQUFVLFdBQW1CLEVBQzVDLFNBQWlCO1FBRGpCLFlBQU8sR0FBUCxPQUFPLENBQVE7UUFBVSxnQkFBVyxHQUFYLFdBQVcsQ0FBUTtRQUM1QyxjQUFTLEdBQVQsU0FBUyxDQUFRO1FBSHJCLFlBQU8sR0FBRyxDQUFDLENBQUM7SUFHWSxDQUFDO0lBRWpDLGNBQWM7UUFDWixPQUFPLElBQUksQ0FBQztJQUNkLENBQUM7SUFFRCxLQUFLLENBQUMsSUFBSSxDQUFDLEtBQWlCO1FBQzFCLEtBQUssRUFBRSxDQUFDO1FBQ1IsTUFBTSxRQUFRLEdBQUcsTUFBTSxJQUFJLE9BQU8sQ0FDOUIsT0FBTyxDQUFDLEVBQUUsQ0FBQyxVQUFVLENBQ2pCLEdBQUcsRUFBRSxDQUFDLE9BQU8sQ0FBQyxJQUFJLENBQUMsV0FBVyxHQUFHLElBQUksQ0FBQyxPQUFPLEVBQUUsQ0FBQyxFQUFFLElBQUksQ0FBQyxPQUFPLENBQUMsQ0FBQyxDQUFDO1FBQ3pFLE9BQU8sRUFBQyxRQUFRLEVBQUUsbUJBQW1CLEVBQUUsR0FBRyxFQUFFLENBQUMsSUFBSSxDQUFDLFNBQVMsRUFBQyxDQUFDO0lBQy9ELENBQUM7Q0FDRjtBQUVELE1BQU0sVUFBVyxTQUFRLE1BQU07SUFDcEIsZ0JBQWdC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QyxDQUFDLENBQUMsQ0FBQyxDQUFDO1lBQzFCLE1BQU0sRUFBRSxFQUFFLENBQUMsUUFBUSxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUM7WUFDeEIsd0JBQXdCLEVBQUUsU0FBUztTQUNwQyxDQUFDO1FBQ0YsTUFBTSxTQUFTLEdBQUcsRUFBRSxDQUFDO1FBQ3JCLE1BQU0sTUFBTSxHQUFHLElBQUksTUFBTSxFQUFFLENBQUM7UUFDNUIsS0FBSyxDQUFDLE9BQU8sRUFBRSxLQUFLLENBQUMsQ0FBQztRQUN0QixNQUFNLGFBQWEsR0FBRyxPQUFPLENBQUMsR0FBa0IsQ0FBQztRQUVqRCxNQUFNLENBQUMsZ0JBQWdCLENBQ25CLFVBQVUsRUFBRSxPQUFPLEVBQUUsSUFBSSxFQUFFLE1BQU0sRUFBRSxNQUFNLEVBQUUsU0FBUyxDQUFDLENBQUM7UUFFMUQsTUFBTSxDQUFDLGFBQWEsQ0FBQyxLQUFLLENBQUMsS0FBSyxFQUFFLENBQUMsSUFBSSxDQUFDO2FBQ25DLEdBQUcsQ0FBQyxTQUFTLENBQUMsd0JBQXdCLENBQUMsQ0FBQztJQUMvQyxDQUFDLENBQUMsQ0FBQztBQUNMLENBQUMsQ0FBQyxDQUFDIiwic291cmNlc0NvbnRlbnQiOlsiLyoqXG4gKiBAbGljZW5zZVxuICogQ29weXJpZ2h0IDIwMTggR29vZ2xlIExMQy4gQWxsIFJpZ2h0cyBSZXNlcnZlZC5cbiAqIExpY2Vuc2VkIHVuZGVyIHRoZSBBcGFjaGUgTGljZW5zZSwgVmVyc2lvbiAyLjAgKHRoZSBcIkxpY2Vuc2VcIik7XG4gKiB5b3UgbWF5IG5vdCB1c2UgdGhpcyBmaWxlIGV4Y2VwdCBpbiBjb21wbGlhbmNlIHdpdGggdGhlIExpY2Vuc2UuXG4gKiBZb3UgbWF5IG9idGFpbiBhIGNvcHkgb2YgdGhlIExpY2Vuc2UgYXRcbiAqXG4gKiBodHRwOi8vd3d3LmFwYWNoZS5vcmcvbGljZW5zZXMvTElDRU5TRS0yLjBcbiAqXG4gKiBVbmxlc3MgcmVxdWlyZWQgYnkgYXBwbGljYWJsZSBsYXcgb3IgYWdyZWVkIHRvIGluIHdyaXRpbmcsIHNvZnR3YXJlXG4gKiBkaXN0cmlidXRlZCB1bmRlciB0aGUgTGljZW5zZSBpcyBkaXN0cmlidXRlZCBvbiBhbiBcIkFTIElTXCIgQkFTSVMsXG4gKiBXSVRIT1VUIFdBUlJBTlRJRVMgT1IgQ09ORElUSU9OUyBPRiBBTlkgS0lORCwgZWl0aGVyIGV4cHJlc3Mgb3IgaW1wbGllZC5cbiAqIFNlZSB0aGUgTGljZW5zZSBmb3IgdGhlIHNwZWNpZmljIGxhbmd1YWdlIGdvdmVybmluZyBwZXJtaXNzaW9ucyBhbmRcbiAqIGxpbWl0YXRpb25zIHVuZGVyIHRoZSBMaWNlbnNlLlxuICogPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT1cbiAqL1xuXG5pbXBvcnQge0JhY2tlbmRUaW1lciwgQmFja2VuZFRpbWluZ0luZm99IGZyb20gJy4vYmFja2VuZHMvYmFja2VuZCc7XG5pbXBvcnQgKiBhcyB0ZiBmcm9tICcuL2luZGV4JztcbmltcG9ydCB7QUxMX0VOVlMsIGRlc2NyaWJlV2l0aEZsYWdzLCBTWU5DX0JBQ0tFTkRfRU5WU30gZnJvbSAnLi9qYXNtaW5lX3V0aWwnO1xuaW1wb3J0IHtjaGVja0NvbXB1dGF0aW9uRm9yRXJyb3JzLCBLZXJuZWxQcm9maWxlLCBMb2dnZXIsIFByb2ZpbGVyfSBmcm9tICcuL3Byb2ZpbGVyJztcbmltcG9ydCB7VGVuc29yfSBmcm9tICcuL3RlbnNvcic7XG5pbXBvcnQge05hbWVkVGVuc29yTWFwfSBmcm9tICcuL3RlbnNvcl90eXBlcyc7XG5pbXBvcnQge1R5cGVkQXJyYXl9IGZyb20gJy4vdHlwZXMnO1xuXG5jbGFzcyBUZXN0QmFja2VuZFRpbWVyIGltcGxlbWVudHMgQmFja2VuZFRpbWVyIHtcbiAgcHJpdmF0ZSBjb3VudGVyID0gMTtcbiAgY29uc3RydWN0b3IoXG4gICAgICBwcml2YXRlIGRlbGF5TXM6IG51bWJlciwgcHJpdmF0ZSBxdWVyeVRpbWVNczogbnVtYmVyLFxuICAgICAgcHJpdmF0ZSBleHRyYUluZm86IHN0cmluZykge31cblxuICB0aW1lckF2YWlsYWJsZSgpIHtcbiAgICByZXR1cm4gdHJ1ZTtcbiAgfVxuXG4gIGFzeW5jIHRpbWUocXVlcnk6ICgpID0+IHZvaWQpOiBQcm9taXNlPEJhY2tlbmRUaW1pbmdJbmZvPiB7XG4gICAgcXVlcnkoKTtcbiAgICBjb25zdCBrZXJuZWxNcyA9IGF3YWl0IG5ldyBQcm9taXNlPG51bWJlcj4oXG4gICAgICAgIHJlc29sdmUgPT4gc2V0VGltZW91dChcbiAgICAgICAgICAgICgpID0+IHJlc29sdmUodGhpcy5xdWVyeVRpbWVNcyAqIHRoaXMuY291bnRlcisrKSwgdGhpcy5kZWxheU1zKSk7XG4gICAgcmV0dXJuIHtrZXJuZWxNcywgZ2V0RXh0cmFQcm9maWxlSW5mbzogKCkgPT4gdGhpcy5leHRyYUluZm99O1xuICB9XG59XG5cbmNsYXNzIFRlc3RMb2dnZXIgZXh0ZW5kcyBMb2dnZXIge1xuICBvdmVycmlkZSBsb2dLZXJuZWxQcm9maWxlKFxuICAgICAgbmFtZTogc3RyaW5nLCByZXN1bHQ6IFRlbnNvciwgdmFsczogVHlwZWRBcnJheSwgdGltZU1zOiBudW1iZXIpIHt9XG59XG5cbmZ1bmN0aW9uIHByb21pc2VDaGVja1dyYXBwZXIoYWN0dXJhbFZhbFByb21pc2U6IFByb21pc2U8e30+LCB0cnV0aFZhbDoge30pIHtcbiAgcmV0dXJuIGFjdHVyYWxWYWxQcm9taXNlLnRoZW4oYWN0dXJhbFZhbCA9PiB7XG4gICAgZXhwZWN0KGFjdHVyYWxWYWwpLnRvRXF1YWwodHJ1dGhWYWwpO1xuICB9KTtcbn1cblxuZnVuY3Rpb24gY2hlY2tLZXJuZWxQcm9maWxlKGFjdHVyYWxWYWw6IEtlcm5lbFByb2ZpbGUsIHRydXRoVmFsOiB7XG4gIGtlcm5lbE5hbWU6IHN0cmluZyxcbiAgb3V0cHV0czogVGVuc29yW10sXG4gIHRpbWVNczogbnVtYmVyfHtlcnJvcjogc3RyaW5nfSxcbiAgaW5wdXRzOiBOYW1lZFRlbnNvck1hcCxcbiAgZXh0cmFJbmZvOiBzdHJpbmdcbn0pIHtcbiAgZXhwZWN0KGFjdHVyYWxWYWwua2VybmVsTmFtZSkudG9CZSh0cnV0aFZhbC5rZXJuZWxOYW1lKTtcbiAgZXhwZWN0KGFjdHVyYWxWYWwuaW5wdXRzKS50b0JlKHRydXRoVmFsLmlucHV0cyk7XG4gIGFjdHVyYWxWYWwub3V0cHV0cy5mb3JFYWNoKChvdXRwdXQsIGluZGV4KSA9PiB7XG4gICAgZXhwZWN0KG91dHB1dCkudG9CZSh0cnV0aFZhbC5vdXRwdXRzW2luZGV4XSk7XG4gIH0pO1xuXG4gIGNvbnN0IHByb21pc2VDb250YWluZXIgPSBbXG4gICAgcHJvbWlzZUNoZWNrV3JhcHBlcihhY3R1cmFsVmFsLnRpbWVNcywgdHJ1dGhWYWwudGltZU1zKSxcbiAgICBwcm9taXNlQ2hlY2tXcmFwcGVyKGFjdHVyYWxWYWwuZXh0cmFJbmZvLCB0cnV0aFZhbC5leHRyYUluZm8pLFxuICBdO1xuICByZXR1cm4gUHJvbWlzZS5hbGwocHJvbWlzZUNvbnRhaW5lcik7XG59XG5cbmRlc2NyaWJlV2l0aEZsYWdzKCdwcm9maWxlci5Qcm9maWxlcicsIFNZTkNfQkFDS0VORF9FTlZTLCAoKSA9PiB7XG4gIGl0KCdwcm9maWxlcyBzaW1wbGUgZnVuY3Rpb24nLCBkb25lRm4gPT4ge1xuICAgIGNvbnN0IGRlbGF5TXMgPSA1O1xuICAgIGNvbnN0IHF1ZXJ5VGltZU1zID0gMTA7XG4gICAgY29uc3QgaW5wdXRzID0geyd4JzogdGYudGVuc29yMWQoWzFdKX07XG4gICAgY29uc3QgZXh0cmFJbmZvID0gJyc7XG4gICAgY29uc3QgdGltZXIgPSBuZXcgVGVzdEJhY2tlbmRUaW1lcihkZWxheU1zLCBxdWVyeVRpbWVNcywgZXh0cmFJbmZvKTtcbiAgICBjb25zdCBsb2dnZXIgPSBuZXcgVGVzdExvZ2dlcigpO1xuICAgIGNvbnN0IHByb2ZpbGVyID0gbmV3IFByb2ZpbGVyKHRpbWVyLCBsb2dnZXIpO1xuXG4gICAgc3B5T24odGltZXIsICd0aW1lJykuYW5kLmNhbGxUaHJvdWdoKCk7XG4gICAgc3B5T24obG9nZ2VyLCAnbG9nS2VybmVsUHJvZmlsZScpLmFuZC5jYWxsVGhyb3VnaCgpO1xuXG4gICAgY29uc3QgdGltZVNweSA9IHRpbWVyLnRpbWUgYXMgamFzbWluZS5TcHk7XG5cbiAgICBsZXQga2VybmVsQ2FsbGVkID0gZmFsc2U7XG4gICAgY29uc3QgcmVzdWx0ID0gMTtcbiAgICBjb25zdCByZXN1bHRTY2FsYXIgPSB0Zi5zY2FsYXIocmVzdWx0KTtcblxuICAgIGNvbnN0IGtlcm5lbFByb2ZpbGUgPSBwcm9maWxlci5wcm9maWxlS2VybmVsKCdNYXRNdWwnLCBpbnB1dHMsICgpID0+IHtcbiAgICAgIGtlcm5lbENhbGxlZCA9IHRydWU7XG4gICAgICByZXR1cm4gW3Jlc3VsdFNjYWxhcl07XG4gICAgfSk7XG4gICAgc2V0VGltZW91dCgoKSA9PiB7XG4gICAgICBleHBlY3QodGltZVNweS5jYWxscy5jb3VudCgpKS50b0JlKDEpO1xuICAgICAgZXhwZWN0KGtlcm5lbENhbGxlZCkudG9CZSh0cnVlKTtcblxuICAgICAgY2hlY2tLZXJuZWxQcm9maWxlKGtlcm5lbFByb2ZpbGUsIHtcbiAgICAgICAga2VybmVsTmFtZTogJ01hdE11bCcsXG4gICAgICAgIG91dHB1dHM6IFtyZXN1bHRTY2FsYXJdLFxuICAgICAgICB0aW1lTXM6IHF1ZXJ5VGltZU1zLFxuICAgICAgICBpbnB1dHMsXG4gICAgICAgIGV4dHJhSW5mbyxcbiAgICAgIH0pLnRoZW4oKCkgPT4gZG9uZUZuKCkpO1xuICAgIH0sIGRlbGF5TXMgKiAyKTtcbiAgfSk7XG5cbiAgaXQoJ3Byb2ZpbGVzIG5lc3RlZCBrZXJuZWwgd2l0aCBvcHRpb25hbCBpbnB1dHMnLCBkb25lRm4gPT4ge1xuICAgIGNvbnN0IGRlbGF5TXMgPSA1O1xuICAgIGNvbnN0IHF1ZXJ5VGltZU1zID0gMTA7XG4gICAgY29uc3QgaW5wdXRzOiB7J3gnOiB0Zi5UZW5zb3IsXG4gICAgICAgICAgICAgICAgICAgJ2JpYXMnOiBudWxsfSA9IHsneCc6IHRmLnRlbnNvcjFkKFsxXSksICdiaWFzJzogbnVsbH07XG4gICAgY29uc3QgZXh0cmFJbmZvID0gJyc7XG4gICAgY29uc3QgdGltZXIgPSBuZXcgVGVzdEJhY2tlbmRUaW1lcihkZWxheU1zLCBxdWVyeVRpbWVNcywgZXh0cmFJbmZvKTtcbiAgICBjb25zdCBsb2dnZXIgPSBuZXcgVGVzdExvZ2dlcigpO1xuICAgIGNvbnN0IHByb2ZpbGVyID0gbmV3IFByb2ZpbGVyKHRpbWVyLCBsb2dnZXIpO1xuXG4gICAgc3B5T24odGltZXIsICd0aW1lJykuYW5kLmNhbGxUaHJvdWdoKCk7XG4gICAgc3B5T24obG9nZ2VyLCAnbG9nS2VybmVsUHJvZmlsZScpLmFuZC5jYWxsVGhyb3VnaCgpO1xuICAgIGNvbnN0IHRpbWVTcHkgPSB0aW1lci50aW1lIGFzIGphc21pbmUuU3B5O1xuXG4gICAgbGV0IG1hdG11bEtlcm5lbENhbGxlZCA9IGZhbHNlO1xuICAgIGxldCBtYXhLZXJuZWxDYWxsZWQgPSBmYWxzZTtcbiAgICBjb25zdCByZXN1bHQgPSAxO1xuICAgIGNvbnN0IHJlc3VsdFNjYWxhciA9IHRmLnNjYWxhcihyZXN1bHQpO1xuXG4gICAgbGV0IGlubmVyS2VybmVsUHJvZmlsZTogS2VybmVsUHJvZmlsZTtcbiAgICBjb25zdCBvdXRlcktlcm5lbFByb2ZpbGUgPSBwcm9maWxlci5wcm9maWxlS2VybmVsKCdNYXRNdWwnLCBpbnB1dHMsICgpID0+IHtcbiAgICAgIGlubmVyS2VybmVsUHJvZmlsZSA9IHByb2ZpbGVyLnByb2ZpbGVLZXJuZWwoJ01heCcsIGlucHV0cywgKCkgPT4ge1xuICAgICAgICBtYXhLZXJuZWxDYWxsZWQgPSB0cnVlO1xuICAgICAgICByZXR1cm4gW3Jlc3VsdFNjYWxhcl07XG4gICAgICB9KTtcbiAgICAgIG1hdG11bEtlcm5lbENhbGxlZCA9IHRydWU7XG4gICAgICByZXR1cm4gaW5uZXJLZXJuZWxQcm9maWxlLm91dHB1dHM7XG4gICAgfSk7XG5cbiAgICBzZXRUaW1lb3V0KCgpID0+IHtcbiAgICAgIGV4cGVjdCh0aW1lU3B5LmNhbGxzLmNvdW50KCkpLnRvQmUoMik7XG4gICAgICBleHBlY3QobWF0bXVsS2VybmVsQ2FsbGVkKS50b0JlKHRydWUpO1xuICAgICAgZXhwZWN0KG1heEtlcm5lbENhbGxlZCkudG9CZSh0cnVlKTtcblxuICAgICAgY29uc3QgY2hlY2tJbm5lcktlcm5lbFByb2ZpbGUgPSBjaGVja0tlcm5lbFByb2ZpbGUoaW5uZXJLZXJuZWxQcm9maWxlLCB7XG4gICAgICAgIGtlcm5lbE5hbWU6ICdNYXgnLFxuICAgICAgICBvdXRwdXRzOiBbcmVzdWx0U2NhbGFyXSxcbiAgICAgICAgdGltZU1zOiBxdWVyeVRpbWVNcyxcbiAgICAgICAgaW5wdXRzLFxuICAgICAgICBleHRyYUluZm9cbiAgICAgIH0pO1xuICAgICAgY29uc3QgY2hlY2tPdXRlcktlcm5lbFByb2ZpbGUgPSBjaGVja0tlcm5lbFByb2ZpbGUob3V0ZXJLZXJuZWxQcm9maWxlLCB7XG4gICAgICAgIGtlcm5lbE5hbWU6ICdNYXRNdWwnLFxuICAgICAgICBvdXRwdXRzOiBbcmVzdWx0U2NhbGFyXSxcbiAgICAgICAgdGltZU1zOiBxdWVyeVRpbWVNcyAqIDIsXG4gICAgICAgIGlucHV0cyxcbiAgICAgICAgZXh0cmFJbmZvXG4gICAgICB9KTtcbiAgICAgIFByb21pc2UuYWxsKFtjaGVja0lubmVyS2VybmVsUHJvZmlsZSwgY2hlY2tPdXRlcktlcm5lbFByb2ZpbGVdKVxuICAgICAgICAgIC50aGVuKCgpID0+IGRvbmVGbigpKTtcbiAgICB9LCBkZWxheU1zICogMik7XG4gIH0pO1xuXG4gIGl0KCdsb2cga2VybmVsUHJvZmlsZScsIGRvbmVGbiA9PiB7XG4gICAgY29uc3QgZGVsYXlNcyA9IDU7XG4gICAgY29uc3QgcXVlcnlUaW1lTXMgPSAxMDtcbiAgICBjb25zdCBpbnB1dHMgPSB7J3gnOiB0Zi50ZW5zb3IxZChbMV0pfTtcbiAgICBjb25zdCBleHRyYUluZm8gPSAnJztcbiAgICBjb25zdCB0aW1lciA9IG5ldyBUZXN0QmFja2VuZFRpbWVyKGRlbGF5TXMsIHF1ZXJ5VGltZU1zLCBleHRyYUluZm8pO1xuICAgIGNvbnN0IGxvZ2dlciA9IG5ldyBUZXN0TG9nZ2VyKCk7XG4gICAgY29uc3QgcHJvZmlsZXIgPSBuZXcgUHJvZmlsZXIodGltZXIsIGxvZ2dlcik7XG5cbiAgICBzcHlPbihsb2dnZXIsICdsb2dLZXJuZWxQcm9maWxlJykuYW5kLmNhbGxUaHJvdWdoKCk7XG4gICAgY29uc3QgbG9nS2VybmVsUHJvZmlsZVNweSA9IGxvZ2dlci5sb2dLZXJuZWxQcm9maWxlIGFzIGphc21pbmUuU3B5O1xuXG4gICAgY29uc3QgcmVzdWx0ID0gMTtcbiAgICBjb25zdCByZXN1bHRTY2FsYXIgPSB0Zi5zY2FsYXIocmVzdWx0KTtcblxuICAgIGNvbnN0IGtlcm5lbFByb2ZpbGVzID0gcHJvZmlsZXIucHJvZmlsZUtlcm5lbCgnTWF0TXVsJywgaW5wdXRzLCAoKSA9PiB7XG4gICAgICByZXR1cm4gW3Jlc3VsdFNjYWxhcl07XG4gICAgfSk7XG4gICAgcHJvZmlsZXIubG9nS2VybmVsUHJvZmlsZShrZXJuZWxQcm9maWxlcyk7XG5cbiAgICBzZXRUaW1lb3V0KCgpID0+IHtcbiAgICAgIGV4cGVjdChsb2dLZXJuZWxQcm9maWxlU3B5LmNhbGxzLmNvdW50KCkpLnRvQmUoMSk7XG5cbiAgICAgIGV4cGVjdChsb2dLZXJuZWxQcm9maWxlU3B5LmNhbGxzLmZpcnN0KCkuYXJncykudG9FcXVhbChbXG4gICAgICAgICdNYXRNdWwnLCByZXN1bHRTY2FsYXIsIG5ldyBGbG9hdDMyQXJyYXkoW3Jlc3VsdF0pLCBxdWVyeVRpbWVNcywgaW5wdXRzLFxuICAgICAgICBleHRyYUluZm9cbiAgICAgIF0pO1xuICAgICAgZG9uZUZuKCk7XG4gICAgfSwgZGVsYXlNcyAqIDIpO1xuICB9KTtcbn0pO1xuXG5kZXNjcmliZSgncHJvZmlsZXIuY2hlY2tDb21wdXRhdGlvbkZvckVycm9ycycsICgpID0+IHtcbiAgYmVmb3JlQWxsKCgpID0+IHtcbiAgICAvLyBTaWxlbmNlIHdhcm5pbmdzLlxuICAgIHNweU9uKGNvbnNvbGUsICd3YXJuJyk7XG4gIH0pO1xuXG4gIGl0KCdGbG9hdDMyQXJyYXkgaGFzIE5hTicsICgpID0+IHtcbiAgICBleHBlY3QoY2hlY2tDb21wdXRhdGlvbkZvckVycm9ycyhcbiAgICAgICAgICAgICAgIG5ldyBGbG9hdDMyQXJyYXkoWzEsIDIsIDMsIE5hTiwgNCwgMjU1XSksICdmbG9hdDMyJywgJ3Rlc3QnKSlcbiAgICAgICAgLnRvQmUodHJ1ZSk7XG4gIH0pO1xuXG4gIGl0KCdGbG9hdDMyQXJyYXkgaGFzIEluZmluaXR5JywgKCkgPT4ge1xuICAgIGV4cGVjdChcbiAgICAgICAgY2hlY2tDb21wdXRhdGlvbkZvckVycm9ycyhcbiAgICAgICAgICAgIG5ldyBGbG9hdDMyQXJyYXkoWzEsIDIsIDMsIEluZmluaXR5LCA0LCAyNTVdKSwgJ2Zsb2F0MzInLCAndGVzdCcpKVxuICAgICAgICAudG9CZSh0cnVlKTtcbiAgfSk7XG5cbiAgaXQoJ0Zsb2F0MzJBcnJheSBubyBOYU4nLCAoKSA9PiB7XG4gICAgLy8gSW50MzIgYW5kIEJvb2wgTmFOcyBzaG91bGQgbm90IHRyaWdnZXIgYW4gZXJyb3IuXG4gICAgZXhwZWN0KGNoZWNrQ29tcHV0YXRpb25Gb3JFcnJvcnMoXG4gICAgICAgICAgICAgICBuZXcgRmxvYXQzMkFycmF5KFsxLCAyLCAzLCAtMSwgNCwgMjU1XSksICdmbG9hdDMyJywgJ3Rlc3QnKSlcbiAgICAgICAgLnRvQmUoZmFsc2UpO1xuICB9KTtcbn0pO1xuXG5kZXNjcmliZVdpdGhGbGFncygncHJvZmlsZXIuTG9nZ2VyJywgQUxMX0VOVlMsICgpID0+IHtcbiAgaXQoJ3NraXBzIGxvZ2dpbmcgZm9yIHVuZGVmaW5lZCBpbnB1dCBub2RlIGluIGlucHV0IHRlbnNvciBtYXAnLCAoKSA9PiB7XG4gICAgY29uc3Qga2VybmVsTmFtZSA9ICdGdXNlZENvbnYyRCc7XG4gICAgY29uc3QgdmFscyA9IG5ldyBGbG9hdDMyQXJyYXkoMSk7XG4gICAgY29uc3Qgb3V0cHV0cyA9IHRmLnRlbnNvcjFkKFsxXSk7XG4gICAgY29uc3QgdGltZU1zID0gMTA7XG4gICAgY29uc3QgaW5wdXRzOiBOYW1lZFRlbnNvck1hcCA9IHtcbiAgICAgICd4JzogdGYudGVuc29yMWQoWzFdKSxcbiAgICAgICdmaWx0ZXInOiB0Zi50ZW5zb3IxZChbMV0pLFxuICAgICAgJ2JpYXMnOiB0Zi50ZW5zb3IxZChbMV0pLFxuICAgICAgJ3ByZWx1QWN0aXZhdGlvbldlaWdodHMnOiB1bmRlZmluZWRcbiAgICB9O1xuICAgIGNvbnN0IGV4dHJhSW5mbyA9ICcnO1xuICAgIGNvbnN0IGxvZ2dlciA9IG5ldyBMb2dnZXIoKTtcbiAgICBzcHlPbihjb25zb2xlLCAnbG9nJyk7XG4gICAgY29uc3QgY29uc29sZUxvZ1NweSA9IGNvbnNvbGUubG9nIGFzIGphc21pbmUuU3B5O1xuXG4gICAgbG9nZ2VyLmxvZ0tlcm5lbFByb2ZpbGUoXG4gICAgICAgIGtlcm5lbE5hbWUsIG91dHB1dHMsIHZhbHMsIHRpbWVNcywgaW5wdXRzLCBleHRyYUluZm8pO1xuXG4gICAgZXhwZWN0KGNvbnNvbGVMb2dTcHkuY2FsbHMuZmlyc3QoKS5hcmdzKVxuICAgICAgICAubm90LnRvQ29udGFpbigncHJlbHVBY3RpdmF0aW9uV2VpZ2h0cycpO1xuICB9KTtcbn0pO1xuIl19
|