Skip to content

Commit f663bf8

Browse files
joyeecheungpluris
authored and
pluris
committed
src: save the performance milestone time origin in the AliasedArray
Previously we cache the time origin for the milestones in the user land, and refresh it at pre-execution. As result the time origin gets serialized into the snapshot and is therefore not deterministic. Now we store it in the milestone array as an internal value and reset the milestones at serialization time instead of deserialization time. This improves the determinism of the snapshot. Drive-by: remove the unused MarkMilestone() binding. PR-URL: nodejs#48708 Refs: nodejs/build#3043 Reviewed-By: Yagiz Nizipli <[email protected]>
1 parent 61f1868 commit f663bf8

File tree

5 files changed

+60
-56
lines changed

5 files changed

+60
-56
lines changed

lib/internal/perf/utils.js

+13-13
Original file line numberDiff line numberDiff line change
@@ -1,33 +1,33 @@
11
'use strict';
22

3-
const binding = internalBinding('performance');
43
const {
4+
constants: {
5+
NODE_PERFORMANCE_MILESTONE_TIME_ORIGIN,
6+
},
57
milestones,
6-
getTimeOrigin,
7-
} = binding;
8+
} = internalBinding('performance');
89

9-
// TODO(joyeecheung): we may want to warn about access to
10-
// this during snapshot building.
11-
let timeOrigin = getTimeOrigin();
10+
function getTimeOrigin() {
11+
// Do not cache this to prevent it from being serialized into the
12+
// snapshot.
13+
return milestones[NODE_PERFORMANCE_MILESTONE_TIME_ORIGIN] / 1e6;
14+
}
1215

16+
// Returns the time relative to the process start time in milliseconds.
1317
function now() {
1418
const hr = process.hrtime();
15-
return (hr[0] * 1000 + hr[1] / 1e6) - timeOrigin;
19+
return (hr[0] * 1000 + hr[1] / 1e6) - getTimeOrigin();
1620
}
1721

22+
// Returns the milestone relative to the process start time in milliseconds.
1823
function getMilestoneTimestamp(milestoneIdx) {
1924
const ns = milestones[milestoneIdx];
2025
if (ns === -1)
2126
return ns;
22-
return ns / 1e6 - timeOrigin;
23-
}
24-
25-
function refreshTimeOrigin() {
26-
timeOrigin = getTimeOrigin();
27+
return ns / 1e6 - getTimeOrigin();
2728
}
2829

2930
module.exports = {
3031
now,
3132
getMilestoneTimestamp,
32-
refreshTimeOrigin,
3333
};

lib/internal/process/pre_execution.js

