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: https://github.com/nodejs/node/pull/48708 Refs: https://github.com/nodejs/build/issues/3043 Reviewed-By: Yagiz Nizipli <yagiz@nizipli.com>
This commit is contained in:
parent
ac34e7561a
commit
4ee4718857
@ -1,33 +1,33 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
const binding = internalBinding('performance');
|
|
||||||
const {
|
const {
|
||||||
|
constants: {
|
||||||
|
NODE_PERFORMANCE_MILESTONE_TIME_ORIGIN,
|
||||||
|
},
|
||||||
milestones,
|
milestones,
|
||||||
getTimeOrigin,
|
} = internalBinding('performance');
|
||||||
} = binding;
|
|
||||||
|
|
||||||
// TODO(joyeecheung): we may want to warn about access to
|
function getTimeOrigin() {
|
||||||
// this during snapshot building.
|
// Do not cache this to prevent it from being serialized into the
|
||||||
let timeOrigin = getTimeOrigin();
|
// snapshot.
|
||||||
|
return milestones[NODE_PERFORMANCE_MILESTONE_TIME_ORIGIN] / 1e6;
|
||||||
function now() {
|
|
||||||
const hr = process.hrtime();
|
|
||||||
return (hr[0] * 1000 + hr[1] / 1e6) - timeOrigin;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Returns the time relative to the process start time in milliseconds.
|
||||||
|
function now() {
|
||||||
|
const hr = process.hrtime();
|
||||||
|
return (hr[0] * 1000 + hr[1] / 1e6) - getTimeOrigin();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns the milestone relative to the process start time in milliseconds.
|
||||||
function getMilestoneTimestamp(milestoneIdx) {
|
function getMilestoneTimestamp(milestoneIdx) {
|
||||||
const ns = milestones[milestoneIdx];
|
const ns = milestones[milestoneIdx];
|
||||||
if (ns === -1)
|
if (ns === -1)
|
||||||
return ns;
|
return ns;
|
||||||
return ns / 1e6 - timeOrigin;
|
return ns / 1e6 - getTimeOrigin();
|
||||||
}
|
|
||||||
|
|
||||||
function refreshTimeOrigin() {
|
|
||||||
timeOrigin = getTimeOrigin();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
now,
|
now,
|
||||||
getMilestoneTimestamp,
|
getMilestoneTimestamp,
|
||||||
refreshTimeOrigin,
|
|
||||||
};
|
};
|
||||||
|
@ -67,7 +67,6 @@ function prepareExecution(options) {
|
|||||||
// Patch the process object with legacy properties and normalizations
|
// Patch the process object with legacy properties and normalizations
|
||||||
patchProcessObject(expandArgv1);
|
patchProcessObject(expandArgv1);
|
||||||
setupTraceCategoryState();
|
setupTraceCategoryState();
|
||||||
setupPerfHooks();
|
|
||||||
setupInspectorHooks();
|
setupInspectorHooks();
|
||||||
setupWarningHandler();
|
setupWarningHandler();
|
||||||
setupFetch();
|
setupFetch();
|
||||||
@ -423,10 +422,6 @@ function setupTraceCategoryState() {
|
|||||||
toggleTraceCategoryState(isTraceCategoryEnabled('node.async_hooks'));
|
toggleTraceCategoryState(isTraceCategoryEnabled('node.async_hooks'));
|
||||||
}
|
}
|
||||||
|
|
||||||
function setupPerfHooks() {
|
|
||||||
require('internal/perf/utils').refreshTimeOrigin();
|
|
||||||
}
|
|
||||||
|
|
||||||
function setupInspectorHooks() {
|
function setupInspectorHooks() {
|
||||||
// If Debugger.setAsyncCallStackDepth is sent during bootstrap,
|
// If Debugger.setAsyncCallStackDepth is sent during bootstrap,
|
||||||
// we cannot immediately call into JS to enable the hooks, which could
|
// we cannot immediately call into JS to enable the hooks, which could
|
||||||
|
@ -783,7 +783,7 @@ Environment::Environment(IsolateData* isolate_data,
|
|||||||
destroy_async_id_list_.reserve(512);
|
destroy_async_id_list_.reserve(512);
|
||||||
|
|
||||||
performance_state_ = std::make_unique<performance::PerformanceState>(
|
performance_state_ = std::make_unique<performance::PerformanceState>(
|
||||||
isolate, MAYBE_FIELD_PTR(env_info, performance_state));
|
isolate, time_origin_, MAYBE_FIELD_PTR(env_info, performance_state));
|
||||||
|
|
||||||
if (*TRACE_EVENT_API_GET_CATEGORY_GROUP_ENABLED(
|
if (*TRACE_EVENT_API_GET_CATEGORY_GROUP_ENABLED(
|
||||||
TRACING_CATEGORY_NODE1(environment)) != 0) {
|
TRACING_CATEGORY_NODE1(environment)) != 0) {
|
||||||
@ -1732,7 +1732,7 @@ void Environment::DeserializeProperties(const EnvSerializeInfo* info) {
|
|||||||
immediate_info_.Deserialize(ctx);
|
immediate_info_.Deserialize(ctx);
|
||||||
timeout_info_.Deserialize(ctx);
|
timeout_info_.Deserialize(ctx);
|
||||||
tick_info_.Deserialize(ctx);
|
tick_info_.Deserialize(ctx);
|
||||||
performance_state_->Deserialize(ctx);
|
performance_state_->Deserialize(ctx, time_origin_);
|
||||||
exit_info_.Deserialize(ctx);
|
exit_info_.Deserialize(ctx);
|
||||||
stream_base_state_.Deserialize(ctx);
|
stream_base_state_.Deserialize(ctx);
|
||||||
should_abort_on_uncaught_toggle_.Deserialize(ctx);
|
should_abort_on_uncaught_toggle_.Deserialize(ctx);
|
||||||
|
@ -20,7 +20,6 @@ using v8::Function;
|
|||||||
using v8::FunctionCallbackInfo;
|
using v8::FunctionCallbackInfo;
|
||||||
using v8::GCCallbackFlags;
|
using v8::GCCallbackFlags;
|
||||||
using v8::GCType;
|
using v8::GCType;
|
||||||
using v8::Int32;
|
|
||||||
using v8::Integer;
|
using v8::Integer;
|
||||||
using v8::Isolate;
|
using v8::Isolate;
|
||||||
using v8::Local;
|
using v8::Local;
|
||||||
@ -43,6 +42,7 @@ const double performance_process_start_timestamp =
|
|||||||
uint64_t performance_v8_start;
|
uint64_t performance_v8_start;
|
||||||
|
|
||||||
PerformanceState::PerformanceState(Isolate* isolate,
|
PerformanceState::PerformanceState(Isolate* isolate,
|
||||||
|
uint64_t time_origin,
|
||||||
const PerformanceState::SerializeInfo* info)
|
const PerformanceState::SerializeInfo* info)
|
||||||
: root(isolate,
|
: root(isolate,
|
||||||
sizeof(performance_state_internal),
|
sizeof(performance_state_internal),
|
||||||
@ -58,24 +58,51 @@ PerformanceState::PerformanceState(Isolate* isolate,
|
|||||||
root,
|
root,
|
||||||
MAYBE_FIELD_PTR(info, observers)) {
|
MAYBE_FIELD_PTR(info, observers)) {
|
||||||
if (info == nullptr) {
|
if (info == nullptr) {
|
||||||
for (size_t i = 0; i < milestones.Length(); i++) milestones[i] = -1.;
|
// For performance states initialized from scratch, reset
|
||||||
|
// all the milestones and initialize the time origin.
|
||||||
|
// For deserialized performance states, we will do the
|
||||||
|
// initialization in the deserialize callback.
|
||||||
|
ResetMilestones();
|
||||||
|
Initialize(time_origin);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void PerformanceState::ResetMilestones() {
|
||||||
|
size_t milestones_length = milestones.Length();
|
||||||
|
for (size_t i = 0; i < milestones_length; ++i) {
|
||||||
|
milestones[i] = -1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
PerformanceState::SerializeInfo PerformanceState::Serialize(
|
PerformanceState::SerializeInfo PerformanceState::Serialize(
|
||||||
v8::Local<v8::Context> context, v8::SnapshotCreator* creator) {
|
v8::Local<v8::Context> context, v8::SnapshotCreator* creator) {
|
||||||
|
// Reset all the milestones to improve determinism in the snapshot.
|
||||||
|
// We'll re-initialize them after deserialization.
|
||||||
|
ResetMilestones();
|
||||||
|
|
||||||
SerializeInfo info{root.Serialize(context, creator),
|
SerializeInfo info{root.Serialize(context, creator),
|
||||||
milestones.Serialize(context, creator),
|
milestones.Serialize(context, creator),
|
||||||
observers.Serialize(context, creator)};
|
observers.Serialize(context, creator)};
|
||||||
return info;
|
return info;
|
||||||
}
|
}
|
||||||
|
|
||||||
void PerformanceState::Deserialize(v8::Local<v8::Context> context) {
|
void PerformanceState::Initialize(uint64_t time_origin) {
|
||||||
|
// We are only reusing the milestone array to store the time origin, so do
|
||||||
|
// not use the Mark() method. The time origin milestone is not exposed
|
||||||
|
// to user land.
|
||||||
|
this->milestones[NODE_PERFORMANCE_MILESTONE_TIME_ORIGIN] =
|
||||||
|
static_cast<double>(time_origin);
|
||||||
|
}
|
||||||
|
|
||||||
|
void PerformanceState::Deserialize(v8::Local<v8::Context> context,
|
||||||
|
uint64_t time_origin) {
|
||||||
|
// Resets the pointers.
|
||||||
root.Deserialize(context);
|
root.Deserialize(context);
|
||||||
// This is just done to set up the pointers, we will actually reset
|
|
||||||
// all the milestones after deserialization.
|
|
||||||
milestones.Deserialize(context);
|
milestones.Deserialize(context);
|
||||||
observers.Deserialize(context);
|
observers.Deserialize(context);
|
||||||
|
|
||||||
|
// Re-initialize the time origin i.e. the process start time.
|
||||||
|
Initialize(time_origin);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::ostream& operator<<(std::ostream& o,
|
std::ostream& operator<<(std::ostream& o,
|
||||||
@ -96,18 +123,6 @@ void PerformanceState::Mark(PerformanceMilestone milestone, uint64_t ts) {
|
|||||||
TRACE_EVENT_SCOPE_THREAD, ts / 1000);
|
TRACE_EVENT_SCOPE_THREAD, ts / 1000);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Allows specific Node.js lifecycle milestones to be set from JavaScript
|
|
||||||
void MarkMilestone(const FunctionCallbackInfo<Value>& args) {
|
|
||||||
Realm* realm = Realm::GetCurrent(args);
|
|
||||||
// TODO(legendecas): Remove this check once the sub-realms are supported.
|
|
||||||
CHECK_EQ(realm->kind(), Realm::Kind::kPrincipal);
|
|
||||||
Environment* env = realm->env();
|
|
||||||
PerformanceMilestone milestone =
|
|
||||||
static_cast<PerformanceMilestone>(args[0].As<Int32>()->Value());
|
|
||||||
if (milestone != NODE_PERFORMANCE_MILESTONE_INVALID)
|
|
||||||
env->performance_state()->Mark(milestone);
|
|
||||||
}
|
|
||||||
|
|
||||||
void SetupPerformanceObservers(const FunctionCallbackInfo<Value>& args) {
|
void SetupPerformanceObservers(const FunctionCallbackInfo<Value>& args) {
|
||||||
Realm* realm = Realm::GetCurrent(args);
|
Realm* realm = Realm::GetCurrent(args);
|
||||||
// TODO(legendecas): Remove this check once the sub-realms are supported.
|
// TODO(legendecas): Remove this check once the sub-realms are supported.
|
||||||
@ -275,12 +290,6 @@ void CreateELDHistogram(const FunctionCallbackInfo<Value>& args) {
|
|||||||
args.GetReturnValue().Set(histogram->object());
|
args.GetReturnValue().Set(histogram->object());
|
||||||
}
|
}
|
||||||
|
|
||||||
void GetTimeOrigin(const FunctionCallbackInfo<Value>& args) {
|
|
||||||
Environment* env = Environment::GetCurrent(args);
|
|
||||||
args.GetReturnValue().Set(
|
|
||||||
Number::New(args.GetIsolate(), env->time_origin() / NANOS_PER_MILLIS));
|
|
||||||
}
|
|
||||||
|
|
||||||
void GetTimeOriginTimeStamp(const FunctionCallbackInfo<Value>& args) {
|
void GetTimeOriginTimeStamp(const FunctionCallbackInfo<Value>& args) {
|
||||||
Environment* env = Environment::GetCurrent(args);
|
Environment* env = Environment::GetCurrent(args);
|
||||||
args.GetReturnValue().Set(Number::New(
|
args.GetReturnValue().Set(Number::New(
|
||||||
@ -300,7 +309,6 @@ static void CreatePerIsolateProperties(IsolateData* isolate_data,
|
|||||||
|
|
||||||
HistogramBase::Initialize(isolate_data, target);
|
HistogramBase::Initialize(isolate_data, target);
|
||||||
|
|
||||||
SetMethod(isolate, target, "markMilestone", MarkMilestone);
|
|
||||||
SetMethod(isolate, target, "setupObservers", SetupPerformanceObservers);
|
SetMethod(isolate, target, "setupObservers", SetupPerformanceObservers);
|
||||||
SetMethod(isolate,
|
SetMethod(isolate,
|
||||||
target,
|
target,
|
||||||
@ -312,7 +320,6 @@ static void CreatePerIsolateProperties(IsolateData* isolate_data,
|
|||||||
RemoveGarbageCollectionTracking);
|
RemoveGarbageCollectionTracking);
|
||||||
SetMethod(isolate, target, "notify", Notify);
|
SetMethod(isolate, target, "notify", Notify);
|
||||||
SetMethod(isolate, target, "loopIdleTime", LoopIdleTime);
|
SetMethod(isolate, target, "loopIdleTime", LoopIdleTime);
|
||||||
SetMethod(isolate, target, "getTimeOrigin", GetTimeOrigin);
|
|
||||||
SetMethod(isolate, target, "getTimeOriginTimestamp", GetTimeOriginTimeStamp);
|
SetMethod(isolate, target, "getTimeOriginTimestamp", GetTimeOriginTimeStamp);
|
||||||
SetMethod(isolate, target, "createELDHistogram", CreateELDHistogram);
|
SetMethod(isolate, target, "createELDHistogram", CreateELDHistogram);
|
||||||
SetMethod(isolate, target, "markBootstrapComplete", MarkBootstrapComplete);
|
SetMethod(isolate, target, "markBootstrapComplete", MarkBootstrapComplete);
|
||||||
@ -373,13 +380,11 @@ void CreatePerContextProperties(Local<Object> target,
|
|||||||
}
|
}
|
||||||
|
|
||||||
void RegisterExternalReferences(ExternalReferenceRegistry* registry) {
|
void RegisterExternalReferences(ExternalReferenceRegistry* registry) {
|
||||||
registry->Register(MarkMilestone);
|
|
||||||
registry->Register(SetupPerformanceObservers);
|
registry->Register(SetupPerformanceObservers);
|
||||||
registry->Register(InstallGarbageCollectionTracking);
|
registry->Register(InstallGarbageCollectionTracking);
|
||||||
registry->Register(RemoveGarbageCollectionTracking);
|
registry->Register(RemoveGarbageCollectionTracking);
|
||||||
registry->Register(Notify);
|
registry->Register(Notify);
|
||||||
registry->Register(LoopIdleTime);
|
registry->Register(LoopIdleTime);
|
||||||
registry->Register(GetTimeOrigin);
|
|
||||||
registry->Register(GetTimeOriginTimeStamp);
|
registry->Register(GetTimeOriginTimeStamp);
|
||||||
registry->Register(CreateELDHistogram);
|
registry->Register(CreateELDHistogram);
|
||||||
registry->Register(MarkBootstrapComplete);
|
registry->Register(MarkBootstrapComplete);
|
||||||
|
@ -25,6 +25,7 @@ extern const double performance_process_start_timestamp;
|
|||||||
extern uint64_t performance_v8_start;
|
extern uint64_t performance_v8_start;
|
||||||
|
|
||||||
#define NODE_PERFORMANCE_MILESTONES(V) \
|
#define NODE_PERFORMANCE_MILESTONES(V) \
|
||||||
|
V(TIME_ORIGIN, "timeOrigin") \
|
||||||
V(ENVIRONMENT, "environment") \
|
V(ENVIRONMENT, "environment") \
|
||||||
V(NODE_START, "nodeStart") \
|
V(NODE_START, "nodeStart") \
|
||||||
V(V8_START, "v8Start") \
|
V(V8_START, "v8Start") \
|
||||||
@ -32,7 +33,6 @@ extern uint64_t performance_v8_start;
|
|||||||
V(LOOP_EXIT, "loopExit") \
|
V(LOOP_EXIT, "loopExit") \
|
||||||
V(BOOTSTRAP_COMPLETE, "bootstrapComplete")
|
V(BOOTSTRAP_COMPLETE, "bootstrapComplete")
|
||||||
|
|
||||||
|
|
||||||
#define NODE_PERFORMANCE_ENTRY_TYPES(V) \
|
#define NODE_PERFORMANCE_ENTRY_TYPES(V) \
|
||||||
V(GC, "gc") \
|
V(GC, "gc") \
|
||||||
V(HTTP, "http") \
|
V(HTTP, "http") \
|
||||||
@ -62,10 +62,12 @@ class PerformanceState {
|
|||||||
AliasedBufferIndex observers;
|
AliasedBufferIndex observers;
|
||||||
};
|
};
|
||||||
|
|
||||||
explicit PerformanceState(v8::Isolate* isolate, const SerializeInfo* info);
|
explicit PerformanceState(v8::Isolate* isolate,
|
||||||
|
uint64_t time_origin,
|
||||||
|
const SerializeInfo* info);
|
||||||
SerializeInfo Serialize(v8::Local<v8::Context> context,
|
SerializeInfo Serialize(v8::Local<v8::Context> context,
|
||||||
v8::SnapshotCreator* creator);
|
v8::SnapshotCreator* creator);
|
||||||
void Deserialize(v8::Local<v8::Context> context);
|
void Deserialize(v8::Local<v8::Context> context, uint64_t time_origin);
|
||||||
friend std::ostream& operator<<(std::ostream& o, const SerializeInfo& i);
|
friend std::ostream& operator<<(std::ostream& o, const SerializeInfo& i);
|
||||||
|
|
||||||
AliasedUint8Array root;
|
AliasedUint8Array root;
|
||||||
@ -79,6 +81,8 @@ class PerformanceState {
|
|||||||
uint64_t ts = PERFORMANCE_NOW());
|
uint64_t ts = PERFORMANCE_NOW());
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
void Initialize(uint64_t time_origin);
|
||||||
|
void ResetMilestones();
|
||||||
struct performance_state_internal {
|
struct performance_state_internal {
|
||||||
// doubles first so that they are always sizeof(double)-aligned
|
// doubles first so that they are always sizeof(double)-aligned
|
||||||
double milestones[NODE_PERFORMANCE_MILESTONE_INVALID];
|
double milestones[NODE_PERFORMANCE_MILESTONE_INVALID];
|
||||||
|
Loading…
x
Reference in New Issue
Block a user