diff --git a/doc/api/cli.md b/doc/api/cli.md index 74863327c73746..f85d4c2d14557b 100644 --- a/doc/api/cli.md +++ b/doc/api/cli.md @@ -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. Some placeholders are 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` diff --git a/src/inspector_profiler.cc b/src/inspector_profiler.cc index f09dd04ccd7f6e..dd57c779595c80 100644 --- a/src/inspector_profiler.cc +++ b/src/inspector_profiler.cc @@ -8,6 +8,7 @@ #include "node_file.h" #include "node_internals.h" #include "util-inl.h" +#include "uv.h" #include "v8-inspector.h" #include @@ -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> + 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(env)); @@ -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( diff --git a/test/sequential/test-cpu-prof-name.js b/test/sequential/test-cpu-prof-name.js index 3f1c6945c5436f..f40e639e92f624 100644 --- a/test/sequential/test-cpu-prof-name.js +++ b/test/sequential/test-cpu-prof-name.js @@ -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'); @@ -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); +}