Skip to content

Commit 7ea98fb

Browse files
joyeecheungtargos
authored andcommitted
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: #38971 Refs: #35711 Reviewed-By: James M Snell <[email protected]>
1 parent 0e55cb7 commit 7ea98fb

15 files changed

+224
-183
lines changed

Diff for: lib/internal/bootstrap/pre_execution.js

+7
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ function prepareMainThreadExecution(expandArgv1 = false) {
2727
// Patch the process object with legacy properties and normalizations
2828
patchProcessObject(expandArgv1);
2929
setupTraceCategoryState();
30+
setupPerfHooks();
3031
setupInspectorHooks();
3132
setupWarningHandler();
3233

@@ -222,6 +223,11 @@ function setupTraceCategoryState() {
222223
toggleTraceCategoryState(isTraceCategoryEnabled('node.async_hooks'));
223224
}
224225

226+
function setupPerfHooks() {
227+
require('internal/perf/performance').refreshTimeOrigin();
228+
require('internal/perf/utils').refreshTimeOrigin();
229+
}
230+
225231
function setupInspectorHooks() {
226232
// If Debugger.setAsyncCallStackDepth is sent during bootstrap,
227233
// we cannot immediately call into JS to enable the hooks, which could
@@ -474,6 +480,7 @@ module.exports = {
474480
setupCoverageHooks,
475481
setupWarningHandler,
476482
setupDebugEnv,
483+
setupPerfHooks,
477484
prepareMainThreadExecution,
478485
initializeDeprecations,
479486
initializeESMLoader,

Diff for: lib/internal/http.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ const {
99

1010
const { setUnrefTimeout } = require('internal/timers');
1111

12-
const { InternalPerformanceEntry } = require('internal/perf/perf');
12+
const { InternalPerformanceEntry } = require('internal/perf/performance_entry');
1313

1414
const {
1515
enqueue,

Diff for: lib/internal/main/worker_thread.js

+2
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ const {
1818
setupInspectorHooks,
1919
setupWarningHandler,
2020
setupDebugEnv,
21+
setupPerfHooks,
2122
initializeDeprecations,
2223
initializeWASI,
2324
initializeCJSLoader,
@@ -114,6 +115,7 @@ port.on('message', (message) => {
114115
} = message;
115116

116117
setupTraceCategoryState();
118+
setupPerfHooks();
117119
initializeReport();
118120
if (manifestSrc) {
119121
require('internal/process/policy').setup(manifestSrc, manifestURL);

Diff for: lib/internal/perf/event_loop_utilization.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
const nodeTiming = require('internal/perf/nodetiming');
44

5-
const { now } = require('internal/perf/perf');
5+
const { now } = require('internal/perf/utils');
66

77
function eventLoopUtilization(util1, util2) {
88
const ls = nodeTiming.loopStart;

Diff for: lib/internal/perf/nodetiming.js

+4-27
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,14 @@
33
const {
44
ObjectDefineProperties,
55
ObjectSetPrototypeOf,
6-
SafeArrayIterator,
7-
SafeSet,
86
} = primordials;
97

8+
const { PerformanceEntry } = require('internal/perf/performance_entry');
9+
1010
const {
11-
PerformanceEntry,
12-
kReadOnlyAttributes,
1311
now,
14-
} = require('internal/perf/perf');
12+
getMilestoneTimestamp,
13+
} = require('internal/perf/utils');
1514

1615
const {
1716
customInspectSymbol: kInspect,
@@ -29,26 +28,8 @@ const {
2928
NODE_PERFORMANCE_MILESTONE_ENVIRONMENT
3029
},
3130
loopIdleTime,
32-
milestones,
33-
timeOrigin,
3431
} = internalBinding('performance');
3532

36-
function getMilestoneTimestamp(milestoneIdx) {
37-
const ns = milestones[milestoneIdx];
38-
if (ns === -1)
39-
return ns;
40-
return ns / 1e6 - timeOrigin;
41-
}
42-
43-
const readOnlyAttributes = new SafeSet(new SafeArrayIterator([
44-
'nodeStart',
45-
'v8Start',
46-
'environment',
47-
'loopStart',
48-
'loopExit',
49-
'bootstrapComplete',
50-
]));
51-
5233
class PerformanceNodeTiming {
5334
constructor() {
5435
ObjectDefineProperties(this, {
@@ -159,10 +140,6 @@ class PerformanceNodeTiming {
159140
idleTime: this.idleTime,
160141
};
161142
}
162-
163-
static get [kReadOnlyAttributes]() {
164-
return readOnlyAttributes;
165-
}
166143
}
167144

168145
ObjectSetPrototypeOf(

Diff for: lib/internal/perf/observe.js

+7-5
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ const {
3131
const {
3232
InternalPerformanceEntry,
3333
isPerformanceEntry,
34-
} = require('internal/perf/perf');
34+
} = require('internal/perf/performance_entry');
3535

3636
const {
3737
codes: {
@@ -174,11 +174,13 @@ class PerformanceObserverEntryList {
174174
}
175175

176176
class PerformanceObserver {
177-
[kBuffer] = [];
178-
[kEntryTypes] = new SafeSet();
179-
[kType] = undefined;
180-
181177
constructor(callback) {
178+
// TODO(joyeecheung): V8 snapshot does not support instance member
179+
// initializers for now:
180+
// https://bugs.chromium.org/p/v8/issues/detail?id=10704
181+
this[kBuffer] = [];
182+
this[kEntryTypes] = new SafeSet();
183+
this[kType] = undefined;
182184
validateCallback(callback);
183185
this[kCallback] = callback;
184186
}

Diff for: lib/internal/perf/performance.js

+129
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
'use strict';
2+
3+
const {
4+
ObjectDefineProperty,
5+
ObjectDefineProperties,
6+
ObjectSetPrototypeOf,
7+
TypeError,
8+
} = primordials;
9+
10+
const {
11+
EventTarget,
12+
} = require('internal/event_target');
13+
14+
const { now } = require('internal/perf/utils');
15+
16+
const {
17+
mark,
18+
measure,
19+
clearMarks,
20+
} = require('internal/perf/usertiming');
21+
22+
const eventLoopUtilization = require('internal/perf/event_loop_utilization');
23+
const nodeTiming = require('internal/perf/nodetiming');
24+
const timerify = require('internal/perf/timerify');
25+
const { customInspectSymbol: kInspect } = require('internal/util');
26+
const { inspect } = require('util');
27+
28+
const {
29+
getTimeOriginTimestamp
30+
} = internalBinding('performance');
31+
32+
class Performance extends EventTarget {
33+
constructor() {
34+
// eslint-disable-next-line no-restricted-syntax
35+
throw new TypeError('Illegal constructor');
36+
}
37+
38+
[kInspect](depth, options) {
39+
if (depth < 0) return this;
40+
41+
const opts = {
42+
...options,
43+
depth: options.depth == null ? null : options.depth - 1
44+
};
45+
46+
return `Performance ${inspect({
47+
nodeTiming: this.nodeTiming,
48+
timeOrigin: this.timeOrigin,
49+
}, opts)}`;
50+
}
51+
52+
}
53+
54+
function toJSON() {
55+
return {
56+
nodeTiming: this.nodeTiming,
57+
timeOrigin: this.timeOrigin,
58+
eventLoopUtilization: this.eventLoopUtilization()
59+
};
60+
}
61+
62+
class InternalPerformance extends EventTarget {}
63+
InternalPerformance.prototype.constructor = Performance.prototype.constructor;
64+
ObjectSetPrototypeOf(InternalPerformance.prototype, Performance.prototype);
65+
66+
ObjectDefineProperties(Performance.prototype, {
67+
clearMarks: {
68+
configurable: true,
69+
enumerable: false,
70+
value: clearMarks,
71+
},
72+
eventLoopUtilization: {
73+
configurable: true,
74+
enumerable: false,
75+
value: eventLoopUtilization,
76+
},
77+
mark: {
78+
configurable: true,
79+
enumerable: false,
80+
value: mark,
81+
},
82+
measure: {
83+
configurable: true,
84+
enumerable: false,
85+
value: measure,
86+
},
87+
nodeTiming: {
88+
configurable: true,
89+
enumerable: false,
90+
value: nodeTiming,
91+
},
92+
now: {
93+
configurable: true,
94+
enumerable: false,
95+
value: now,
96+
},
97+
timerify: {
98+
configurable: true,
99+
enumerable: false,
100+
value: timerify,
101+
},
102+
// This would be updated during pre-execution in case
103+
// the process is launched from a snapshot.
104+
// TODO(joyeecheung): we may want to warn about access to
105+
// this during snapshot building.
106+
timeOrigin: {
107+
configurable: true,
108+
enumerable: true,
109+
value: getTimeOriginTimestamp(),
110+
},
111+
toJSON: {
112+
configurable: true,
113+
enumerable: true,
114+
value: toJSON,
115+
}
116+
});
117+
118+
function refreshTimeOrigin() {
119+
ObjectDefineProperty(Performance.prototype, 'timeOrigin', {
120+
configurable: true,
121+
enumerable: true,
122+
value: getTimeOriginTimestamp(),
123+
});
124+
}
125+
126+
module.exports = {
127+
InternalPerformance,
128+
refreshTimeOrigin
129+
};

Diff for: lib/internal/perf/perf.js renamed to lib/internal/perf/performance_entry.js

-12
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,6 @@ const {
66
TypeError,
77
} = primordials;
88

9-
const {
10-
timeOrigin,
11-
} = internalBinding('performance');
12-
139
const {
1410
customInspectSymbol: kInspect,
1511
} = require('internal/util');
@@ -21,12 +17,6 @@ const kType = Symbol('kType');
2117
const kStart = Symbol('kStart');
2218
const kDuration = Symbol('kDuration');
2319
const kDetail = Symbol('kDetail');
24-
const kReadOnlyAttributes = Symbol('kReadOnlyAttributes');
25-
26-
function now() {
27-
const hr = process.hrtime();
28-
return (hr[0] * 1000 + hr[1] / 1e6) - timeOrigin;
29-
}
3020

3121
function isPerformanceEntry(obj) {
3222
return obj?.[kName] !== undefined;
@@ -88,7 +78,5 @@ ObjectSetPrototypeOf(
8878
module.exports = {
8979
InternalPerformanceEntry,
9080
PerformanceEntry,
91-
kReadOnlyAttributes,
9281
isPerformanceEntry,
93-
now,
9482
};

Diff for: lib/internal/perf/timerify.js

+2-4
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,8 @@ const {
99
Symbol,
1010
} = primordials;
1111

12-
const {
13-
InternalPerformanceEntry,
14-
now,
15-
} = require('internal/perf/perf');
12+
const { InternalPerformanceEntry } = require('internal/perf/performance_entry');
13+
const { now } = require('internal/perf/utils');
1614

1715
const {
1816
validateFunction,

Diff for: lib/internal/perf/usertiming.js

+13-9
Original file line numberDiff line numberDiff line change
@@ -3,16 +3,13 @@
33
const {
44
ObjectKeys,
55
SafeMap,
6+
SafeSet,
7+
SafeArrayIterator,
68
} = primordials;
79

8-
const {
9-
InternalPerformanceEntry,
10-
kReadOnlyAttributes,
11-
now,
12-
} = require('internal/perf/perf');
13-
10+
const { InternalPerformanceEntry } = require('internal/perf/performance_entry');
11+
const { now } = require('internal/perf/utils');
1412
const { enqueue } = require('internal/perf/observe');
15-
1613
const nodeTiming = require('internal/perf/nodetiming');
1714

1815
const {
@@ -31,8 +28,15 @@ const {
3128
} = require('internal/errors');
3229

3330
const marks = new SafeMap();
34-
const nodeTimingReadOnlyAttributes =
35-
nodeTiming.constructor[kReadOnlyAttributes];
31+
32+
const nodeTimingReadOnlyAttributes = new SafeSet(new SafeArrayIterator([
33+
'nodeStart',
34+
'v8Start',
35+
'environment',
36+
'loopStart',
37+
'loopExit',
38+
'bootstrapComplete',
39+
]));
3640

3741
function getMark(name) {
3842
if (name === undefined) return;

Diff for: lib/internal/perf/utils.js

+33
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
'use strict';
2+
3+
const binding = internalBinding('performance');
4+
const {
5+
milestones,
6+
getTimeOrigin,
7+
} = binding;
8+
9+
// TODO(joyeecheung): we may want to warn about access to
10+
// this during snapshot building.
11+
let timeOrigin = getTimeOrigin();
12+
13+
function now() {
14+
const hr = process.hrtime();
15+
return (hr[0] * 1000 + hr[1] / 1e6) - timeOrigin;
16+
}
17+
18+
function getMilestoneTimestamp(milestoneIdx) {
19+
const ns = milestones[milestoneIdx];
20+
if (ns === -1)
21+
return ns;
22+
return ns / 1e6 - timeOrigin;
23+
}
24+
25+
function refreshTimeOrigin() {
26+
timeOrigin = getTimeOrigin();
27+
}
28+
29+
module.exports = {
30+
now,
31+
getMilestoneTimestamp,
32+
refreshTimeOrigin
33+
};

0 commit comments

Comments
 (0)