-5
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,6 @@ function prepareExecution(options) {
6767
// Patch the process object with legacy properties and normalizations
6868
patchProcessObject(expandArgv1);
6969
setupTraceCategoryState();
70-
setupPerfHooks();
7170
setupInspectorHooks();
7271
setupWarningHandler();
7372
setupFetch();
@@ -423,10 +422,6 @@ function setupTraceCategoryState() {
423422
toggleTraceCategoryState(isTraceCategoryEnabled('node.async_hooks'));
424423
}
425424

426-
function setupPerfHooks() {
427-
require('internal/perf/utils').refreshTimeOrigin();
428-
}
429-
430425
function setupInspectorHooks() {
431426
// If Debugger.setAsyncCallStackDepth is sent during bootstrap,
432427
// we cannot immediately call into JS to enable the hooks, which could

src/env.cc

+2-2
Original file line numberDiff line numberDiff line change
@@ -783,7 +783,7 @@ Environment::Environment(IsolateData* isolate_data,
783783
destroy_async_id_list_.reserve(512);
784784

785785
performance_state_ = std::make_unique<performance::PerformanceState>(
786-
isolate, MAYBE_FIELD_PTR(env_info, performance_state));
786+
isolate, time_origin_, MAYBE_FIELD_PTR(env_info, performance_state));
787787

788788
if (*TRACE_EVENT_API_GET_CATEGORY_GROUP_ENABLED(
789789
TRACING_CATEGORY_NODE1(environment)) != 0) {
@@ -1732,7 +1732,7 @@ void Environment::DeserializeProperties(const EnvSerializeInfo* info) {
17321732
immediate_info_.Deserialize(ctx);
17331733
timeout_info_.Deserialize(ctx);
17341734
tick_info_.Deserialize(ctx);
1735-
performance_state_->Deserialize(ctx);
1735+
performance_state_->Deserialize(ctx, time_origin_);
17361736
exit_info_.Deserialize(ctx);
17371737
stream_base_state_.Deserialize(ctx);
17381738
should_abort_on_uncaught_toggle_.Deserialize(ctx);

src/node_perf.cc

+32-27
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@ using v8::Function;
2020
using v8::FunctionCallbackInfo;
2121
using v8::GCCallbackFlags;
2222
using v8::GCType;
23-
using v8::Int32;
2423
using v8::Integer;
2524
using v8::Isolate;
2625
using v8::Local;
@@ -43,6 +42,7 @@ const double performance_process_start_timestamp =
4342
uint64_t performance_v8_start;
4443

4544
PerformanceState::PerformanceState(Isolate* isolate,
45+
uint64_t time_origin,
4646
const PerformanceState::SerializeInfo* info)
4747
: root(isolate,
4848
sizeof(performance_state_internal),
@@ -58,24 +58,51 @@ PerformanceState::PerformanceState(Isolate* isolate,
5858
root,
5959
MAYBE_FIELD_PTR(info, observers)) {
6060
if (info == nullptr) {
61-
for (size_t i = 0; i < milestones.Length(); i++) milestones[i] = -1.;
61+
// For performance states initialized from scratch, reset
62+
// all the milestones and initialize the time origin.
63+
// For deserialized performance states, we will do the
64+
// initialization in the deserialize callback.
65+
ResetMilestones();
66+
Initialize(time_origin);
67+
}
68+
}
69+
70+
void PerformanceState::ResetMilestones() {
71+
size_t milestones_length = milestones.Length();
72+
for (size_t i = 0; i < milestones_length; ++i) {
73+
milestones[i] = -1;
6274
}
6375
}
6476

6577
PerformanceState::SerializeInfo PerformanceState::Serialize(
6678
v8::Local<v8::Context> context, v8::SnapshotCreator* creator) {
79+
// Reset all the milestones to improve determinism in the snapshot.
80+
// We'll re-initialize them after deserialization.
81+
ResetMilestones();
82+
6783
SerializeInfo info{root.Serialize(context, creator),
6884
milestones.Serialize(context, creator),
6985
observers.Serialize(context, creator)};
7086
return info;
7187
}
7288

73-
void PerformanceState::Deserialize(v8::Local<v8::Context> context) {
89+
void PerformanceState::Initialize(uint64_t time_origin) {
90+
// We are only reusing the milestone array to store the time origin, so do
91+
// not use the Mark() method. The time origin milestone is not exposed
92+
// to user land.
93+
this->milestones[NODE_PERFORMANCE_MILESTONE_TIME_ORIGIN] =
94+
static_cast<double>(time_origin);
95+
}
96+
97+
void PerformanceState::Deserialize(v8::Local<v8::Context> context,
98+
uint64_t time_origin) {
99+
// Resets the pointers.
74100
root.Deserialize(context);
75-
// This is just done to set up the pointers, we will actually reset
76-
// all the milestones after deserialization.
77101
milestones.Deserialize(context);
78102
observers.Deserialize(context);
103+
104+
// Re-initialize the time origin i.e. the process start time.
105+
Initialize(time_origin);
79106
}
80107

81108
std::ostream& operator<<(std::ostream& o,
@@ -96,18 +123,6 @@ void PerformanceState::Mark(PerformanceMilestone milestone, uint64_t ts) {
96123
TRACE_EVENT_SCOPE_THREAD, ts / 1000);
97124
}
98125

99-
// Allows specific Node.js lifecycle milestones to be set from JavaScript
100-
void MarkMilestone(const FunctionCallbackInfo<Value>& args) {
101-
Realm* realm = Realm::GetCurrent(args);
102-
// TODO(legendecas): Remove this check once the sub-realms are supported.
103-
CHECK_EQ(realm->kind(), Realm::Kind::kPrincipal);
104-
Environment* env = realm->env();
105-
PerformanceMilestone milestone =
106-
static_cast<PerformanceMilestone>(args[0].As<Int32>()->Value());
107-
if (milestone != NODE_PERFORMANCE_MILESTONE_INVALID)
108-
env->performance_state()->Mark(milestone);
109-
}
110-
111126
void SetupPerformanceObservers(const FunctionCallbackInfo<Value>& args) {
112127
Realm* realm = Realm::GetCurrent(args);
113128
// TODO(legendecas): Remove this check once the sub-realms are supported.
@@ -275,12 +290,6 @@ void CreateELDHistogram(const FunctionCallbackInfo<Value>& args) {
275290
args.GetReturnValue().Set(histogram->object());
276291
}
277292

278-
void GetTimeOrigin(const FunctionCallbackInfo<Value>& args) {
279-
Environment* env = Environment::GetCurrent(args);
280-
args.GetReturnValue().Set(
281-
Number::New(args.GetIsolate(), env->time_origin() / NANOS_PER_MILLIS));
282-
}
283-
284293
void GetTimeOriginTimeStamp(const FunctionCallbackInfo<Value>& args) {
285294
Environment* env = Environment::GetCurrent(args);
286295
args.GetReturnValue().Set(Number::New(
@@ -300,7 +309,6 @@ static void CreatePerIsolateProperties(IsolateData* isolate_data,
300309

301310
HistogramBase::Initialize(isolate_data, target);
302311

303-
SetMethod(isolate, target, "markMilestone", MarkMilestone);
304312
SetMethod(isolate, target, "setupObservers", SetupPerformanceObservers);
305313
SetMethod(isolate,
306314
target,
@@ -312,7 +320,6 @@ static void CreatePerIsolateProperties(IsolateData* isolate_data,
312320
RemoveGarbageCollectionTracking);
313321
SetMethod(isolate, target, "notify", Notify);
314322
SetMethod(isolate, target, "loopIdleTime", LoopIdleTime);
315-
SetMethod(isolate, target, "getTimeOrigin", GetTimeOrigin);
316323
SetMethod(isolate, target, "getTimeOriginTimestamp", GetTimeOriginTimeStamp);
317324
SetMethod(isolate, target, "createELDHistogram", CreateELDHistogram);
318325
SetMethod(isolate, target, "markBootstrapComplete", MarkBootstrapComplete);
@@ -373,13 +380,11 @@ void CreatePerContextProperties(Local<Object> target,
373380
}
374381

375382
void RegisterExternalReferences(ExternalReferenceRegistry* registry) {
376-
registry->Register(MarkMilestone);
377383
registry->Register(SetupPerformanceObservers);
378384
registry->Register(InstallGarbageCollectionTracking);
379385
registry->Register(RemoveGarbageCollectionTracking);
380386
registry->Register(Notify);
381387
registry->Register(LoopIdleTime);
382-
registry->Register(GetTimeOrigin);
383388
registry->Register(GetTimeOriginTimeStamp);
384389
registry->Register(CreateELDHistogram);
385390
registry->Register(MarkBootstrapComplete);

src/node_perf_common.h

+13-9
Original file line numberDiff line numberDiff line change
@@ -24,15 +24,15 @@ extern const uint64_t performance_process_start;
2424
extern const double performance_process_start_timestamp;
2525
extern uint64_t performance_v8_start;
2626

27-
#define NODE_PERFORMANCE_MILESTONES(V) \
28-
V(ENVIRONMENT, "environment") \
29-
V(NODE_START, "nodeStart") \
30-
V(V8_START, "v8Start") \
31-
V(LOOP_START, "loopStart") \
32-
V(LOOP_EXIT, "loopExit") \
27+
#define NODE_PERFORMANCE_MILESTONES(V) \
28+
V(TIME_ORIGIN, "timeOrigin") \
29+
V(ENVIRONMENT, "environment") \
30+
V(NODE_START, "nodeStart") \
31+
V(V8_START, "v8Start") \
32+
V(LOOP_START, "loopStart") \
33+
V(LOOP_EXIT, "loopExit") \
3334
V(BOOTSTRAP_COMPLETE, "bootstrapComplete")
3435

35-
3636
#define NODE_PERFORMANCE_ENTRY_TYPES(V) \
3737
V(GC, "gc") \
3838
V(HTTP, "http") \
@@ -62,10 +62,12 @@ class PerformanceState {
6262
AliasedBufferIndex observers;
6363
};
6464

65-
explicit PerformanceState(v8::Isolate* isolate, const SerializeInfo* info);
65+
explicit PerformanceState(v8::Isolate* isolate,
66+
uint64_t time_origin,
67+
const SerializeInfo* info);
6668
SerializeInfo Serialize(v8::Local<v8::Context> context,
6769
v8::SnapshotCreator* creator);
68-
void Deserialize(v8::Local<v8::Context> context);
70+
void Deserialize(v8::Local<v8::Context> context, uint64_t time_origin);
6971
friend std::ostream& operator<<(std::ostream& o, const SerializeInfo& i);
7072

7173
AliasedUint8Array root;
@@ -79,6 +81,8 @@ class PerformanceState {
7981
uint64_t ts = PERFORMANCE_NOW());
8082

8183
private:
84+
void Initialize(uint64_t time_origin);
85+
void ResetMilestones();
8286
struct performance_state_internal {
8387
// doubles first so that they are always sizeof(double)-aligned
8488
double milestones[NODE_PERFORMANCE_MILESTONE_INVALID];

0 commit comments

Comments
 (0)