Skip to content
Closed
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Next Next commit
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)
  • Loading branch information
joyeecheung committed Jun 25, 2021
commit 9064c0d977b3515990c67483fc7e3d75b8561d2d
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