Skip to content

Commit ff06537

Browse files
committed
v8: add cpu profile options
1 parent 8c3cf47 commit ff06537

File tree

13 files changed

+186
-26
lines changed

13 files changed

+186
-26
lines changed

doc/api/v8.md

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1771,21 +1771,25 @@ writeString('hello');
17711771
writeString('你好');
17721772
```
17731773
1774-
## `v8.startCpuProfile()`
1774+
## `v8.startCpuProfile([options])`
17751775
17761776
<!-- YAML
17771777
added:
17781778
- v25.0.0
17791779
- v24.12.0
17801780
-->
17811781
1782+
* `options` {Object}
1783+
* `sampleInterval` {number} Requested sampling interval in milliseconds. **Default:** `0`.
1784+
* `maxBufferSize` {integer} Maximum number of samples to keep before older
1785+
entries are discarded. **Default:** `4294967295`.
17821786
* Returns: {SyncCPUProfileHandle}
17831787
17841788
Starting a CPU profile then return a `SyncCPUProfileHandle` object.
17851789
This API supports `using` syntax.
17861790
17871791
```cjs
1788-
const handle = v8.startCpuProfile();
1792+
const handle = v8.startCpuProfile({ sampleInterval: 1, maxBufferSize: 10_000 });
17891793
const profile = handle.stop();
17901794
console.log(profile);
17911795
```

doc/api/worker_threads.md

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1962,12 +1962,16 @@ this matches its values.
19621962
19631963
If the worker has stopped, the return value is an empty object.
19641964
1965-
### `worker.startCpuProfile()`
1965+
### `worker.startCpuProfile([options])`
19661966
19671967
<!-- YAML
19681968
added: v24.8.0
19691969
-->
19701970
1971+
* `options` {Object}
1972+
* `sampleInterval` {number} Requested sampling interval in milliseconds. **Default:** `0`.
1973+
* `maxBufferSize` {integer} Maximum number of samples to retain.
1974+
**Default:** `4294967295`.
19711975
* Returns: {Promise}
19721976
19731977
Starting a CPU profile then return a Promise that fulfills with an error
@@ -1982,7 +1986,7 @@ const worker = new Worker(`
19821986
`, { eval: true });
19831987
19841988
worker.on('online', async () => {
1985-
const handle = await worker.startCpuProfile();
1989+
const handle = await worker.startCpuProfile({ sampleInterval: 1 });
19861990
const profile = await handle.stop();
19871991
console.log(profile);
19881992
worker.terminate();

lib/internal/v8/cpu_profiler.js

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
'use strict';
2+
3+
const {
4+
MathFloor,
5+
} = primordials;
6+
7+
const {
8+
validateNumber,
9+
validateObject,
10+
} = require('internal/validators');
11+
12+
const kMicrosPerMilli = 1_000;
13+
const kMaxSamplingIntervalUs = 0x7FFFFFFF;
14+
const kMaxSamplingIntervalMs = kMaxSamplingIntervalUs / kMicrosPerMilli;
15+
const kMaxSamplesUnlimited = 0xFFFF_FFFF;
16+
17+
function normalizeCpuProfileOptions(options = {}) {
18+
validateObject(options, 'options');
19+
20+
// TODO(ishabi): add support for 'mode' and 'filterContext' options
21+
const {
22+
sampleInterval,
23+
maxBufferSize,
24+
} = options;
25+
26+
let samplingIntervalMicros = 0;
27+
if (sampleInterval !== undefined) {
28+
validateNumber(sampleInterval,
29+
'options.sampleInterval',
30+
0,
31+
kMaxSamplingIntervalMs);
32+
samplingIntervalMicros = MathFloor(sampleInterval * kMicrosPerMilli);
33+
if (sampleInterval > 0 && samplingIntervalMicros === 0) {
34+
samplingIntervalMicros = 1;
35+
}
36+
}
37+
38+
const size = maxBufferSize;
39+
let normalizedMaxSamples = kMaxSamplesUnlimited;
40+
if (size !== undefined) {
41+
validateNumber(size,
42+
'options.maxBufferSize',
43+
1,
44+
kMaxSamplesUnlimited);
45+
normalizedMaxSamples = MathFloor(size);
46+
}
47+
48+
return {
49+
samplingIntervalMicros,
50+
maxSamples: normalizedMaxSamples,
51+
};
52+
}
53+
54+
module.exports = {
55+
normalizeCpuProfileOptions,
56+
};

lib/internal/worker.js

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,6 @@ const {
7171
validateObject,
7272
validateNumber,
7373
} = require('internal/validators');
74-
let normalizeHeapProfileOptions;
7574
const {
7675
throwIfBuildingSnapshot,
7776
} = require('internal/v8/startup_snapshot');
@@ -111,6 +110,8 @@ const dc = require('diagnostics_channel');
111110
const workerThreadsChannel = dc.channel('worker_threads');
112111

113112
let cwdCounter;
113+
let normalizeHeapProfileOptions;
114+
let normalizeCpuProfileOptions;
114115

115116
const environmentData = new SafeMap();
116117

@@ -574,9 +575,16 @@ class Worker extends EventEmitter {
574575
});
575576
}
576577

577-
// TODO(theanarkh): add options, such as sample_interval, CpuProfilingMode
578-
startCpuProfile() {
579-
const startTaker = this[kHandle]?.startCpuProfile();
578+
startCpuProfile(options) {
579+
normalizeCpuProfileOptions ??=
580+
require('internal/v8/cpu_profiler').normalizeCpuProfileOptions;
581+
const {
582+
samplingIntervalMicros,
583+
maxSamples,
584+
} = normalizeCpuProfileOptions(options);
585+
const startTaker = this[kHandle]?.startCpuProfile(
586+
samplingIntervalMicros,
587+
maxSamples);
580588
return new Promise((resolve, reject) => {
581589
if (!startTaker) return reject(new ERR_WORKER_NOT_RUNNING());
582590
startTaker.ondone = (err, id) => {

lib/v8.js

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,9 @@ const {
5353
const {
5454
normalizeHeapProfileOptions,
5555
} = require('internal/v8/heap_profile');
56+
const {
57+
normalizeCpuProfileOptions,
58+
} = require('internal/v8/cpu_profiler');
5659

5760
let profiler = {};
5861
if (internalBinding('config').hasInspector) {
@@ -214,10 +217,17 @@ class SyncHeapProfileHandle {
214217

215218
/**
216219
* Starting CPU Profile.
220+
* @param {object} [options]
221+
* @param {number} [options.sampleInterval]
222+
* @param {number} [options.maxBufferSize]
217223
* @returns {SyncCPUProfileHandle}
218224
*/
219-
function startCpuProfile() {
220-
const id = _startCpuProfile();
225+
function startCpuProfile(options) {
226+
const {
227+
samplingIntervalMicros,
228+
maxSamples,
229+
} = normalizeCpuProfileOptions(options);
230+
const id = _startCpuProfile(samplingIntervalMicros, maxSamples);
221231
return new SyncCPUProfileHandle(id);
222232
}
223233

src/env.cc

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2261,14 +2261,18 @@ void Environment::RunWeakRefCleanup() {
22612261
isolate()->ClearKeptObjects();
22622262
}
22632263

2264-
v8::CpuProfilingResult Environment::StartCpuProfile() {
2264+
v8::CpuProfilingResult Environment::StartCpuProfile(
2265+
const CpuProfileOptions& options) {
22652266
HandleScope handle_scope(isolate());
22662267
if (!cpu_profiler_) {
22672268
cpu_profiler_ = v8::CpuProfiler::New(isolate());
22682269
}
2269-
v8::CpuProfilingResult result = cpu_profiler_->Start(
2270-
v8::CpuProfilingOptions{v8::CpuProfilingMode::kLeafNodeLineNumbers,
2271-
v8::CpuProfilingOptions::kNoSampleLimit});
2270+
v8::CpuProfilingOptions start_options(
2271+
v8::CpuProfilingMode::kLeafNodeLineNumbers,
2272+
options.max_samples,
2273+
options.sampling_interval_us);
2274+
v8::CpuProfilingResult result =
2275+
cpu_profiler_->Start(std::move(start_options));
22722276
if (result.status == v8::CpuProfilingStatus::kStarted) {
22732277
pending_profiles_.push_back(result.id);
22742278
}

src/env.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1053,7 +1053,7 @@ class Environment final : public MemoryRetainer {
10531053

10541054
inline void RemoveHeapSnapshotNearHeapLimitCallback(size_t heap_limit);
10551055

1056-
v8::CpuProfilingResult StartCpuProfile();
1056+
v8::CpuProfilingResult StartCpuProfile(const CpuProfileOptions& options);
10571057
v8::CpuProfile* StopCpuProfile(v8::ProfilerId profile_id);
10581058

10591059
// Field identifiers for exit_info_

src/node_v8.cc

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -250,7 +250,8 @@ void SetFlagsFromString(const FunctionCallbackInfo<Value>& args) {
250250
void StartCpuProfile(const FunctionCallbackInfo<Value>& args) {
251251
Environment* env = Environment::GetCurrent(args);
252252
Isolate* isolate = env->isolate();
253-
CpuProfilingResult result = env->StartCpuProfile();
253+
CpuProfileOptions options = ParseCpuProfileOptions(args);
254+
CpuProfilingResult result = env->StartCpuProfile(options);
254255
if (result.status == CpuProfilingStatus::kErrorTooManyProfilers) {
255256
return THROW_ERR_CPU_PROFILE_TOO_MANY(isolate,
256257
"There are too many CPU profiles");

src/node_worker.cc

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -923,6 +923,7 @@ void Worker::StartCpuProfile(const FunctionCallbackInfo<Value>& args) {
923923
Worker* w;
924924
ASSIGN_OR_RETURN_UNWRAP(&w, args.This());
925925
Environment* env = w->env();
926+
CpuProfileOptions options = ParseCpuProfileOptions(args);
926927

927928
AsyncHooks::DefaultTriggerAsyncIdScope trigger_id_scope(w);
928929
Local<Object> wrap;
@@ -935,9 +936,9 @@ void Worker::StartCpuProfile(const FunctionCallbackInfo<Value>& args) {
935936
BaseObjectPtr<WorkerCpuProfileTaker> taker =
936937
MakeDetachedBaseObject<WorkerCpuProfileTaker>(env, wrap);
937938

938-
bool scheduled = w->RequestInterrupt([taker = std::move(taker),
939-
env](Environment* worker_env) mutable {
940-
CpuProfilingResult result = worker_env->StartCpuProfile();
939+
bool scheduled = w->RequestInterrupt([taker = std::move(taker), env, options](
940+
Environment* worker_env) mutable {
941+
CpuProfilingResult result = worker_env->StartCpuProfile(options);
941942
env->SetImmediateThreadsafe(
942943
[taker = std::move(taker), result = result](Environment* env) mutable {
943944
Isolate* isolate = env->isolate();

src/util.cc

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -894,4 +894,18 @@ HeapProfileOptions ParseHeapProfileOptions(
894894
return options;
895895
}
896896

897+
CpuProfileOptions ParseCpuProfileOptions(
898+
const v8::FunctionCallbackInfo<v8::Value>& args) {
899+
CpuProfileOptions options;
900+
CHECK_LE(args.Length(), 2);
901+
if (args.Length() > 0) {
902+
CHECK(args[0]->IsInt32());
903+
options.sampling_interval_us = args[0].As<v8::Int32>()->Value();
904+
}
905+
if (args.Length() > 1) {
906+
CHECK(args[1]->IsUint32());
907+
options.max_samples = args[1].As<v8::Uint32>()->Value();
908+
}
909+
return options;
910+
}
897911
} // namespace node

0 commit comments

Comments
 (0)