node/lib/internal/perf/usertiming.js
Joyee Cheung a75d4e2724
perf_hooks: refactor perf_hooks for snapshot building
- Move Performance and InternalPerformance to a new
  lib/internal/perf/performance.js
- Move now() getMilestoneTimestamp() into
  lib/internal/perf/utils.js
- Rename lib/internal/perf/perf.js to
  lib/internal/perf/performance_entry.js
- Refresh time origin at startup (this means the
  time origins could differ between snapshot building
  time and snapshot creation time)

PR-URL: https://github.com/nodejs/node/pull/38971
Refs: https://github.com/nodejs/node/issues/35711
Reviewed-By: James M Snell <jasnell@gmail.com>
2021-06-28 16:20:12 +08:00

158 lines
3.8 KiB
JavaScript

'use strict';
const {
ObjectKeys,
SafeMap,
SafeSet,
SafeArrayIterator,
} = primordials;
const { InternalPerformanceEntry } = require('internal/perf/performance_entry');
const { now } = require('internal/perf/utils');
const { enqueue } = require('internal/perf/observe');
const nodeTiming = require('internal/perf/nodetiming');
const {
validateNumber,
validateObject,
validateString,
} = require('internal/validators');
const {
codes: {
ERR_INVALID_ARG_VALUE,
ERR_INVALID_PERFORMANCE_MARK,
ERR_PERFORMANCE_INVALID_TIMESTAMP,
ERR_PERFORMANCE_MEASURE_INVALID_OPTIONS,
},
} = require('internal/errors');
const marks = new SafeMap();
const nodeTimingReadOnlyAttributes = new SafeSet(new SafeArrayIterator([
'nodeStart',
'v8Start',
'environment',
'loopStart',
'loopExit',
'bootstrapComplete',
]));
function getMark(name) {
if (name === undefined) return;
if (typeof name === 'number') {
if (name < 0)
throw new ERR_PERFORMANCE_INVALID_TIMESTAMP(name);
return name;
}
name = `${name}`;
if (nodeTimingReadOnlyAttributes.has(name))
return nodeTiming[name];
const ts = marks.get(name);
if (ts === undefined)
throw new ERR_INVALID_PERFORMANCE_MARK(name);
return ts;
}
class PerformanceMark extends InternalPerformanceEntry {
constructor(name, options = {}) {
name = `${name}`;
if (nodeTimingReadOnlyAttributes.has(name))
throw new ERR_INVALID_ARG_VALUE('name', name);
validateObject(options, 'options');
const {
detail,
startTime = now(),
} = options;
validateNumber(startTime, 'startTime');
if (startTime < 0)
throw new ERR_PERFORMANCE_INVALID_TIMESTAMP(startTime);
marks.set(name, startTime);
super(name, 'mark', startTime, 0, detail);
enqueue(this);
}
}
class PerformanceMeasure extends InternalPerformanceEntry {
constructor(name, start, duration, detail) {
super(name, 'measure', start, duration, detail);
enqueue(this);
}
}
function mark(name, options = {}) {
return new PerformanceMark(name, options);
}
function calculateStartDuration(startOrMeasureOptions, endMark) {
startOrMeasureOptions ??= 0;
let detail;
let start;
let end;
let duration;
if (typeof startOrMeasureOptions === 'object' &&
ObjectKeys(startOrMeasureOptions).length) {
({
start,
end,
duration,
detail,
} = startOrMeasureOptions);
if (endMark !== undefined) {
throw new ERR_PERFORMANCE_MEASURE_INVALID_OPTIONS(
'endMark must not be specified');
}
if (start === undefined && end === undefined) {
throw new ERR_PERFORMANCE_MEASURE_INVALID_OPTIONS(
'One of options.start or options.end is required');
}
if (start !== undefined && end !== undefined && duration !== undefined) {
throw new ERR_PERFORMANCE_MEASURE_INVALID_OPTIONS(
'Must not have options.start, options.end, and ' +
'options.duration specified');
}
start = getMark(start);
duration = getMark(duration);
} else {
start = getMark(startOrMeasureOptions);
}
end = getMark(endMark || end) ??
((start !== undefined && duration !== undefined) ?
start + duration : now());
start ??= (duration !== undefined) ? end - duration : 0;
duration ??= end - start;
return { start, duration, detail };
}
function measure(name, startOrMeasureOptions, endMark) {
validateString(name, 'name');
const {
start,
duration,
detail
} = calculateStartDuration(startOrMeasureOptions, endMark);
return new PerformanceMeasure(name, start, duration, detail);
}
function clearMarks(name) {
if (name !== undefined) {
name = `${name}`;
if (nodeTimingReadOnlyAttributes.has(name))
throw new ERR_INVALID_ARG_VALUE('name', name);
marks.delete(name);
return;
}
marks.clear();
}
module.exports = {
PerformanceMark,
clearMarks,
mark,
measure,
};