v8: add cpu profile

PR-URL: https://github.com/nodejs/node/pull/59807
Reviewed-By: Juan José Arboleda <soyjuanarbol@gmail.com>
Reviewed-By: James M Snell <jasnell@gmail.com>
This commit is contained in:
theanarkh 2025-10-12 01:20:57 +08:00 committed by GitHub
parent a1244f04de
commit f9fcc746f3
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 132 additions and 0 deletions

View File

@ -1398,6 +1398,30 @@ setTimeout(() => {
}, 1000);
```
## Class: `SyncCPUProfileHandle`
<!-- YAML
added: REPLACEME
-->
### `syncCpuProfileHandle.stop()`
<!-- YAML
added: REPLACEME
-->
* Returns: {string}
Stopping collecting the profile and return the profile data.
### `syncCpuProfileHandle[Symbol.dispose]()`
<!-- YAML
added: REPLACEME
-->
Stopping collecting the profile and the profile will be discarded.
## Class: `CPUProfileHandle`
<!-- YAML
@ -1493,6 +1517,23 @@ writeString('hello');
writeString('你好');
```
## `v8.startCpuProfile()`
<!-- YAML
added: REPLACEME
-->
* Returns: {SyncCPUProfileHandle}
Starting a CPU profile then return a `SyncCPUProfileHandle` object.
This API supports `using` syntax.
```cjs
const handle = v8.startCpuProfile();
const profile = handle.stop();
console.log(profile);
```
[CppHeap]: https://v8docs.nodesource.com/node-22.4/d9/dc4/classv8_1_1_cpp_heap.html
[HTML structured clone algorithm]: https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Structured_clone_algorithm
[Hook Callbacks]: #hook-callbacks

View File

