Skip to content

Commit 9ba75fa

Browse files
committedSep 1, 2024
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: #54413 Reviewed-By: Matteo Collina <[email protected]> Reviewed-By: Benjamin Gruenbaum <[email protected]> Reviewed-By: Vinícius Lourenço Claro Cardoso <[email protected]> Reviewed-By: James M Snell <[email protected]> Reviewed-By: Tobias Nießen <[email protected]> Reviewed-By: Richard Lau <[email protected]> Reviewed-By: Marco Ippolito <[email protected]> Reviewed-By: Yagiz Nizipli <[email protected]>
1 parent fe5666f commit 9ba75fa

File tree

6 files changed

+137
-0
lines changed

6 files changed

+137
-0
lines changed
 

‎doc/api/perf_hooks.md

+34
Original file line numberDiff line numberDiff line change
@@ -887,6 +887,40 @@ added: v8.5.0
887887
The high resolution millisecond timestamp at which the Node.js process was
888888
initialized.
889889

890+
### `performanceNodeTiming.uvMetricsInfo`
891+
892+
<!-- YAML
893+
added: REPLACEME
894+
-->
895+
896+
* Returns: {Object}
897+
* `loopCount` {number} Number of event loop iterations.
898+
* `events` {number} Number of events that have been processed by the event handler.
899+
* `eventsWaiting` {number} Number of events that were waiting to be processed when the event provider was called.
900+
901+
This is a wrapper to the `uv_metrics_info` function.
902+
It returns the current set of event loop metrics.
903+
904+
It is recommended to use this property inside a function whose execution was
905+
scheduled using `setImmediate` to avoid collecting metrics before finishing all
906+
operations scheduled during the current loop iteration.
907+
908+
```cjs
909+
const { performance } = require('node:perf_hooks');
910+
911+
setImmediate(() => {
912+
console.log(performance.nodeTiming.uvMetricsInfo);
913+
});
914+
```
915+
916+
```mjs
917+
import { performance } from 'node:perf_hooks';
918+
919+
setImmediate(() => {
920+
console.log(performance.nodeTiming.uvMetricsInfo);
921+
});
922+
```
923+
890924
### `performanceNodeTiming.v8Start`
891925

892926
<!-- YAML

‎lib/internal/perf/nodetiming.js

+8
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ const {
2828
NODE_PERFORMANCE_MILESTONE_ENVIRONMENT,
2929
},
3030
loopIdleTime,
31+
uvMetricsInfo,
3132
} = internalBinding('performance');
3233

3334
class PerformanceNodeTiming {
@@ -122,6 +123,13 @@ class PerformanceNodeTiming {
122123
configurable: true,
123124
get: loopIdleTime,
124125
},
126+
127+
uvMetricsInfo: {
128+
__proto__: null,
129+
enumerable: true,
130+
configurable: true,
131+
get: uvMetricsInfo,
132+
},
125133
});
126134
}
127135

‎src/env_properties.h

+3
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,8 @@
144144
V(env_var_settings_string, "envVarSettings") \
145145
V(errno_string, "errno") \
146146
V(error_string, "error") \
147+
V(events, "events") \
148+
V(events_waiting, "eventsWaiting") \
147149
V(exchange_string, "exchange") \
148150
V(expire_string, "expire") \
149151
V(exponent_string, "exponent") \
@@ -213,6 +215,7 @@
213215
V(kind_string, "kind") \
214216
V(length_string, "length") \
215217
V(library_string, "library") \
218+
V(loop_count, "loopCount") \
216219
V(mac_string, "mac") \
217220
V(max_buffer_string, "maxBuffer") \
218221
V(max_concurrent_streams_string, "maxConcurrentStreams") \

‎src/node_perf.cc

+26
Original file line numberDiff line numberDiff line change
@@ -261,6 +261,30 @@ void LoopIdleTime(const FunctionCallbackInfo<Value>& args) {
261261
args.GetReturnValue().Set(1.0 * idle_time / NANOS_PER_MILLIS);
262262
}
263263

