mirror of
https://github.com/nodejs/node.git
synced 2026-10-28 10:15:07 +08:00
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>
This commit is contained in:
1 parent
0519f500b4
commit
2ce7a73fec
10 files changed
+353
-32
No files matched your search
+27
-4
@@ -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
|
||||
|
||||
@@ -1041,8 +1041,10 @@ module.exports = {
|
||||
isHistogram,
|
||||
kDestroy,
|
||||
kHandle,
|
||||
kMaxInt64,
|
||||
kSkipThrow,
|
||||
createHistogram,
|
||||
createSlidingWindowHistogram,
|
||||
importHistogram,
|
||||
validateHistogramOptions,
|
||||
};
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
+11
-10
@@ -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) {
|
||||
|
||||
+7
-7
@@ -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);
|
||||
|
||||
+31
-7
@@ -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) {
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
})();
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
})();
|
||||
+3
@@ -145,6 +145,9 @@ export interface PerformanceBinding {
|
||||
createELDHistogram(
|
||||
interval: number,
|
||||
samplePerIteration: boolean,
|
||||
lowest: bigint,
|
||||
highest: bigint,
|
||||
figures: number,
|
||||
): InternalPerformanceBinding.ELDHistogram;
|
||||
markBootstrapComplete(): void;
|
||||
uvMetricsInfo(): void;
|
||||
|
||||
Reference in new issue
Block a user