diff --git a/lib/internal/perf/nodetiming.js b/lib/internal/perf/nodetiming.js index a9e0c3f252ce..5de5e3e6644f 100644 --- a/lib/internal/perf/nodetiming.js +++ b/lib/internal/perf/nodetiming.js @@ -29,6 +29,7 @@ const { }, loopIdleTime, uvMetricsInfo, + uvMetricsBuffer, } = internalBinding('performance'); class PerformanceNodeTiming { @@ -129,11 +130,11 @@ class PerformanceNodeTiming { enumerable: true, configurable: true, get: () => { - const metrics = uvMetricsInfo(); + uvMetricsInfo(); return { - loopCount: metrics[0], - events: metrics[1], - eventsWaiting: metrics[2], + loopCount: uvMetricsBuffer[0], + events: uvMetricsBuffer[1], + eventsWaiting: uvMetricsBuffer[2], }; }, }, diff --git a/src/node_perf.cc b/src/node_perf.cc index 177c2a789854..75a62b89a534 100644 --- a/src/node_perf.cc +++ b/src/node_perf.cc @@ -14,7 +14,6 @@ namespace node { namespace performance { -using v8::Array; using v8::Context; using v8::DontDelete; using v8::Function; @@ -57,7 +56,12 @@ PerformanceState::PerformanceState(Isolate* isolate, offsetof(performance_state_internal, observers), NODE_PERFORMANCE_ENTRY_TYPE_INVALID, root, - MAYBE_FIELD_PTR(info, observers)) { + MAYBE_FIELD_PTR(info, observers)), + uv_metrics(isolate, + offsetof(performance_state_internal, uv_metrics), + 3, + root, + MAYBE_FIELD_PTR(info, uv_metrics)) { if (info == nullptr) { // For performance states initialized from scratch, reset // all the milestones and initialize the time origin. @@ -81,9 +85,15 @@ PerformanceState::SerializeInfo PerformanceState::Serialize( // We'll re-initialize them after deserialization. ResetMilestones(); + // Do not retain runtime metrics in the snapshot. + for (size_t i = 0; i < uv_metrics.Length(); ++i) { + uv_metrics[i] = 0; + } + SerializeInfo info{root.Serialize(context, creator), milestones.Serialize(context, creator), - observers.Serialize(context, creator)}; + observers.Serialize(context, creator), + uv_metrics.Serialize(context, creator)}; return info; } @@ -105,6 +115,7 @@ void PerformanceState::Deserialize(v8::Local context, root.Deserialize(context); milestones.Deserialize(context); observers.Deserialize(context); + uv_metrics.Deserialize(context); // Re-initialize the time origin and timestamp i.e. the process start time. Initialize(time_origin, time_origin_timestamp); @@ -116,6 +127,7 @@ std::ostream& operator<<(std::ostream& o, << " " << i.root << ", // root\n" << " " << i.milestones << ", // milestones\n" << " " << i.observers << ", // observers\n" + << " " << i.uv_metrics << ", // uv_metrics\n" << "}"; return o; } @@ -265,17 +277,13 @@ void LoopIdleTime(const FunctionCallbackInfo& args) { void UvMetricsInfo(const FunctionCallbackInfo& args) { Environment* env = Environment::GetCurrent(args); - Isolate* isolate = env->isolate(); uv_metrics_t metrics; // uv_metrics_info always return 0 CHECK_EQ(uv_metrics_info(env->event_loop(), &metrics), 0); - Local data[] = { - Integer::New(isolate, metrics.loop_count), - Integer::New(isolate, metrics.events), - Integer::New(isolate, metrics.events_waiting), - }; - Local arr = Array::New(env->isolate(), data, arraysize(data)); - args.GetReturnValue().Set(arr); + AliasedInt32Array& buffer = env->performance_state()->uv_metrics; + buffer[0] = static_cast(metrics.loop_count); + buffer[1] = static_cast(metrics.events); + buffer[2] = static_cast(metrics.events_waiting); } void CreateELDHistogram(const FunctionCallbackInfo& args) { @@ -366,6 +374,11 @@ void CreatePerContextProperties(Local target, target->Set(context, FIXED_ONE_BYTE_STRING(isolate, "milestones"), state->milestones.GetJSArray()).Check(); + target + ->Set(context, + FIXED_ONE_BYTE_STRING(isolate, "uvMetricsBuffer"), + state->uv_metrics.GetJSArray()) + .Check(); Local constants = Object::New(isolate); diff --git a/src/node_perf_common.h b/src/node_perf_common.h index 01e7f35241ac..aa84ba55b08e 100644 --- a/src/node_perf_common.h +++ b/src/node_perf_common.h @@ -62,6 +62,7 @@ class PerformanceState { AliasedBufferIndex root; AliasedBufferIndex milestones; AliasedBufferIndex observers; + AliasedBufferIndex uv_metrics; }; explicit PerformanceState(v8::Isolate* isolate, @@ -78,6 +79,7 @@ class PerformanceState { AliasedUint8Array root; AliasedFloat64Array milestones; AliasedUint32Array observers; + AliasedInt32Array uv_metrics; uint64_t performance_last_gc_start_mark = 0; uint16_t current_gc_type = 0; @@ -92,6 +94,7 @@ class PerformanceState { // doubles first so that they are always sizeof(double)-aligned double milestones[NODE_PERFORMANCE_MILESTONE_INVALID]; uint32_t observers[NODE_PERFORMANCE_ENTRY_TYPE_INVALID]; + int32_t uv_metrics[3]; }; }; diff --git a/src/node_snapshotable.cc b/src/node_snapshotable.cc index e861e499534c..cb0938d49af7 100644 --- a/src/node_snapshotable.cc +++ b/src/node_snapshotable.cc @@ -392,6 +392,7 @@ size_t SnapshotSerializer::Write(const ImmediateInfo::SerializeInfo& data) { // [ 4/8 bytes ] snapshot index of root // [ 4/8 bytes ] snapshot index of milestones // [ 4/8 bytes ] snapshot index of observers +// [ 4/8 bytes ] snapshot index of uv_metrics template <> performance::PerformanceState::SerializeInfo SnapshotDeserializer::Read() { Debug("Read()\n"); @@ -400,6 +401,7 @@ performance::PerformanceState::SerializeInfo SnapshotDeserializer::Read() { result.root = ReadArithmetic(); result.milestones = ReadArithmetic(); result.observers = ReadArithmetic(); + result.uv_metrics = ReadArithmetic(); if (is_debug) { std::string str = ToStr(result); Debug("Read() %s\n", str); @@ -418,6 +420,7 @@ size_t SnapshotSerializer::Write( size_t written_total = WriteArithmetic(data.root); written_total += WriteArithmetic(data.milestones); written_total += WriteArithmetic(data.observers); + written_total += WriteArithmetic(data.uv_metrics); Debug("Write() wrote %d bytes\n", written_total); diff --git a/test/fixtures/test-nodetiming-uvmetricsinfo.js b/test/fixtures/test-nodetiming-uvmetricsinfo.js index 59b1cc8ebf11..038ca8b79904 100644 --- a/test/fixtures/test-nodetiming-uvmetricsinfo.js +++ b/test/fixtures/test-nodetiming-uvmetricsinfo.js @@ -40,7 +40,15 @@ function safeMetricsInfo(cb) { fs.open(__filename, 'r', (err) => { assert.ifError(err); }); + + const saved = { ...info }; + safeMetricsInfo((nextInfo) => { + assert.notStrictEqual(nextInfo, info); + assert.ok(nextInfo.loopCount > saved.loopCount); + // Updating the shared buffer must not change earlier results. + assert.deepStrictEqual(info, saved); + }); } safeMetricsInfo(openFile); -} \ No newline at end of file +} diff --git a/typings/internalBinding/performance.d.ts b/typings/internalBinding/performance.d.ts index fa9a3810fc7a..dc4d1e20c6b3 100644 --- a/typings/internalBinding/performance.d.ts +++ b/typings/internalBinding/performance.d.ts @@ -129,6 +129,7 @@ export interface PerformanceBinding { samplePerIteration: boolean, ): InternalPerformanceBinding.ELDHistogram; markBootstrapComplete(): void; - uvMetricsInfo(): [number, number, number]; + uvMetricsInfo(): void; + uvMetricsBuffer: Int32Array; now(): number; }