src,lib: add performance.uvMetricsInfo

This commit exposes a new API to the perf_hooks.performance
module. This wraps uv_metrics_info into
performance.uvMetricsInfo() function.

PR-URL: https://github.com/nodejs/node/pull/54413
Reviewed-By: Matteo Collina <matteo.collina@gmail.com>
Reviewed-By: Benjamin Gruenbaum <benjamingr@gmail.com>
Reviewed-By: Vinícius Lourenço Claro Cardoso <contact@viniciusl.com.br>
Reviewed-By: James M Snell <jasnell@gmail.com>
Reviewed-By: Tobias Nießen <tniessen@tnie.de>
Reviewed-By: Richard Lau <rlau@redhat.com>
Reviewed-By: Marco Ippolito <marcoippolito54@gmail.com>
Reviewed-By: Yagiz Nizipli <yagiz@nizipli.com>
This commit is contained in:
Rafael Gonzaga 2024-08-30 11:22:28 -03:00 committed by GitHub
parent d2479fa020
commit 9a275e15c3
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 137 additions and 0 deletions

View File

@ -887,6 +887,40 @@ added: v8.5.0
The high resolution millisecond timestamp at which the Node.js process was
initialized.
### `performanceNodeTiming.uvMetricsInfo`
<!-- YAML
added: REPLACEME
-->
* Returns: {Object}
* `loopCount` {number} Number of event loop iterations.
* `events` {number} Number of events that have been processed by the event handler.
* `eventsWaiting` {number} Number of events that were waiting to be processed when the event provider was called.
This is a wrapper to the `uv_metrics_info` function.
It returns the current set of event loop metrics.
It is recommended to use this property inside a function whose execution was
scheduled using `setImmediate` to avoid collecting metrics before finishing all
operations scheduled during the current loop iteration.
```cjs
const { performance } = require('node:perf_hooks');
setImmediate(() => {
console.log(performance.nodeTiming.uvMetricsInfo);
});
```
```mjs
import { performance } from 'node:perf_hooks';
setImmediate(() => {
console.log(performance.nodeTiming.uvMetricsInfo);
});
```
### `performanceNodeTiming.v8Start`
<!-- YAML

View File

@ -28,6 +28,7 @@ const {
NODE_PERFORMANCE_MILESTONE_ENVIRONMENT,
},
loopIdleTime,
uvMetricsInfo,
} = internalBinding('performance');
class PerformanceNodeTiming {
@ -122,6 +123,13 @@ class PerformanceNodeTiming {
configurable: true,
get: loopIdleTime,
},
uvMetricsInfo: {
__proto__: null,
enumerable: true,
configurable: true,
get: uvMetricsInfo,
},
});
}

View File

@ -144,6 +144,8 @@
V(env_var_settings_string, "envVarSettings") \
V(errno_string, "errno") \
V(error_string, "error") \
V(events, "events") \
V(events_waiting, "eventsWaiting") \
V(exchange_string, "exchange") \
V(expire_string, "expire") \
V(exponent_string, "exponent") \
@ -213,6 +215,7 @@
V(kind_string, "kind") \
V(length_string, "length") \
V(library_string, "library") \
V(loop_count, "loopCount") \
V(mac_string, "mac") \
V(max_buffer_string, "maxBuffer") \
V(max_concurrent_streams_string, "maxConcurrentStreams") \

View File

@ -261,6 +261,30 @@ void LoopIdleTime(const FunctionCallbackInfo<Value>& args) {
args.GetReturnValue().Set(1.0 * idle_time / NANOS_PER_MILLIS);
}
void UvMetricsInfo(const FunctionCallbackInfo<Value>& args) {
Environment* env = Environment::GetCurrent(args);
uv_metrics_t metrics;
// uv_metrics_info always return 0
CHECK_EQ(uv_metrics_info(env->event_loop(), &metrics), 0);
Local<Object> obj = Object::New(env->isolate());
obj->Set(env->context(),
env->loop_count(),
Integer::NewFromUnsigned(env->isolate(), metrics.loop_count))
.Check();
obj->Set(env->context(),
env->events(),
Integer::NewFromUnsigned(env->isolate(), metrics.events))
.Check();
obj->Set(env->context(),
env->events_waiting(),
Integer::NewFromUnsigned(env->isolate(), metrics.events_waiting))
.Check();
args.GetReturnValue().Set(obj);
}
void CreateELDHistogram(const FunctionCallbackInfo<Value>& args) {
Environment* env = Environment::GetCurrent(args);
int64_t interval = args[0].As<Integer>()->Value();
@ -324,6 +348,7 @@ static void CreatePerIsolateProperties(IsolateData* isolate_data,
SetMethod(isolate, target, "loopIdleTime", LoopIdleTime);
SetMethod(isolate, target, "createELDHistogram", CreateELDHistogram);
SetMethod(isolate, target, "markBootstrapComplete", MarkBootstrapComplete);
SetMethod(isolate, target, "uvMetricsInfo", UvMetricsInfo);
SetFastMethodNoSideEffect(
isolate, target, "now", SlowPerformanceNow, &fast_performance_now);
}
@ -390,6 +415,7 @@ void RegisterExternalReferences(ExternalReferenceRegistry* registry) {
registry->Register(LoopIdleTime);
registry->Register(CreateELDHistogram);
registry->Register(MarkBootstrapComplete);
registry->Register(UvMetricsInfo);
registry->Register(SlowPerformanceNow);
registry->Register(FastPerformanceNow);
registry->Register(fast_performance_now.GetTypeInfo());

View File

@ -0,0 +1,46 @@
// Enforcing strict checks on the order or number of events across different
// platforms can be tricky and unreliable due to various factors.
// As a result, this test relies on the `uv_metrics_info` call instead.
const { performance } = require('node:perf_hooks');
const assert = require('node:assert');
const fs = require('node:fs');
const { nodeTiming } = performance;
function safeMetricsInfo(cb) {
setImmediate(() => {
const info = nodeTiming.uvMetricsInfo;
cb(info);
});
}
{
const info = nodeTiming.uvMetricsInfo;
assert.strictEqual(info.loopCount, 0);
assert.strictEqual(info.events, 0);
// This is the only part of the test that we test events waiting
// Adding checks for this property will make the test flaky
// as it can be highly influenced by race conditions.
assert.strictEqual(info.eventsWaiting, 0);
}
{
// The synchronous call should obviously not affect the uv metrics
const fd = fs.openSync(__filename, 'r');
fs.readFileSync(fd);
const info = nodeTiming.uvMetricsInfo;
assert.strictEqual(info.loopCount, 0);
assert.strictEqual(info.events, 0);
assert.strictEqual(info.eventsWaiting, 0);
}
{
function openFile(info) {
assert.strictEqual(info.loopCount, 1);
fs.open(__filename, 'r', (err) => {
assert.ifError(err);
});
}
safeMetricsInfo(openFile);
}

View File

@ -0,0 +1,20 @@
'use strict';
const common = require('../common');
common.skipIfWorker();
const { spawnSync } = require('node:child_process');
const assert = require('node:assert');
const fixtures = require('../common/fixtures');
const file = fixtures.path('test-nodetiming-uvmetricsinfo.js');
{
const { status, stderr } = spawnSync(
process.execPath,
[
file,
],
);
assert.strictEqual(status, 0, stderr.toString());
}