Commit 2ce7a73fec4 for nodejs
commit 2ce7a73fec4478a340754ee81bc1fc66bef3e250
Author: James M Snell <jasnell@gmail.com>
Date: Sun Sep 27 01:10:15 2026 -0700
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 <jasnell@gmail.com>
Assisted-by: OpenCode
PR-URL: https://github.com/nodejs/node/pull/66115
Reviewed-By: Matteo Collina <matteo.collina@gmail.com>
Reviewed-By: Filip Skokan <panva.ip@gmail.com>
diff --git a/doc/api/perf_hooks.md b/doc/api/perf_hooks.md
index f9533c1260a..dcdfd0f93c5 100644
--- a/doc/api/perf_hooks.md
+++ b/doc/api/perf_hooks.md
@@ -1926,6 +1926,9 @@ are not guaranteed to reflect any correct state of the event loop.
<!-- YAML
added: v11.10.0
changes:
+ - version: REPLACEME
+ pr-url: https://github.com/nodejs/node/pull/66115
+ description: Added the `lowest`, `highest`, and `figures` options.
- version:
- v26.5.0
- v24.19.0
@@ -1939,6 +1942,14 @@ changes:
* `resolution` {number} The sampling rate in milliseconds for interval-based
sampling. Must be greater than zero. This option is ignored when
`samplePerIteration` is `true`. **Default:** `10`.
+ * `lowest` {number|bigint} The lowest discernible delay, in nanoseconds. Must
+ be an integer value greater than `0`. **Default:** `1` when
+ `samplePerIteration` is `true`, otherwise `1000`.
+ * `highest` {number|bigint} The highest recordable delay, in nanoseconds.
+ Must be an integer value that is equal to or greater than two times
+ `lowest`. **Default:** `2n ** 63n - 1n`.
+ * `figures` {number} The number of accuracy digits. Must be an integer
+ between `1` and `5`. **Default:** `3`.
* Returns: {ELDHistogram}
_This property is an extension by Node.js. It is not available in Web browsers._
@@ -1954,6 +1965,16 @@ the application is idle.
The two sampling modes produce significantly different results and should not
be compared directly.
+The `lowest`, `highest`, and `figures` options configure the histogram as they
+do for [`perf_hooks.createHistogram()`][]. `lowest` must be greater than `0`
+because an event loop delay of zero is not possible: the event loop has a
+minimal overhead, and the measurement itself depends on the event loop turning.
+Delays greater than `highest` are not recorded, and are counted by
+[`histogram.exceeds`][] instead. With interval-based sampling, every sample
+includes the `resolution`, so `highest` should be well above
+`resolution * 1e6`. The histogram's memory use depends on these options, not
+on the number of samples.
+
```mjs
import { monitorEventLoopDelay } from 'node:perf_hooks';
@@ -2253,8 +2274,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`
@@ -2266,8 +2287,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()`
@@ -3432,7 +3453,9 @@ dns.promises.resolve('localhost');
[`'exit'`]: process.md#event-exit
[`child_process.spawnSync()`]: child_process.md#child_processspawnsynccommand-args-options
[`histogram.diff()`]: #histogramdiffother
+[`histogram.exceeds`]: #histogramexceeds
[`histogram.export()`]: #histogramexport
+[`perf_hooks.createHistogram()`]: #perf_hookscreatehistogramoptions
[`perf_hooks.createSlidingWindowHistogram()`]: #perf_hookscreateslidingwindowhistogramoptions
[`perf_hooks.eventLoopUtilization()`]: #perf_hookseventlooputilizationutilization1-utilization2
[`perf_hooks.importHistogram()`]: #perf_hooksimporthistogramdata
diff --git a/lib/internal/histogram.js b/lib/internal/histogram.js
index 4014f3aeb74..9366eb13543 100644
--- a/lib/internal/histogram.js
+++ b/lib/internal/histogram.js
@@ -1041,8 +1041,10 @@ module.exports = {
isHistogram,
kDestroy,
kHandle,
+ kMaxInt64,
kSkipThrow,
createHistogram,
createSlidingWindowHistogram,
importHistogram,
+ validateHistogramOptions,
};
diff --git a/lib/internal/perf/event_loop_delay.js b/lib/internal/perf/event_loop_delay.js
index 4d14182c83f..541d6e38fcb 100644
--- a/lib/internal/perf/event_loop_delay.js
+++ b/lib/internal/perf/event_loop_delay.js
@@ -1,5 +1,6 @@
'use strict';
const {
+ BigInt,
Symbol,
SymbolDispose,
} = primordials;
@@ -24,7 +25,9 @@ const {
const {
Histogram,
kHandle,
+ kMaxInt64,
kSkipThrow,
+ validateHistogramOptions,
} = require('internal/histogram');
const {
@@ -37,6 +40,12 @@ const {
const kEnabled = Symbol('kEnabled');
+// Default histogram options. The lowest discernible delay is in nanoseconds,
+// and its default depends on the sampling mode.
+const kDefaultIntervalLowest = 1000;
+const kDefaultIterationLowest = 1;
+const kDefaultFigures = 3;
+
class ELDHistogram extends Histogram {
constructor(skipThrowSymbol = undefined) {
if (skipThrowSymbol !== kSkipThrow) {
@@ -76,8 +85,11 @@ class ELDHistogram extends Histogram {
/**
* @param {{
- * samplePerIteration : boolean,
- * resolution : number
+ * samplePerIteration? : boolean,
+ * resolution? : number,
+ * lowest? : number|bigint,
+ * highest? : number|bigint,
+ * figures? : number,
* }} [options]
* @returns {ELDHistogram}
*/
@@ -88,10 +100,22 @@ function monitorEventLoopDelay(options = kEmptyObject) {
validateBoolean(samplePerIteration, 'options.samplePerIteration');
validateInteger(resolution, 'options.resolution', 1);
+ const {
+ lowest = samplePerIteration ?
+ kDefaultIterationLowest : kDefaultIntervalLowest,
+ highest = kMaxInt64,
+ figures = kDefaultFigures,
+ } = options;
+ validateHistogramOptions(lowest, highest, figures);
+
+ // Throws if the native histogram cannot be created with these options.
+ const handle = createELDHistogram(
+ resolution, samplePerIteration, BigInt(lowest), BigInt(highest), figures);
+
const histogram = new ELDHistogram(kSkipThrow);
markTransferMode(histogram, true, false);
histogram[kEnabled] = false;
- histogram[kHandle] = createELDHistogram(resolution, samplePerIteration);
+ histogram[kHandle] = handle;
return histogram;
}
diff --git a/src/histogram.cc b/src/histogram.cc
index eb7fd575c98..6efe2d6985c 100644
--- a/src/histogram.cc
+++ b/src/histogram.cc
@@ -2616,11 +2616,11 @@ void IntervalHistogram::RegisterExternalReferences(
IntervalHistogram::IntervalHistogram(Environment* env,
Local<Object> wrap,
AsyncWrap::ProviderType type,
- int32_t interval,
+ uint64_t interval,
OnInterval on_interval,
- const Histogram::Options& options)
+ std::shared_ptr<Histogram> histogram)
: HandleWrap(env, wrap, reinterpret_cast<uv_handle_t*>(&timer_), type),
- HistogramImpl(options),
+ HistogramImpl(std::move(histogram)),
interval_(interval),
on_interval_(on_interval) {
MakeWeak();
@@ -2633,9 +2633,9 @@ IntervalHistogram::IntervalHistogram(Environment* env,
BaseObjectPtr<IntervalHistogram> IntervalHistogram::Create(
Environment* env,
- int32_t interval,
+ uint64_t interval,
OnInterval on_interval,
- const Histogram::Options& options,
+ std::shared_ptr<Histogram> histogram,
AsyncWrap::ProviderType type) {
Local<Object> obj;
if (!GetConstructorTemplate(env)
@@ -2646,7 +2646,7 @@ BaseObjectPtr<IntervalHistogram> IntervalHistogram::Create(
}
return MakeBaseObject<IntervalHistogram>(
- env, obj, type, interval, on_interval, options);
+ env, obj, type, interval, on_interval, std::move(histogram));
}
void IntervalHistogram::TimerCB(uv_timer_t* handle) {
@@ -2712,10 +2712,10 @@ void IterationHistogram::RegisterExternalReferences(
IterationHistogram::IterationHistogram(Environment* env,
Local<Object> wrap,
AsyncWrap::ProviderType type,
- const Histogram::Options& options)
+ std::shared_ptr<Histogram> histogram)
: HandleWrap(
env, wrap, reinterpret_cast<uv_handle_t*>(&check_handle_), type),
- HistogramImpl(options) {
+ HistogramImpl(std::move(histogram)) {
MakeWeak();
wrap->SetAlignedPointerInInternalField(
HistogramImpl::InternalFields::kImplField,
@@ -2729,7 +2729,7 @@ IterationHistogram::IterationHistogram(Environment* env,
BaseObjectPtr<IterationHistogram> IterationHistogram::Create(
Environment* env,
- const Histogram::Options& options,
+ std::shared_ptr<Histogram> histogram,
AsyncWrap::ProviderType type) {
Local<Object> obj;
if (!GetConstructorTemplate(env)
@@ -2739,7 +2739,8 @@ BaseObjectPtr<IterationHistogram> IterationHistogram::Create(
return nullptr;
}
- return MakeBaseObject<IterationHistogram>(env, obj, type, options);
+ return MakeBaseObject<IterationHistogram>(
+ env, obj, type, std::move(histogram));
}
void IterationHistogram::PrepareCB(uv_prepare_t* handle) {
diff --git a/src/histogram.h b/src/histogram.h
index 93fe2b267d4..2bbbbe7d7ad 100644
--- a/src/histogram.h
+++ b/src/histogram.h
@@ -499,17 +499,17 @@ class IntervalHistogram final : public HandleWrap,
static BaseObjectPtr<IntervalHistogram> Create(
Environment* env,
- int32_t interval,
+ uint64_t interval,
OnInterval on_interval,
- const Histogram::Options& options,
+ std::shared_ptr<Histogram> histogram,
AsyncWrap::ProviderType type = AsyncWrap::PROVIDER_ELDHISTOGRAM);
IntervalHistogram(Environment* env,
v8::Local<v8::Object> wrap,
AsyncWrap::ProviderType type,
- int32_t interval,
+ uint64_t interval,
OnInterval on_interval,
- const Histogram::Options& options = Histogram::Options{});
+ std::shared_ptr<Histogram> histogram);
static void FastStart(v8::Local<v8::Value> receiver, bool reset);
static void FastStop(v8::Local<v8::Value> receiver);
@@ -534,7 +534,7 @@ class IntervalHistogram final : public HandleWrap,
template <typename T>
friend void StopHandleHistogram(v8::Local<v8::Value>);
- int32_t interval_ = 0;
+ uint64_t interval_ = 0;
OnInterval on_interval_ = nullptr;
uv_timer_t timer_;
@@ -559,13 +559,13 @@ class IterationHistogram final
static BaseObjectPtr<IterationHistogram> Create(
Environment* env,
- const Histogram::Options& options,
+ std::shared_ptr<Histogram> histogram,
AsyncWrap::ProviderType type = AsyncWrap::PROVIDER_ELDHISTOGRAM);
IterationHistogram(Environment* env,
v8::Local<v8::Object> wrap,
AsyncWrap::ProviderType type,
- const Histogram::Options& options = Histogram::Options{});
+ std::shared_ptr<Histogram> histogram);
static void FastStart(v8::Local<v8::Value> receiver, bool reset);
static void FastStop(v8::Local<v8::Value> receiver);
diff --git a/src/node_perf.cc b/src/node_perf.cc
index 381a8bdf91e..74f3c1d8f9e 100644
--- a/src/node_perf.cc
+++ b/src/node_perf.cc
@@ -4,6 +4,7 @@
#include "histogram-inl.h"
#include "memory_tracker-inl.h"
#include "node_buffer.h"
+#include "node_errors.h"
#include "node_external_reference.h"
#include "node_internals.h"
#include "node_process-inl.h"
@@ -14,6 +15,7 @@
namespace node {
namespace performance {
+using v8::BigInt;
using v8::Context;
using v8::DontDelete;
using v8::Function;
@@ -28,6 +30,7 @@ using v8::Object;
using v8::ObjectTemplate;
using v8::PropertyAttribute;
using v8::ReadOnly;
+using v8::Uint32;
using v8::Value;
// Microseconds in a millisecond, as a float.
@@ -318,14 +321,34 @@ void CreateELDHistogram(const FunctionCallbackInfo<Value>& args) {
Environment* env = Environment::GetCurrent(args);
int64_t interval = args[0].As<Integer>()->Value();
CHECK_GT(interval, 0);
+ CHECK(args[2]->IsBigInt());
+ CHECK(args[3]->IsBigInt());
+ CHECK(args[4]->IsUint32());
+ bool lossless = true;
+ const int64_t lowest = args[2].As<BigInt>()->Int64Value(&lossless);
+ CHECK(lossless);
+ const int64_t highest = args[3].As<BigInt>()->Int64Value(&lossless);
+ CHECK(lossless);
+ const int figures = static_cast<int>(args[4].As<Uint32>()->Value());
+
+ // The options are validated in JS, but hdr_init() still rejects some
+ // combinations, such as a very large lowest value.
+ std::shared_ptr<Histogram> histogram =
+ Histogram::Create(Histogram::Options{lowest, highest, figures});
+ if (!histogram) {
+ return THROW_ERR_INVALID_ARG_VALUE(env, "Invalid histogram options");
+ }
+
if (args[1]->IsTrue()) {
- BaseObjectPtr<IterationHistogram> histogram =
- IterationHistogram::Create(env, Histogram::Options{1});
- args.GetReturnValue().Set(histogram->object());
+ BaseObjectPtr<IterationHistogram> eld =
+ IterationHistogram::Create(env, std::move(histogram));
+ if (eld) args.GetReturnValue().Set(eld->object());
return;
}
- BaseObjectPtr<IntervalHistogram> histogram =
- IntervalHistogram::Create(env, interval, [](Histogram& histogram) {
+ BaseObjectPtr<IntervalHistogram> eld = IntervalHistogram::Create(
+ env,
+ interval,
+ [](Histogram& histogram) {
uint64_t delta = histogram.RecordDelta();
TRACE_COUNTER1(TRACING_CATEGORY_NODE2(perf, event_loop),
"delay", delta);
@@ -337,8 +360,9 @@ void CreateELDHistogram(const FunctionCallbackInfo<Value>& args) {
"mean", histogram.Mean());
TRACE_COUNTER1(TRACING_CATEGORY_NODE2(perf, event_loop),
"stddev", histogram.Stddev());
- }, Histogram::Options { 1000 });
- args.GetReturnValue().Set(histogram->object());
+ },
+ std::move(histogram));
+ if (eld) args.GetReturnValue().Set(eld->object());
}
void MarkBootstrapComplete(const FunctionCallbackInfo<Value>& args) {
diff --git a/test/parallel/test-perf-hooks-monitor-event-loop-delay-fast-calls.js b/test/parallel/test-perf-hooks-monitor-event-loop-delay-fast-calls.js
index 1954f52e441..cdd038d57d2 100644
--- a/test/parallel/test-perf-hooks-monitor-event-loop-delay-fast-calls.js
+++ b/test/parallel/test-perf-hooks-monitor-event-loop-delay-fast-calls.js
@@ -7,7 +7,7 @@ const assert = require('assert');
const { internalBinding } = require('internal/test/binding');
const { createELDHistogram } = internalBinding('performance');
-const histogram = createELDHistogram(1, true);
+const histogram = createELDHistogram(1, true, 1n, 2n ** 63n - 1n, 3);
function testFastMethods() {
histogram.start(true);
diff --git a/test/parallel/test-perf-hooks-monitor-event-loop-delay-options.js b/test/parallel/test-perf-hooks-monitor-event-loop-delay-options.js
new file mode 100644
index 00000000000..265061659b8
--- /dev/null
+++ b/test/parallel/test-perf-hooks-monitor-event-loop-delay-options.js
@@ -0,0 +1,213 @@
+'use strict';
+
+// Tests the lowest, highest, and figures options of monitorEventLoopDelay().
+
+const common = require('../common');
+const assert = require('assert');
+const { monitorEventLoopDelay } = require('perf_hooks');
+
+const kMaxInt64 = 2n ** 63n - 1n;
+
+function readHead(data, offset) {
+ const view = new DataView(data.buffer, data.byteOffset, data.byteLength);
+ const info = data[offset] & 0x1f;
+ const major = data[offset] >> 5;
+ switch (info) {
+ case 24: return { major, argument: BigInt(view.getUint8(offset + 1)), next: offset + 2 };
+ case 25: return { major, argument: BigInt(view.getUint16(offset + 1)), next: offset + 3 };
+ case 26: return { major, argument: BigInt(view.getUint32(offset + 1)), next: offset + 5 };
+ case 27: return { major, argument: view.getBigUint64(offset + 1), next: offset + 9 };
+ default:
+ assert.ok(info < 24);
+ return { major, argument: BigInt(info), next: offset + 1 };
+ }
+}
+
+// Returns the histogram configuration from the documented export() format:
+// a CBOR map in which key 1 is the lowest discernible value, key 2 the highest
+// trackable value, key 3 the number of significant figures, and key 9 the
+// length of the counts array.
+function getLayout(histogram) {
+ const data = histogram.export();
+ const map = readHead(data, 0);
+ assert.strictEqual(map.major, 5);
+ const fields = new Map();
+ let offset = map.next;
+ for (let n = 0n; n < map.argument; n++) {
+ const key = readHead(data, offset);
+ const value = readHead(data, key.next);
+ offset = value.next;
+ if (value.major === 4) {
+ // The counts array contains unsigned integers.
+ for (let i = 0n; i < value.argument; i++)
+ offset = readHead(data, offset).next;
+ }
+ fields.set(key.argument, value.argument);
+ }
+ return {
+ lowest: fields.get(1n),
+ highest: fields.get(2n),
+ figures: fields.get(3n),
+ countsLength: fields.get(9n),
+ };
+}
+
+{
+ // The defaults are unchanged.
+ assert.deepStrictEqual(getLayout(monitorEventLoopDelay()), {
+ lowest: 1000n,
+ highest: kMaxInt64,
+ figures: 3n,
+ countsLength: 46080n,
+ });
+ assert.deepStrictEqual(
+ getLayout(monitorEventLoopDelay({ samplePerIteration: true })), {
+ lowest: 1n,
+ highest: kMaxInt64,
+ figures: 3n,
+ countsLength: 55296n,
+ });
+}
+
+for (const samplePerIteration of [false, true]) {
+ assert.deepStrictEqual(getLayout(monitorEventLoopDelay({
+ samplePerIteration,
+ lowest: 1000,
+ highest: 3_600_000_000_000,
+ figures: 2,
+ })), {
+ lowest: 1000n,
+ highest: 3_600_000_000_000n,
+ figures: 2n,
+ countsLength: 3456n,
+ });
+
+ assert.deepStrictEqual(getLayout(monitorEventLoopDelay({
+ samplePerIteration,
+ lowest: 1_000_000n,
+ highest: 60_000_000_000n,
+ figures: 1,
+ })), {
+ lowest: 1_000_000n,
+ highest: 60_000_000_000n,
+ figures: 1n,
+ countsLength: 224n,
+ });
+
+ assert.deepStrictEqual(getLayout(monitorEventLoopDelay({
+ samplePerIteration,
+ lowest: 1_000_000,
+ })), {
+ lowest: 1_000_000n,
+ highest: kMaxInt64,
+ figures: 3n,
+ countsLength: 35840n,
+ });
+
+ for (const name of ['lowest', 'highest', 'figures']) {
+ for (const value of ['a', null, false, {}, []]) {
+ assert.throws(() => monitorEventLoopDelay({
+ samplePerIteration,
+ [name]: value,
+ }), { code: 'ERR_INVALID_ARG_TYPE' });
+ }
+ }
+ assert.throws(() => monitorEventLoopDelay({ samplePerIteration, figures: 3n }),
+ { code: 'ERR_INVALID_ARG_TYPE' });
+
+ for (const options of [
+ { lowest: 0 },
+ { lowest: 1.5 },
+ { lowest: 2 ** 53 },
+ { lowest: 0n },
+ { lowest: 2n ** 63n },
+ { highest: 0 },
+ { highest: 1.5 },
+ { highest: 2 ** 53 },
+ { highest: 2n ** 63n },
+ { lowest: 10, highest: 19 },
+ { lowest: 10n, highest: 19n },
+ { figures: 0 },
+ { figures: 6 },
+ { figures: 1.5 },
+ ]) {
+ assert.throws(() => monitorEventLoopDelay({
+ samplePerIteration,
+ ...options,
+ }), { code: 'ERR_OUT_OF_RANGE' });
+ }
+
+ // These options pass validation, but the histogram cannot be created.
+ assert.throws(() => monitorEventLoopDelay({
+ samplePerIteration,
+ lowest: 2 ** 45,
+ highest: 2 ** 46,
+ figures: 5,
+ }), { code: 'ERR_INVALID_ARG_VALUE' });
+}
+
+{
+ // The default lowest depends on the sampling mode, and highest is validated
+ // against it.
+ assert.throws(() => monitorEventLoopDelay({ highest: 1999 }),
+ { code: 'ERR_OUT_OF_RANGE' });
+ monitorEventLoopDelay({ highest: 2000 });
+ monitorEventLoopDelay({ samplePerIteration: true, highest: 1999 });
+ assert.throws(() => monitorEventLoopDelay({
+ samplePerIteration: true,
+ highest: 1,
+ }), { code: 'ERR_OUT_OF_RANGE' });
+}
+
+{
+ // A right-sized histogram records one sample per event loop iteration.
+ const iterations = 10;
+ const histogram = monitorEventLoopDelay({
+ samplePerIteration: true,
+ lowest: 1000,
+ highest: 3_600_000_000_000,
+ figures: 2,
+ });
+ histogram.enable();
+
+ const done = common.mustCall(() => {
+ histogram.disable();
+ assert.ok(histogram.count >= iterations - 1,
+ `Expected at least ${iterations - 1} samples, got ${histogram.count}`);
+ assert.strictEqual(histogram.exceeds, 0);
+ });
+
+ let remaining = iterations;
+ function tick() {
+ if (--remaining > 0) {
+ setImmediate(tick);
+ } else {
+ done();
+ }
+ }
+ setImmediate(tick);
+}
+
+{
+ // Check that delays greater than highest are counted by exceeds.
+ // Delayed timer callbacks can run close together, so some samples may
+ // still be recorded despite resolution being much larger than highest.
+ const histogram = monitorEventLoopDelay({
+ resolution: 20,
+ highest: 2_000_000,
+ });
+ histogram.enable();
+
+ const done = common.mustCall(() => {
+ histogram.disable();
+ assert.ok(histogram.exceeds >= 2);
+ });
+
+ (function wait() {
+ if (histogram.exceeds >= 2) {
+ done();
+ } else {
+ setTimeout(wait, 5);
+ }
+ })();
+}
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 00000000000..f0402524e7c
--- /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);
+ }
+})();
diff --git a/typings/internalBinding/performance.d.ts b/typings/internalBinding/performance.d.ts
index 98a176ea1ac..abf07578a4a 100644
--- a/typings/internalBinding/performance.d.ts
+++ b/typings/internalBinding/performance.d.ts
@@ -145,6 +145,9 @@ export interface PerformanceBinding {
createELDHistogram(
interval: number,
samplePerIteration: boolean,
+ lowest: bigint,
+ highest: bigint,
+ figures: number,
): InternalPerformanceBinding.ELDHistogram;
markBootstrapComplete(): void;
uvMetricsInfo(): void;