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>
This commit is contained in:
Joyee Cheung committed 2021-06-28 16:20:12 +08:00
1 parent 2de139b5d5
commit a75d4e2724
15 files changed
+224 -183

No files matched your search

+7
View File
@@ -27,6 +27,7 @@ function prepareMainThreadExecution(expandArgv1 = false) {
// Patch the process object with legacy properties and normalizations
patchProcessObject(expandArgv1);
setupTraceCategoryState();
setupPerfHooks();
setupInspectorHooks();
setupWarningHandler();
@@ -222,6 +223,11 @@ function setupTraceCategoryState() {
toggleTraceCategoryState(isTraceCategoryEnabled('node.async_hooks'));
}
function setupPerfHooks() {
require('internal/perf/performance').refreshTimeOrigin();
require('internal/perf/utils').refreshTimeOrigin();
}
function setupInspectorHooks() {
// If Debugger.setAsyncCallStackDepth is sent during bootstrap,
// we cannot immediately call into JS to enable the hooks, which could
@@ -474,6 +480,7 @@ module.exports = {
setupCoverageHooks,
setupWarningHandler,
setupDebugEnv,
setupPerfHooks,
prepareMainThreadExecution,
initializeDeprecations,
initializeESMLoader,
+1 -1
View File
@@ -9,7 +9,7 @@ const {
const { setUnrefTimeout } = require('internal/timers');
const { InternalPerformanceEntry } = require('internal/perf/perf');
const { InternalPerformanceEntry } = require('internal/perf/performance_entry');
const {
enqueue,
+2
View File
@@ -18,6 +18,7 @@ const {
setupInspectorHooks,
setupWarningHandler,
setupDebugEnv,
setupPerfHooks,
initializeDeprecations,
initializeWASI,
initializeCJSLoader,
@@ -114,6 +115,7 @@ port.on('message', (message) => {
} = message;
setupTraceCategoryState();
setupPerfHooks();
initializeReport();
if (manifestSrc) {
require('internal/process/policy').setup(manifestSrc, manifestURL);
+1 -1
View File
@@ -2,7 +2,7 @@
const nodeTiming = require('internal/perf/nodetiming');
const { now } = require('internal/perf/perf');
const { now } = require('internal/perf/utils');
function eventLoopUtilization(util1, util2) {
const ls = nodeTiming.loopStart;
+4 -27
View File
@@ -3,15 +3,14 @@
const {
ObjectDefineProperties,
ObjectSetPrototypeOf,
SafeArrayIterator,
SafeSet,
} = primordials;
const { PerformanceEntry } = require('internal/perf/performance_entry');
const {
PerformanceEntry,
kReadOnlyAttributes,
now,
} = require('internal/perf/perf');
getMilestoneTimestamp,
} = require('internal/perf/utils');
const {
customInspectSymbol: kInspect,
@@ -29,26 +28,8 @@ const {
NODE_PERFORMANCE_MILESTONE_ENVIRONMENT
},
loopIdleTime,
milestones,
timeOrigin,
} = internalBinding('performance');
function getMilestoneTimestamp(milestoneIdx) {
const ns = milestones[milestoneIdx];
if (ns === -1)
return ns;
return ns / 1e6 - timeOrigin;
}
const readOnlyAttributes = new SafeSet(new SafeArrayIterator([
'nodeStart',
'v8Start',
'environment',
'loopStart',
'loopExit',
'bootstrapComplete',
]));
class PerformanceNodeTiming {
constructor() {
ObjectDefineProperties(this, {
@@ -159,10 +140,6 @@ class PerformanceNodeTiming {
idleTime: this.idleTime,
};
}
static get [kReadOnlyAttributes]() {
return readOnlyAttributes;
}
}
ObjectSetPrototypeOf(
+7 -5
View File
@@ -31,7 +31,7 @@ const {
const {
InternalPerformanceEntry,
isPerformanceEntry,
} = require('internal/perf/perf');
} = require('internal/perf/performance_entry');
const {
codes: {
@@ -174,11 +174,13 @@ class PerformanceObserverEntryList {
}
class PerformanceObserver {
[kBuffer] = [];
[kEntryTypes] = new SafeSet();
[kType] = undefined;
constructor(callback) {
// TODO(joyeecheung): V8 snapshot does not support instance member
// initializers for now:
// https://bugs.chromium.org/p/v8/issues/detail?id=10704
this[kBuffer] = [];
this[kEntryTypes] = new SafeSet();
this[kType] = undefined;
validateCallback(callback);
this[kCallback] = callback;
}
+129
View File
@@ -0,0 +1,129 @@
'use strict';
const {
ObjectDefineProperty,
ObjectDefineProperties,
ObjectSetPrototypeOf,
TypeError,
} = primordials;
const {
EventTarget,
} = require('internal/event_target');
const { now } = require('internal/perf/utils');
const {
mark,
measure,
clearMarks,
} = require('internal/perf/usertiming');
const eventLoopUtilization = require('internal/perf/event_loop_utilization');
const nodeTiming = require('internal/perf/nodetiming');
const timerify = require('internal/perf/timerify');
const { customInspectSymbol: kInspect } = require('internal/util');
const { inspect } = require('util');
const {
getTimeOriginTimestamp
} = internalBinding('performance');
class Performance extends EventTarget {
constructor() {
// eslint-disable-next-line no-restricted-syntax
throw new TypeError('Illegal constructor');
}
[kInspect](depth, options) {
if (depth < 0) return this;
const opts = {
...options,
depth: options.depth == null ? null : options.depth - 1
};
return `Performance ${inspect({
nodeTiming: this.nodeTiming,
timeOrigin: this.timeOrigin,
}, opts)}`;
}
}
function toJSON() {
return {
nodeTiming: this.nodeTiming,
timeOrigin: this.timeOrigin,
eventLoopUtilization: this.eventLoopUtilization()
};
}
class InternalPerformance extends EventTarget {}
InternalPerformance.prototype.constructor = Performance.prototype.constructor;
ObjectSetPrototypeOf(InternalPerformance.prototype, Performance.prototype);
ObjectDefineProperties(Performance.prototype, {
clearMarks: {
configurable: true,
enumerable: false,
value: clearMarks,
},
eventLoopUtilization: {
configurable: true,
enumerable: false,
value: eventLoopUtilization,
},
mark: {
configurable: true,
enumerable: false,
value: mark,
},
measure: {
configurable: true,
enumerable: false,
value: measure,
},
nodeTiming: {
configurable: true,
enumerable: false,
value: nodeTiming,
},
now: {
configurable: true,
enumerable: false,
value: now,
},
timerify: {
configurable: true,
enumerable: false,
value: timerify,
},
// This would be updated during pre-execution in case
// the process is launched from a snapshot.
// TODO(joyeecheung): we may want to warn about access to
// this during snapshot building.
timeOrigin: {
configurable: true,
enumerable: true,
value: getTimeOriginTimestamp(),
},
toJSON: {
configurable: true,
enumerable: true,
value: toJSON,
}
});
function refreshTimeOrigin() {
ObjectDefineProperty(Performance.prototype, 'timeOrigin', {
configurable: true,
enumerable: true,
value: getTimeOriginTimestamp(),
});
}
module.exports = {
InternalPerformance,
refreshTimeOrigin
};
@@ -6,10 +6,6 @@ const {
TypeError,
} = primordials;
const {
timeOrigin,
} = internalBinding('performance');
const {
customInspectSymbol: kInspect,
} = require('internal/util');
@@ -21,12 +17,6 @@ const kType = Symbol('kType');
const kStart = Symbol('kStart');
const kDuration = Symbol('kDuration');
const kDetail = Symbol('kDetail');
const kReadOnlyAttributes = Symbol('kReadOnlyAttributes');
function now() {
const hr = process.hrtime();
return (hr[0] * 1000 + hr[1] / 1e6) - timeOrigin;
}
function isPerformanceEntry(obj) {
return obj?.[kName] !== undefined;
@@ -88,7 +78,5 @@ ObjectSetPrototypeOf(
module.exports = {
InternalPerformanceEntry,
PerformanceEntry,
kReadOnlyAttributes,
isPerformanceEntry,
now,
};
+2 -4
View File
@@ -9,10 +9,8 @@ const {
Symbol,
} = primordials;
const {
InternalPerformanceEntry,
now,
} = require('internal/perf/perf');
const { InternalPerformanceEntry } = require('internal/perf/performance_entry');
const { now } = require('internal/perf/utils');
const {
validateFunction,
+13 -9
View File
@@ -3,16 +3,13 @@
const {
ObjectKeys,
SafeMap,
SafeSet,
SafeArrayIterator,
} = primordials;
const {
InternalPerformanceEntry,
kReadOnlyAttributes,
now,
} = require('internal/perf/perf');
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 {
@@ -31,8 +28,15 @@ const {
} = require('internal/errors');
const marks = new SafeMap();
const nodeTimingReadOnlyAttributes =
nodeTiming.constructor[kReadOnlyAttributes];
const nodeTimingReadOnlyAttributes = new SafeSet(new SafeArrayIterator([
'nodeStart',
'v8Start',
'environment',
'loopStart',
'loopExit',
'bootstrapComplete',
]));
function getMark(name) {
if (name === undefined) return;
+33
View File
@@ -0,0 +1,33 @@
'use strict';
const binding = internalBinding('performance');
const {
milestones,
getTimeOrigin,
} = binding;
// TODO(joyeecheung): we may want to warn about access to
// this during snapshot building.
let timeOrigin = getTimeOrigin();
function now() {
const hr = process.hrtime();
return (hr[0] * 1000 + hr[1] / 1e6) - timeOrigin;
}
function getMilestoneTimestamp(milestoneIdx) {
const ns = milestones[milestoneIdx];
if (ns === -1)
return ns;
return ns / 1e6 - timeOrigin;
}
function refreshTimeOrigin() {
timeOrigin = getTimeOrigin();
}
module.exports = {
now,
getMilestoneTimestamp,
refreshTimeOrigin
};
+1 -7
View File
@@ -28,7 +28,7 @@ const {
const EventEmitter = require('events');
const assert = require('internal/assert');
const path = require('path');
const { timeOrigin } = internalBinding('performance');
const { now } = require('internal/perf/utils');
const errorCodes = require('internal/errors').codes;
const {
@@ -504,12 +504,6 @@ function eventLoopUtilization(util1, util2) {
return { idle: idle_delta, active: active_delta, utilization };
}
// Duplicate code from performance.now() so don't need to require perf_hooks.
function now() {
const hr = process.hrtime();
return (hr[0] * 1000 + hr[1] / 1e6) - timeOrigin;
}
module.exports = {
ownsProcessState,
isMainThread,
+3 -106
View File
@@ -1,126 +1,23 @@
'use strict';
const {
ObjectDefineProperties,
ObjectDefineProperty,
ObjectSetPrototypeOf,
TypeError,
} = primordials;
const {
timeOriginTimestamp,
constants,
} = internalBinding('performance');
const {
EventTarget,
} = require('internal/event_target');
const {
PerformanceEntry,
now,
} = require('internal/perf/perf');
const { PerformanceEntry } = require('internal/perf/performance_entry');
const { PerformanceObserver } = require('internal/perf/observe');
const {
PerformanceMark,
mark,
measure,
clearMarks,
} = require('internal/perf/usertiming');
const { PerformanceMark } = require('internal/perf/usertiming');
const { InternalPerformance } = require('internal/perf/performance');
const {
createHistogram
} = require('internal/histogram');
const eventLoopUtilization = require('internal/perf/event_loop_utilization');
const monitorEventLoopDelay = require('internal/perf/event_loop_delay');
const nodeTiming = require('internal/perf/nodetiming');
const timerify = require('internal/perf/timerify');
const { customInspectSymbol: kInspect } = require('internal/util');
const { inspect } = require('util');
class Performance extends EventTarget {
constructor() {
// eslint-disable-next-line no-restricted-syntax
throw new TypeError('Illegal constructor');
}
[kInspect](depth, options) {
if (depth < 0) return this;
const opts = {
...options,
depth: options.depth == null ? null : options.depth - 1
};
return `Performance ${inspect({
nodeTiming: this.nodeTiming,
timeOrigin: this.timeOrigin,
}, opts)}`;
}
}
function toJSON() {
return {
nodeTiming: this.nodeTiming,
timeOrigin: this.timeOrigin,
eventLoopUtilization: this.eventLoopUtilization()
};
}
class InternalPerformance extends EventTarget {}
InternalPerformance.prototype.constructor = Performance.prototype.constructor;
ObjectSetPrototypeOf(InternalPerformance.prototype, Performance.prototype);
ObjectDefineProperties(Performance.prototype, {
clearMarks: {
configurable: true,
enumerable: false,
value: clearMarks,
},
eventLoopUtilization: {
configurable: true,
enumerable: false,
value: eventLoopUtilization,
},
mark: {
configurable: true,
enumerable: false,
value: mark,
},
measure: {
configurable: true,
enumerable: false,
value: measure,
},
nodeTiming: {
configurable: true,
enumerable: false,
value: nodeTiming,
},
now: {
configurable: true,
enumerable: false,
value: now,
},
timerify: {
configurable: true,
enumerable: false,
value: timerify,
},
timeOrigin: {
configurable: true,
enumerable: true,
value: timeOriginTimestamp,
},
toJSON: {
configurable: true,
enumerable: true,
value: toJSON,
}
});
module.exports = {
PerformanceEntry,
+11 -11
View File
@@ -274,6 +274,15 @@ void ELDHistogram::OnInterval() {
"stddev", histogram()->Stddev());
}
void GetTimeOrigin(const FunctionCallbackInfo<Value>& args) {
args.GetReturnValue().Set(Number::New(args.GetIsolate(), timeOrigin / 1e6));
}
void GetTimeOriginTimeStamp(const FunctionCallbackInfo<Value>& args) {
args.GetReturnValue().Set(
Number::New(args.GetIsolate(), timeOriginTimestamp / MICROS_PER_MILLIS));
}
void Initialize(Local<Object> target,
Local<Value> unused,
Local<Context> context,
@@ -308,6 +317,8 @@ void Initialize(Local<Object> target,
RemoveGarbageCollectionTracking);
env->SetMethod(target, "notify", Notify);
env->SetMethod(target, "loopIdleTime", LoopIdleTime);
env->SetMethod(target, "getTimeOrigin", GetTimeOrigin);
env->SetMethod(target, "getTimeOriginTimestamp", GetTimeOriginTimeStamp);
Local<Object> constants = Object::New(isolate);
@@ -344,17 +355,6 @@ void Initialize(Local<Object> target,
PropertyAttribute attr =
static_cast<PropertyAttribute>(ReadOnly | DontDelete);
target->DefineOwnProperty(context,
FIXED_ONE_BYTE_STRING(isolate, "timeOrigin"),
Number::New(isolate, timeOrigin / 1e6),
attr).ToChecked();
target->DefineOwnProperty(
context,
FIXED_ONE_BYTE_STRING(isolate, "timeOriginTimestamp"),
Number::New(isolate, timeOriginTimestamp / MICROS_PER_MILLIS),
attr).ToChecked();
target->DefineOwnProperty(context,
env->constants_string(),
constants,
+10
View File
@@ -23,6 +23,7 @@ const expectedModules = new Set([
'Internal Binding module_wrap',
'Internal Binding native_module',
'Internal Binding options',
'Internal Binding performance',
'Internal Binding process_methods',
'Internal Binding report',
'Internal Binding serdes',
@@ -60,6 +61,7 @@ const expectedModules = new Set([
'NativeModule internal/fs/rimraf',
'NativeModule internal/fs/watchers',
'NativeModule internal/heap_utils',
'NativeModule internal/histogram',
'NativeModule internal/idna',
'NativeModule internal/linkedlist',
'NativeModule internal/modules/run_main',
@@ -77,6 +79,14 @@ const expectedModules = new Set([
'NativeModule internal/modules/esm/translators',
'NativeModule internal/process/esm_loader',
'NativeModule internal/options',
'NativeModule internal/perf/event_loop_utilization',
'NativeModule internal/perf/nodetiming',
'NativeModule internal/perf/observe',
'NativeModule internal/perf/performance',
'NativeModule internal/perf/performance_entry',
'NativeModule internal/perf/timerify',
'NativeModule internal/perf/usertiming',
'NativeModule internal/perf/utils',
'NativeModule internal/priority_queue',
'NativeModule internal/process/execution',
'NativeModule internal/process/per_thread',