mirror of
https://github.com/nodejs/node.git
synced 2026-10-28 10:15:07 +08:00
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:
15 files changed
+224
-183
No files matched your search
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
};
|
||||
@@ -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,
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
};
|
||||
@@ -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
@@ -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
@@ -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,
|
||||
|
||||
@@ -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',
|
||||
|
||||
Reference in new issue
Block a user