Skip to content
9 changes: 6 additions & 3 deletions doc/api/cli.md
Original file line number Diff line number Diff line change
Expand Up @@ -540,13 +540,16 @@ $ ls *.cpuprofile
CPU.20190409.202950.15293.0.0.cpuprofile
```

If `--cpu-prof-name` is specified, the provided value will be used as-is; patterns such as
`${hhmmss}` or `${pid}` are not supported.
If `--cpu-prof-name` is specified, the provided value is used as a template
for the file name. The following placeholder is supported and will be
substituted at runtime:

* `${pid}` — the current process ID

```console
$ node --cpu-prof --cpu-prof-name 'CPU.${pid}.cpuprofile' index.js
$ ls *.cpuprofile
'CPU.${pid}.cpuprofile'
CPU.15293.cpuprofile
```

### `--cpu-prof-dir`
Expand Down
26 changes: 25 additions & 1 deletion src/inspector_profiler.cc
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
#include "node_file.h"
#include "node_internals.h"
#include "util-inl.h"
#include "uv.h"
#include "v8-inspector.h"

#include <cinttypes>
Expand Down Expand Up @@ -465,6 +466,27 @@ static void EndStartedProfilers(Environment* env) {
}
}

static std::string ReplacePlaceholders(const std::string& pattern) {
std::string result = pattern;

static const std::unordered_map<std::string, std::function<std::string()>>
kPlaceholderMap = {
{"${pid}", []() { return std::to_string(uv_os_getpid()); }},
// TODO(haramj): Add more placeholders as needed.
};

for (const auto& [placeholder, getter] : kPlaceholderMap) {
size_t pos = 0;
while ((pos = result.find(placeholder, pos)) != std::string::npos) {
const std::string value = getter();
result.replace(pos, placeholder.length(), value);
pos += value.length();
}
}

return result;
}

void StartProfilers(Environment* env) {
AtExit(env, [](void* env) {
EndStartedProfilers(static_cast<Environment*>(env));
Expand All @@ -486,7 +508,9 @@ void StartProfilers(Environment* env) {
DiagnosticFilename filename(env, "CPU", "cpuprofile");
env->set_cpu_prof_name(*filename);
} else {
env->set_cpu_prof_name(env->options()->cpu_prof_name);
std::string resolved_name =
ReplacePlaceholders(env->options()->cpu_prof_name);
env->set_cpu_prof_name(resolved_name);
}
CHECK_NULL(env->cpu_profiler_connection());
env->set_cpu_profiler_connection(
Expand Down
35 changes: 35 additions & 0 deletions test/sequential/test-cpu-prof-name.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ const fixtures = require('../common/fixtures');
common.skipIfInspectorDisabled();

const assert = require('assert');
const fs = require('fs');
const path = require('path');
const { spawnSync } = require('child_process');

const tmpdir = require('../common/tmpdir');
Expand Down Expand Up @@ -41,3 +43,36 @@ const {
assert.deepStrictEqual(profiles, [file]);
verifyFrames(output, file, 'fibonacci.js');
}

// --cpu-prof-name with ${pid} placeholder
{
tmpdir.refresh();
// eslint-disable-next-line no-template-curly-in-string
const profName = 'CPU.${pid}.cpuprofile';
const dir = tmpdir.path;

const output = spawnSync(process.execPath, [
'--cpu-prof',
'--cpu-prof-interval',
kCpuProfInterval,
'--cpu-prof-name',
profName,
fixtures.path('workload', 'fibonacci.js'),
], {
cwd: dir,
env
});

if (output.status !== 0) {
console.error(output.stderr.toString());
}

assert.strictEqual(output.status, 0);

const expectedFile = path.join(dir, `CPU.${output.pid}.cpuprofile`);
assert.ok(fs.existsSync(expectedFile), `Expected file ${expectedFile} not found.`);

verifyFrames(output, expectedFile, 'fibonacci.js');

fs.unlinkSync(expectedFile);
}
Loading