@ -27,6 +27,7 @@ const {
Int8Array,
JSONParse,
ObjectPrototypeToString,
SymbolDispose,
Uint16Array,
Uint32Array,
Uint8Array,
@ -112,6 +113,8 @@ const binding = internalBinding('v8');
const {
cachedDataVersionTag,
setFlagsFromString: _setFlagsFromString,
startCpuProfile: _startCpuProfile,
stopCpuProfile: _stopCpuProfile,
isStringOneByteRepresentation: _isStringOneByteRepresentation,
updateHeapStatisticsBuffer,
updateHeapSpaceStatisticsBuffer,
@ -166,6 +169,36 @@ function setFlagsFromString(flags) {
_setFlagsFromString(flags);
}
class SyncCPUProfileHandle {
#id = null;
#stopped = false;
constructor(id) {
this.#id = id;
}
stop() {
if (this.#stopped) {
return;
}
this.#stopped = true;
return _stopCpuProfile(this.#id);
};
[SymbolDispose]() {
this.stop();
}
}
/**
* Starting CPU Profile.
* @returns {SyncCPUProfileHandle}
*/
function startCpuProfile() {
const id = _startCpuProfile();
return new SyncCPUProfileHandle(id);
}
/**
* Return whether this string uses one byte as underlying representation or not.
* @param {string} content
@ -478,4 +511,5 @@ module.exports = {
setHeapSnapshotNearHeapLimit,
GCProfiler,
isStringOneByteRepresentation,
startCpuProfile,
};

View File

@ -27,6 +27,7 @@
#include "node.h"
#include "node_external_reference.h"
#include "util-inl.h"
#include "v8-profiler.h"
#include "v8.h"
namespace node {
@ -35,6 +36,9 @@ using v8::Array;
using v8::BigInt;
using v8::CFunction;
using v8::Context;
using v8::CpuProfile;
using v8::CpuProfilingResult;
using v8::CpuProfilingStatus;
using v8::DictionaryTemplate;
using v8::FunctionCallbackInfo;
using v8::FunctionTemplate;
@ -47,6 +51,7 @@ using v8::Isolate;
using v8::Local;
using v8::LocalVector;
using v8::MaybeLocal;
using v8::Number;
using v8::Object;
using v8::ScriptCompiler;
using v8::String;
@ -241,6 +246,39 @@ void SetFlagsFromString(const FunctionCallbackInfo<Value>& args) {
V8::SetFlagsFromString(*flags, static_cast<size_t>(flags.length()));
}
void StartCpuProfile(const FunctionCallbackInfo<Value>& args) {
Environment* env = Environment::GetCurrent(args);
Isolate* isolate = env->isolate();
CpuProfilingResult result = env->StartCpuProfile();
if (result.status == CpuProfilingStatus::kErrorTooManyProfilers) {
return THROW_ERR_CPU_PROFILE_TOO_MANY(isolate,
"There are too many CPU profiles");
} else if (result.status == CpuProfilingStatus::kStarted) {
args.GetReturnValue().Set(Number::New(isolate, result.id));
}
}
void StopCpuProfile(const FunctionCallbackInfo<Value>& args) {
Environment* env = Environment::GetCurrent(args);
Isolate* isolate = env->isolate();
CHECK(args[0]->IsUint32());
uint32_t profile_id = args[0]->Uint32Value(env->context()).FromJust();
CpuProfile* profile = env->StopCpuProfile(profile_id);
if (!profile) {
return THROW_ERR_CPU_PROFILE_NOT_STARTED(isolate,
"CPU profile not started");
}
auto json_out_stream = std::make_unique<node::JSONOutputStream>();
profile->Serialize(json_out_stream.get(),
CpuProfile::SerializationFormat::kJSON);
profile->Delete();
Local<Value> ret;
if (ToV8Value(env->context(), json_out_stream->out_stream().str(), isolate)
.ToLocal(&ret)) {
args.GetReturnValue().Set(ret);
}
}
static void IsStringOneByteRepresentation(
const FunctionCallbackInfo<Value>& args) {
CHECK_EQ(args.Length(), 1);
@ -699,6 +737,9 @@ void Initialize(Local<Object> target,
// Export symbols used by v8.setFlagsFromString()
SetMethod(context, target, "setFlagsFromString", SetFlagsFromString);
SetMethod(context, target, "startCpuProfile", StartCpuProfile);
SetMethod(context, target, "stopCpuProfile", StopCpuProfile);
// Export symbols used by v8.isStringOneByteRepresentation()
SetFastMethodNoSideEffect(context,
target,
@ -743,6 +784,8 @@ void RegisterExternalReferences(ExternalReferenceRegistry* registry) {
registry->Register(GetCppHeapStatistics);
registry->Register(IsStringOneByteRepresentation);
registry->Register(fast_is_string_one_byte_representation_);
registry->Register(StartCpuProfile);
registry->Register(StopCpuProfile);
}
} // namespace v8_utils

View File

@ -7,6 +7,7 @@
#include "aliased_buffer.h"
#include "base_object.h"
#include "json_utils.h"
#include "node_errors.h"
#include "node_snapshotable.h"
#include "util.h"
#include "v8.h"

View File

@ -0,0 +1,12 @@
'use strict';
require('../common');
const assert = require('assert');
const v8 = require('v8');
const handle = v8.startCpuProfile();
const profile = handle.stop();
assert.ok(typeof profile === 'string');
assert.ok(profile.length > 0);
// Call stop() again
assert.ok(handle.stop() === undefined);

View File

@ -352,6 +352,7 @@ const customTypesMap = {
'LockManagerSnapshot': 'https://developer.mozilla.org/en-US/docs/Web/API/LockManagerSnapshot',
'CPUProfileHandle': 'v8.html#class-cpuprofilehandle',
'HeapProfileHandle': 'v8.html#class-heapprofilehandle',
'SyncCPUProfileHandle': 'v8.html#class-synccpuprofilehandle',
};
const arrayPart = /(?:\[])+$/;