Skip to content

Commit de337bb

Browse files
joyeecheungtargos
authored andcommitted
inspector: implement --cpu-prof-interval
This patch implements --cpu-prof-interval to specify the sampling interval of the CPU profiler started by --cpu-prof from the command line. Also adjust the interval to 100 in test-cpu-prof.js to make the test less flaky - it would fail if the time taken to finish the workload is smaller than the sampling interval, which was more likely on powerful machines when the interval was 1000. PR-URL: #27535 Reviewed-By: Jan Krems <[email protected]> Reviewed-By: Anna Henningsen <[email protected]> Reviewed-By: Franziska Hinkelmann <[email protected]> Reviewed-By: Rich Trott <[email protected]>
1 parent 294d2ea commit de337bb

File tree

9 files changed

+113
-3
lines changed

9 files changed

+113
-3
lines changed

doc/api/cli.md

+10
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,16 @@ added: v12.0.0
107107
Specify the directory where the CPU profiles generated by `--cpu-prof` will
108108
be placed.
109109

110+
### `--cpu-prof-interval`
111+
<!-- YAML
112+
added: REPLACEME
113+
-->
114+
115+
> Stability: 1 - Experimental
116+
117+
Specify the sampling interval in microseconds for the CPU profiles generated
118+
by `--cpu-prof`. The default is 1000 microseconds.
119+
110120
### `--cpu-prof-name`
111121
<!-- YAML
112122
added: v12.0.0

doc/node.1

+6
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,12 @@ The directory where the CPU profiles generated by
9090
.Fl -cpu-prof
9191
will be placed.
9292
.
93+
.It Fl -cpu-prof-interval
94+
The sampling interval in microseconds for the CPU profiles generated by
95+
.Fl -cpu-prof .
96+
The default is
97+
.Sy 1000 .
98+
.
9399
.It Fl -cpu-prof-name
94100
File name of the V8 CPU profile generated with
95101
.Fl -cpu-prof

src/env-inl.h

+8
Original file line numberDiff line numberDiff line change
@@ -673,6 +673,14 @@ Environment::cpu_profiler_connection() {
673673
return cpu_profiler_connection_.get();
674674
}
675675

676+
inline void Environment::set_cpu_prof_interval(uint64_t interval) {
677+
cpu_prof_interval_ = interval;
678+
}
679+
680+
inline uint64_t Environment::cpu_prof_interval() const {
681+
return cpu_prof_interval_;
682+
}
683+
676684
inline void Environment::set_cpu_prof_name(const std::string& name) {
677685
cpu_prof_name_ = name;
678686
}

src/env.h