264+
void UvMetricsInfo(const FunctionCallbackInfo<Value>& args) {
265+
Environment* env = Environment::GetCurrent(args);
266+
uv_metrics_t metrics;
267+
268+
// uv_metrics_info always return 0
269+
CHECK_EQ(uv_metrics_info(env->event_loop(), &metrics), 0);
270+
271+
Local<Object> obj = Object::New(env->isolate());
272+
obj->Set(env->context(),
273+
env->loop_count(),
274+
Integer::NewFromUnsigned(env->isolate(), metrics.loop_count))
275+
.Check();
276+
obj->Set(env->context(),
277+
env->events(),
278+
Integer::NewFromUnsigned(env->isolate(), metrics.events))
279+
.Check();
280+
obj->Set(env->context(),
281+
env->events_waiting(),
282+
Integer::NewFromUnsigned(env->isolate(), metrics.events_waiting))
283+
.Check();
284+
285+
args.GetReturnValue().Set(obj);
286+
}
287+
264288
void CreateELDHistogram(const FunctionCallbackInfo<Value>& args) {
265289
Environment* env = Environment::GetCurrent(args);
266290
int64_t interval = args[0].As<Integer>()->Value();
@@ -324,6 +348,7 @@ static void CreatePerIsolateProperties(IsolateData* isolate_data,
324348
SetMethod(isolate, target, "loopIdleTime", LoopIdleTime);
325349
SetMethod(isolate, target, "createELDHistogram", CreateELDHistogram);
326350
SetMethod(isolate, target, "markBootstrapComplete", MarkBootstrapComplete);
351+
SetMethod(isolate, target, "uvMetricsInfo", UvMetricsInfo);
327352
SetFastMethodNoSideEffect(
328353
isolate, target, "now", SlowPerformanceNow, &fast_performance_now);
329354
}
@@ -390,6 +415,7 @@ void RegisterExternalReferences(ExternalReferenceRegistry* registry) {
390415
registry->Register(LoopIdleTime);
391416
registry->Register(CreateELDHistogram);
392417
registry->Register(MarkBootstrapComplete);
418+
registry->Register(UvMetricsInfo);
393419
registry->Register(SlowPerformanceNow);
394420
registry->Register(FastPerformanceNow);
395421
registry->Register(fast_performance_now.GetTypeInfo());
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
// Enforcing strict checks on the order or number of events across different
2+
// platforms can be tricky and unreliable due to various factors.
3+
// As a result, this test relies on the `uv_metrics_info` call instead.
4+
const { performance } = require('node:perf_hooks');
5+
const assert = require('node:assert');
6+
const fs = require('node:fs');
7+
const { nodeTiming } = performance;
8+
9+
function safeMetricsInfo(cb) {
10+
setImmediate(() => {
11+
const info = nodeTiming.uvMetricsInfo;
12+
cb(info);
13+
});
14+
}
15+
16+
{
17+
const info = nodeTiming.uvMetricsInfo;
18+
assert.strictEqual(info.loopCount, 0);
19+
assert.strictEqual(info.events, 0);
20+
// This is the only part of the test that we test events waiting
21+
// Adding checks for this property will make the test flaky
22+
// as it can be highly influenced by race conditions.
23+
assert.strictEqual(info.eventsWaiting, 0);
24+
}
25+
26+
{
27+
// The synchronous call should obviously not affect the uv metrics
28+
const fd = fs.openSync(__filename, 'r');
29+
fs.readFileSync(fd);
30+
const info = nodeTiming.uvMetricsInfo;
31+
assert.strictEqual(info.loopCount, 0);
32+
assert.strictEqual(info.events, 0);
33+
assert.strictEqual(info.eventsWaiting, 0);
34+
}
35+
36+
{
37+
function openFile(info) {
38+
assert.strictEqual(info.loopCount, 1);
39+
40+
fs.open(__filename, 'r', (err) => {
41+
assert.ifError(err);
42+
});
43+
}
44+
45+
safeMetricsInfo(openFile);
46+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
'use strict';
2+
3+
const common = require('../common');
4+
common.skipIfWorker();
5+
6+
const { spawnSync } = require('node:child_process');
7+
const assert = require('node:assert');
8+
const fixtures = require('../common/fixtures');
9+
10+
const file = fixtures.path('test-nodetiming-uvmetricsinfo.js');
11+
12+
{
13+
const { status, stderr } = spawnSync(
14+
process.execPath,
15+
[
16+
file,
17+
],
18+
);
19+
assert.strictEqual(status, 0, stderr.toString());
20+
}

0 commit comments

Comments
 (0)
Please sign in to comment.