Skip to content

Commit

Permalink
perf_hooks: refactor perf_hooks for snapshot building
Browse files Browse the repository at this point in the history
- 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 <jasnell@gmail.com>
  • Loading branch information
joyeecheung authored and targos committed Jul 11, 2021
1 parent 0e55cb7 commit 7ea98fb
Show file tree
Hide file tree
Showing 15 changed files with 224 additions and 183 deletions.
7 changes: 7 additions & 0 deletions lib/internal/bootstrap/pre_execution.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ function prepareMainThreadExecution(expandArgv1 = false) {
// Patch the process object with legacy properties and normalizations
patchProcessObject(expandArgv1);
setupTraceCategoryState();
setupPerfHooks();
setupInspectorHooks();
setupWarningHandler();

Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -474,6 +480,7 @@ module.exports = {
setupCoverageHooks,
setupWarningHandler,
setupDebugEnv,
setupPerfHooks,
prepareMainThreadExecution,
initializeDeprecations,
initializeESMLoader,
Expand Down
2 changes: 1 addition & 1 deletion lib/internal/http.js
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
2 changes: 2 additions & 0 deletions lib/internal/main/worker_thread.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ const {
setupInspectorHooks,
setupWarningHandler,
setupDebugEnv,
setupPerfHooks,
initializeDeprecations,
initializeWASI,
initializeCJSLoader,
Expand Down Expand Up @@ -114,6 +115,7 @@ port.on('message', (message) => {
} = message;

setupTraceCategoryState();
setupPerfHooks();
initializeReport();
if (manifestSrc) {
require('internal/process/policy').setup(manifestSrc, manifestURL);
Expand Down
2 changes: 1 addition & 1 deletion lib/internal/perf/event_loop_utilization.js
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
31 changes: 4 additions & 27 deletions lib/internal/perf/nodetiming.js
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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, {
Expand Down Expand Up @@ -159,10 +140,6 @@ class PerformanceNodeTiming {
idleTime: this.idleTime,
};
}

static get [kReadOnlyAttributes]() {
return readOnlyAttributes;
}
}

ObjectSetPrototypeOf(
Expand Down
12 changes: 7 additions & 5 deletions lib/internal/perf/observe.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ const {
const {
InternalPerformanceEntry,
isPerformanceEntry,
} = require('internal/perf/perf');
} = require('internal/perf/performance_entry');

const {
codes: {
Expand Down Expand Up @@ -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;
}
Expand Down
129 changes: 129 additions & 0 deletions lib/internal/perf/performance.js
Original file line number Diff line number Diff line change
@@ -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
};
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,6 @@ const {
TypeError,
} = primordials;

const {
timeOrigin,
} = internalBinding('performance');

const {
customInspectSymbol: kInspect,
} = require('internal/util');
Expand All @@ -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;
Expand Down Expand Up @@ -88,7 +78,5 @@ ObjectSetPrototypeOf(
module.exports = {
InternalPerformanceEntry,
PerformanceEntry,
kReadOnlyAttributes,
isPerformanceEntry,
now,
};
6 changes: 2 additions & 4 deletions lib/internal/perf/timerify.js
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
22 changes: 13 additions & 9 deletions lib/internal/perf/usertiming.js
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -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;
Expand Down
33 changes: 33 additions & 0 deletions lib/internal/perf/utils.js
Original file line number Diff line number Diff line change
@@ -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
};
Loading

0 comments on commit 7ea98fb

Please sign in to comment.