From 08edb5b8d31f26b7306f52fbaa8d68d403b08f51 Mon Sep 17 00:00:00 2001 From: James M Snell Date: Fri, 18 Sep 2026 19:16:16 +0000 Subject: [PATCH 1/3] perf_hooks: fix truncation of monitorEventLoopDelay() resolution `IntervalHistogram` stored the interval as `int32_t`, so a resolution above 2^31 - 1 ms wrapped: `resolution: 2 ** 32 + 1` sampled every millisecond. Signed-off-by: James M Snell Assisted-by: OpenCode --- src/histogram.cc | 4 +-- src/histogram.h | 6 ++-- ...oks-monitor-event-loop-delay-resolution.js | 31 +++++++++++++++++++ 3 files changed, 36 insertions(+), 5 deletions(-) create mode 100644 test/parallel/test-perf-hooks-monitor-event-loop-delay-resolution.js diff --git a/src/histogram.cc b/src/histogram.cc index 63f297936773..dc2982632011 100644 --- a/src/histogram.cc +++ b/src/histogram.cc @@ -2379,7 +2379,7 @@ void IntervalHistogram::RegisterExternalReferences( IntervalHistogram::IntervalHistogram(Environment* env, Local wrap, AsyncWrap::ProviderType type, - int32_t interval, + uint64_t interval, OnInterval on_interval, const Histogram::Options& options) : HandleWrap(env, wrap, reinterpret_cast(&timer_), type), @@ -2396,7 +2396,7 @@ IntervalHistogram::IntervalHistogram(Environment* env, BaseObjectPtr IntervalHistogram::Create( Environment* env, - int32_t interval, + uint64_t interval, OnInterval on_interval, const Histogram::Options& options, AsyncWrap::ProviderType type) { diff --git a/src/histogram.h b/src/histogram.h index bc72fa36e104..ee7959b70828 100644 --- a/src/histogram.h +++ b/src/histogram.h @@ -464,7 +464,7 @@ class IntervalHistogram final : public HandleWrap, static BaseObjectPtr Create( Environment* env, - int32_t interval, + uint64_t interval, OnInterval on_interval, const Histogram::Options& options, AsyncWrap::ProviderType type = AsyncWrap::PROVIDER_ELDHISTOGRAM); @@ -472,7 +472,7 @@ class IntervalHistogram final : public HandleWrap, IntervalHistogram(Environment* env, v8::Local wrap, AsyncWrap::ProviderType type, - int32_t interval, + uint64_t interval, OnInterval on_interval, const Histogram::Options& options = Histogram::Options{}); @@ -499,7 +499,7 @@ class IntervalHistogram final : public HandleWrap, template friend void StopHandleHistogram(v8::Local); - int32_t interval_ = 0; + uint64_t interval_ = 0; OnInterval on_interval_ = nullptr; uv_timer_t timer_; diff --git a/test/parallel/test-perf-hooks-monitor-event-loop-delay-resolution.js b/test/parallel/test-perf-hooks-monitor-event-loop-delay-resolution.js new file mode 100644 index 000000000000..f0402524e7c4 --- /dev/null +++ b/test/parallel/test-perf-hooks-monitor-event-loop-delay-resolution.js @@ -0,0 +1,31 @@ +'use strict'; + +// Tests that monitorEventLoopDelay() does not truncate a resolution greater +// than 2 ** 31 - 1 milliseconds to 32 bits. + +const common = require('../common'); +const assert = require('assert'); +const { monitorEventLoopDelay } = require('perf_hooks'); + +// Truncated to 32 bits, this resolution would be 1 ms. +const histogram = monitorEventLoopDelay({ resolution: 2 ** 32 + 1 }); +const control = monitorEventLoopDelay({ resolution: 1 }); +histogram.enable(); +control.enable(); + +const done = common.mustCall(() => { + histogram.disable(); + control.disable(); + // The first sample is recorded after two timer callbacks, which for this + // resolution is roughly 99 days after enable(). + assert.strictEqual(histogram.count, 0); + assert.strictEqual(histogram.exceeds, 0); +}); + +(function wait() { + if (control.count >= 10) { + done(); + } else { + setTimeout(wait, 2); + } +})(); From 4f588580e89829d3ef8dd29f91eb7e8d42227583 Mon Sep 17 00:00:00 2001 From: James M Snell Date: Fri, 18 Sep 2026 19:17:20 +0000 Subject: [PATCH 2/3] doc: fix description of histogram.exceeds It counts values above the highest recordable value, for every histogram type. Event loop delay histograms no longer have a 1 hour limit. Signed-off-by: James M Snell Refs: https://github.com/nodejs/node/pull/41153 Assisted-by: OpenCode --- doc/api/perf_hooks.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/doc/api/perf_hooks.md b/doc/api/perf_hooks.md index 69a4d6613b9b..38892923684e 100644 --- a/doc/api/perf_hooks.md +++ b/doc/api/perf_hooks.md @@ -2160,8 +2160,8 @@ added: v11.10.0 * Type: {number} -The number of times the event loop delay exceeded the maximum 1 hour event -loop delay threshold. +The number of values that were not recorded because they exceeded the +histogram's highest recordable value. ### `histogram.exceedsBigInt` @@ -2173,8 +2173,8 @@ added: * Type: {bigint} -The number of times the event loop delay exceeded the maximum 1 hour event -loop delay threshold. +The number of values that were not recorded because they exceeded the +histogram's highest recordable value. ### `histogram.export()` From ca68db968be4d8921e4a3081bf90926380104a8a Mon Sep 17 00:00:00 2001 From: James M Snell Date: Fri, 18 Sep 2026 19:31:36 +0000 Subject: [PATCH 3/3] perf_hooks: add range options to monitorEventLoopDelay() Accept `lowest`, `highest`, and `figures`, as `createHistogram()` does. The defaults are unchanged, and use up to 432 KiB per histogram. Signed-off-by: James M Snell Assisted-by: OpenCode --- doc/api/perf_hooks.md | 23 ++ lib/internal/histogram.js | 2 + lib/internal/perf/event_loop_delay.js | 30 ++- src/histogram.cc | 17 +- src/histogram.h | 8 +- src/node_perf.cc | 38 +++- ...oks-monitor-event-loop-delay-fast-calls.js | 2 +- ...-hooks-monitor-event-loop-delay-options.js | 213 ++++++++++++++++++ typings/internalBinding/performance.d.ts | 3 + 9 files changed, 313 insertions(+), 23 deletions(-) create mode 100644 test/parallel/test-perf-hooks-monitor-event-loop-delay-options.js diff --git a/doc/api/perf_hooks.md b/doc/api/perf_hooks.md index 38892923684e..2f6a65efc8eb 100644 --- a/doc/api/perf_hooks.md +++ b/doc/api/perf_hooks.md @@ -1879,6 +1879,9 @@ are not guaranteed to reflect any correct state of the event loop.