+4
Original file line numberDiff line numberDiff line change
@@ -1143,6 +1143,9 @@ class Environment : public MemoryRetainer {
11431143
inline void set_cpu_prof_name(const std::string& name);
11441144
inline const std::string& cpu_prof_name() const;
11451145

1146+
inline void set_cpu_prof_interval(uint64_t interval);
1147+
inline uint64_t cpu_prof_interval() const;
1148+
11461149
inline void set_cpu_prof_dir(const std::string& dir);
11471150
inline const std::string& cpu_prof_dir() const;
11481151
#endif // HAVE_INSPECTOR
@@ -1183,6 +1186,7 @@ class Environment : public MemoryRetainer {
11831186
std::string coverage_directory_;
11841187
std::string cpu_prof_dir_;
11851188
std::string cpu_prof_name_;
1189+
uint64_t cpu_prof_interval_;
11861190
#endif // HAVE_INSPECTOR
11871191

11881192
std::shared_ptr<EnvironmentOptions> options_;

src/inspector_profiler.cc

+5-1
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@ using v8::Object;
2121
using v8::String;
2222
using v8::Value;
2323

24-
using v8_inspector::StringBuffer;
2524
using v8_inspector::StringView;
2625

2726
#ifdef _WIN32
@@ -254,6 +253,10 @@ MaybeLocal<Object> V8CpuProfilerConnection::GetProfile(Local<Object> result) {
254253
void V8CpuProfilerConnection::Start() {
255254
DispatchMessage("Profiler.enable");
256255
DispatchMessage("Profiler.start");
256+
std::string params = R"({ "interval": )";
257+
params += std::to_string(env()->cpu_prof_interval());
258+
params += " }";
259+
DispatchMessage("Profiler.setSamplingInterval", params.c_str());
257260
}
258261

259262
void V8CpuProfilerConnection::End() {
@@ -304,6 +307,7 @@ void StartProfilers(Environment* env) {
304307
}
305308
if (env->options()->cpu_prof) {
306309
const std::string& dir = env->options()->cpu_prof_dir;
310+
env->set_cpu_prof_interval(env->options()->cpu_prof_interval);
307311
env->set_cpu_prof_dir(dir.empty() ? GetCwd() : dir);
308312
if (env->options()->cpu_prof_name.empty()) {
309313
DiagnosticFilename filename(env, "CPU", "cpuprofile");

src/node_options.cc

+9
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,11 @@ void EnvironmentOptions::CheckOptions(std::vector<std::string>* errors) {
161161
if (!cpu_prof_dir.empty()) {
162162
errors->push_back("--cpu-prof-dir must be used with --cpu-prof");
163163
}
164+
// We can't catch the case where the value passed is the default value,
165+
// then the option just becomes a noop which is fine.
166+
if (cpu_prof_interval != kDefaultCpuProfInterval) {
167+
errors->push_back("--cpu-prof-interval must be used with --cpu-prof");
168+
}
164169
}
165170

166171
debug_options_.CheckOptions(errors);
@@ -356,6 +361,10 @@ EnvironmentOptionsParser::EnvironmentOptionsParser() {
356361
"specified file name of the V8 CPU profile generated with "
357362
"--cpu-prof",
358363
&EnvironmentOptions::cpu_prof_name);
364+
AddOption("--cpu-prof-interval",
365+
"specified sampling interval in microseconds for the V8 CPU "
366+
"profile generated with --cpu-prof. (default: 1000)",
367+
&EnvironmentOptions::cpu_prof_interval);
359368
AddOption("--cpu-prof-dir",
360369
"Directory where the V8 profiles generated by --cpu-prof will be "
361370
"placed. Does not affect --prof.",

src/node_options.h

+2
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,8 @@ class EnvironmentOptions : public Options {
111111
bool prof_process = false;
112112
#if HAVE_INSPECTOR
113113
std::string cpu_prof_dir;
114+
static const uint64_t kDefaultCpuProfInterval = 1000;
115+
uint64_t cpu_prof_interval = kDefaultCpuProfInterval;
114116
std::string cpu_prof_name;
115117
bool cpu_prof = false;
116118
#endif // HAVE_INSPECTOR

test/fixtures/workload/fibonacci-worker-argv.js

+5-1
Original file line numberDiff line numberDiff line change
@@ -3,5 +3,9 @@
33
const { Worker } = require('worker_threads');
44
const path = require('path');
55
new Worker(path.join(__dirname, 'fibonacci.js'), {
6-
execArgv: ['--cpu-prof']
6+
execArgv: [
7+
'--cpu-prof',
8+
'--cpu-prof-interval',
9+
process.env.CPU_PROF_INTERVAL || '100'
10+
]
711
});

test/sequential/test-cpu-prof.js

+64-1
Original file line numberDiff line numberDiff line change
@@ -50,18 +50,43 @@ if (common.isWindows) {
5050
FIB = 40;
5151
}
5252

53+
// We need to set --cpu-interval to a smaller value to make sure we can
54+
// find our workload in the samples. 50us should be a small enough sampling
55+
// interval for this.
56+
const kCpuProfInterval = 50;
5357
const env = {
5458
...process.env,
5559
FIB,
5660
NODE_DEBUG_NATIVE: 'INSPECTOR_PROFILER'
5761
};
5862

63+
// Test --cpu-prof without --cpu-prof-interval. Here we just verify that
64+
// we manage to generate a profile.
65+
{
66+
tmpdir.refresh();
67+
const output = spawnSync(process.execPath, [
68+
'--cpu-prof',
69+
fixtures.path('workload', 'fibonacci.js'),
70+
], {
71+
cwd: tmpdir.path,
72+
env
73+
});
74+
if (output.status !== 0) {
75+
console.log(output.stderr.toString());
76+
}
77+
assert.strictEqual(output.status, 0);
78+
const profiles = getCpuProfiles(tmpdir.path);
79+
assert.strictEqual(profiles.length, 1);
80+
}
81+
5982
// Outputs CPU profile when event loop is drained.
6083
// TODO(joyeecheung): share the fixutres with v8 coverage tests
6184
{
6285
tmpdir.refresh();
6386
const output = spawnSync(process.execPath, [
6487
'--cpu-prof',
88+
'--cpu-prof-interval',
89+
kCpuProfInterval,
6590
fixtures.path('workload', 'fibonacci.js'),
6691
], {
6792
cwd: tmpdir.path,
@@ -81,6 +106,8 @@ const env = {
81106
tmpdir.refresh();
82107
const output = spawnSync(process.execPath, [
83108
'--cpu-prof',
109+
'--cpu-prof-interval',
110+
kCpuProfInterval,
84111
fixtures.path('workload', 'fibonacci-exit.js'),
85112
], {
86113
cwd: tmpdir.path,
@@ -100,6 +127,8 @@ const env = {
100127
tmpdir.refresh();
101128
const output = spawnSync(process.execPath, [
102129
'--cpu-prof',
130+
'--cpu-prof-interval',
131+
kCpuProfInterval,
103132
fixtures.path('workload', 'fibonacci-sigint.js'),
104133
], {
105134
cwd: tmpdir.path,
@@ -123,7 +152,10 @@ const env = {
123152
fixtures.path('workload', 'fibonacci-worker-argv.js'),
124153
], {
125154
cwd: tmpdir.path,
126-
env
155+
env: {
156+
...process.env,
157+
CPU_PROF_INTERVAL: kCpuProfInterval
158+
}
127159
});
128160
if (output.status !== 0) {
129161
console.log(output.stderr.toString());
@@ -176,12 +208,35 @@ const env = {
176208
`${process.execPath}: --cpu-prof-dir must be used with --cpu-prof`);
177209
}
178210

211+
// --cpu-prof-interval without --cpu-prof
212+
{
213+
tmpdir.refresh();
214+
const output = spawnSync(process.execPath, [
215+
'--cpu-prof-interval',
216+
kCpuProfInterval,
217+
fixtures.path('workload', 'fibonacci.js'),
218+
], {
219+
cwd: tmpdir.path,
220+
env
221+
});
222+
const stderr = output.stderr.toString().trim();
223+
if (output.status !== 9) {
224+
console.log(stderr);
225+
}
226+
assert.strictEqual(output.status, 9);
227+
assert.strictEqual(
228+
stderr,
229+
`${process.execPath}: --cpu-prof-interval must be used with --cpu-prof`);
230+
}
231+
179232
// --cpu-prof-name
180233
{
181234
tmpdir.refresh();
182235
const file = path.join(tmpdir.path, 'test.cpuprofile');
183236
const output = spawnSync(process.execPath, [
184237
'--cpu-prof',
238+
'--cpu-prof-interval',
239+
kCpuProfInterval,
185240
'--cpu-prof-name',
186241
'test.cpuprofile',
187242
fixtures.path('workload', 'fibonacci.js'),
@@ -203,6 +258,8 @@ const env = {
203258
tmpdir.refresh();
204259
const output = spawnSync(process.execPath, [
205260
'--cpu-prof',
261+
'--cpu-prof-interval',
262+
kCpuProfInterval,
206263
'--cpu-prof-dir',
207264
'prof',
208265
fixtures.path('workload', 'fibonacci.js'),
@@ -227,6 +284,8 @@ const env = {
227284
const dir = path.join(tmpdir.path, 'prof');
228285
const output = spawnSync(process.execPath, [
229286
'--cpu-prof',
287+
'--cpu-prof-interval',
288+
kCpuProfInterval,
230289
'--cpu-prof-dir',
231290
dir,
232291
fixtures.path('workload', 'fibonacci.js'),
@@ -251,6 +310,8 @@ const env = {
251310
const file = path.join(dir, 'test.cpuprofile');
252311
const output = spawnSync(process.execPath, [
253312
'--cpu-prof',
313+
'--cpu-prof-interval',
314+
kCpuProfInterval,
254315
'--cpu-prof-name',
255316
'test.cpuprofile',
256317
'--cpu-prof-dir',
@@ -274,6 +335,8 @@ const env = {
274335
{
275336
tmpdir.refresh();
276337
const output = spawnSync(process.execPath, [
338+
'--cpu-prof-interval',
339+
kCpuProfInterval,
277340
'--cpu-prof-dir',
278341
'prof',
279342
'--cpu-prof',

0 commit comments

Comments
 (0)