Skip to content

Commit 10b2275

Browse files
committed
v8: add cpu profile
1 parent 24ded11 commit 10b2275

File tree

6 files changed

+132
-0
lines changed

6 files changed

+132
-0
lines changed

doc/api/v8.md

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1398,6 +1398,30 @@ setTimeout(() => {
13981398
}, 1000);
13991399
```
14001400

1401+
## Class: `SyncCPUProfileHandle`
1402+
1403+
<!-- YAML
1404+
added: REPLACEME
1405+
-->
1406+
1407+
### `syncCpuProfileHandle.stop()`
1408+
1409+
<!-- YAML
1410+
added: REPLACEME
1411+
-->
1412+
1413+
* Returns: {string}
1414+
1415+
Stopping collecting the profile and return the profile data.
1416+
1417+
### `syncCpuProfileHandle[Symbol.dispose]()`
1418+
1419+
<!-- YAML
1420+
added: REPLACEME
1421+
-->
1422+
1423+
Stopping collecting the profile and the profile will be discarded.
1424+
14011425
## Class: `CPUProfileHandle`
14021426

14031427
<!-- YAML
@@ -1493,6 +1517,23 @@ writeString('hello');
14931517
writeString('你好');
14941518
```
14951519

1520+
## `v8.startCpuProfile()`
1521+
1522+
<!-- YAML
1523+
added: REPLACEME
1524+
-->
1525+
1526+
* Returns: {SyncCPUProfileHandle}
1527+
1528+
Starting a CPU profile then return a `SyncCPUProfileHandle` object.
1529+
This API supports `using` syntax.
1530+
1531+
```cjs
1532+
const handle = v8.startCpuProfile();
1533+
const profile = handle.stop();
1534+
console.log(profile);
1535+
```
1536+
14961537
[CppHeap]: https://v8docs.nodesource.com/node-22.4/d9/dc4/classv8_1_1_cpp_heap.html
14971538
[HTML structured clone algorithm]: https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Structured_clone_algorithm
14981539
[Hook Callbacks]: #hook-callbacks

lib/v8.js

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ const {
2727
Int8Array,
2828
JSONParse,
2929
ObjectPrototypeToString,
30+
SymbolDispose,
3031
Uint16Array,
3132
Uint32Array,
3233
Uint8Array,
@@ -112,6 +113,8 @@ const binding = internalBinding('v8');
112113
const {
113114
cachedDataVersionTag,
114115
setFlagsFromString: _setFlagsFromString,
116+
startCpuProfile: _startCpuProfile,
117+
stopCpuProfile: _stopCpuProfile,
115118
isStringOneByteRepresentation: _isStringOneByteRepresentation,
116119
updateHeapStatisticsBuffer,
117120
updateHeapSpaceStatisticsBuffer,
@@ -166,6 +169,36 @@ function setFlagsFromString(flags) {
166169
_setFlagsFromString(flags);
167170
}
168171

172+
class SyncCPUProfileHandle {
173+
#id = null;
174+
#stopped = false;
175+
176+
constructor(id) {
177+
this.#id = id;
178+
}
179+
180+
stop() {
181+
if (this.#stopped) {
182+
return;
183+
}
184+
this.#stopped = true;
185+
return _stopCpuProfile(this.#id);
186+
};
187+
188+
[SymbolDispose]() {
189+
this.stop();
190+
}
191+
}
192+
193+
/**
194+
* Starting CPU Profile.
195+
* @returns {SyncCPUProfileHandle}
196+
*/
197+
function startCpuProfile() {
198+
const id = _startCpuProfile();
199+
return new SyncCPUProfileHandle(id);
200+
}
201+
169202
/**
170203
* Return whether this string uses one byte as underlying representation or not.
171204
* @param {string} content
@@ -478,4 +511,5 @@ module.exports = {
478511
setHeapSnapshotNearHeapLimit,
479512
GCProfiler,
480513
isStringOneByteRepresentation,
514+
startCpuProfile,
481515
};

src/node_v8.cc

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
#include "node.h"
2828
#include "node_external_reference.h"
2929
#include "util-inl.h"
30+
#include "v8-profiler.h"
3031
#include "v8.h"
3132

3233
namespace node {
@@ -35,6 +36,9 @@ using v8::Array;
3536
using v8::BigInt;
3637
using v8::CFunction;
3738
using v8::Context;
39+
using v8::CpuProfile;
40+
using v8::CpuProfilingResult;
41+
using v8::CpuProfilingStatus;
3842
using v8::DictionaryTemplate;
3943
using v8::FunctionCallbackInfo;
4044
using v8::FunctionTemplate;
@@ -47,6 +51,7 @@ using v8::Isolate;
4751
using v8::Local;
4852
using v8::LocalVector;
4953
using v8::MaybeLocal;
54+
using v8::Number;
5055
using v8::Object;
5156
using v8::ScriptCompiler;
5257
using v8::String;
@@ -241,6 +246,39 @@ void SetFlagsFromString(const FunctionCallbackInfo<Value>& args) {
241246
V8::SetFlagsFromString(*flags, static_cast<size_t>(flags.length()));
242247
}
243248

249+
void StartCpuProfile(const FunctionCallbackInfo<Value>& args) {
250+
Environment* env = Environment::GetCurrent(args);
251+
Isolate* isolate = env->isolate();
252+
CpuProfilingResult result = env->StartCpuProfile();
253+
if (result.status == CpuProfilingStatus::kErrorTooManyProfilers) {
254+
return THROW_ERR_CPU_PROFILE_TOO_MANY(isolate,
255+
"There are too many CPU profiles");
256+
} else if (result.status == CpuProfilingStatus::kStarted) {
257+
args.GetReturnValue().Set(Number::New(isolate, result.id));
258+
}
259+
}
260+
261+
void StopCpuProfile(const FunctionCallbackInfo<Value>& args) {
262+
Environment* env = Environment::GetCurrent(args);
263+
Isolate* isolate = env->isolate();
264+
CHECK(args[0]->IsUint32());
265+
uint32_t profile_id = args[0]->Uint32Value(env->context()).FromJust();
266+
CpuProfile* profile = env->StopCpuProfile(profile_id);
267+
if (!profile) {
268+
return THROW_ERR_CPU_PROFILE_NOT_STARTED(isolate,
269+
"CPU profile not started");
270+
}
271+
auto json_out_stream = std::make_unique<node::JSONOutputStream>();
272+
profile->Serialize(json_out_stream.get(),
273+
CpuProfile::SerializationFormat::kJSON);
274+
profile->Delete();
275+
Local<Value> ret;
276+
if (ToV8Value(env->context(), json_out_stream->out_stream().str(), isolate)
277+
.ToLocal(&ret)) {
278+
args.GetReturnValue().Set(ret);
279+
}
280+
}
281+
244282
static void IsStringOneByteRepresentation(
245283
const FunctionCallbackInfo<Value>& args) {
246284
CHECK_EQ(args.Length(), 1);
@@ -699,6 +737,9 @@ void Initialize(Local<Object> target,
699737
// Export symbols used by v8.setFlagsFromString()
700738
SetMethod(context, target, "setFlagsFromString", SetFlagsFromString);
701739

740+
SetMethod(context, target, "startCpuProfile", StartCpuProfile);
741+
SetMethod(context, target, "stopCpuProfile", StopCpuProfile);
742+
702743
// Export symbols used by v8.isStringOneByteRepresentation()
703744
SetFastMethodNoSideEffect(context,
704745
target,
@@ -743,6 +784,8 @@ void RegisterExternalReferences(ExternalReferenceRegistry* registry) {
743784
registry->Register(GetCppHeapStatistics);
744785
registry->Register(IsStringOneByteRepresentation);
745786
registry->Register(fast_is_string_one_byte_representation_);
787+
registry->Register(StartCpuProfile);
788+
registry->Register(StopCpuProfile);
746789
}
747790

748791
} // namespace v8_utils

src/node_v8.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
#include "aliased_buffer.h"
88
#include "base_object.h"
99
#include "json_utils.h"
10+
#include "node_errors.h"
1011
#include "node_snapshotable.h"
1112
#include "util.h"
1213
#include "v8.h"
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
'use strict';
2+
3+
require('../common');
4+
const assert = require('assert');
5+
const v8 = require('v8');
6+
7+
const handle = v8.startCpuProfile();
8+
const profile = handle.stop();
9+
assert.ok(typeof profile === 'string');
10+
assert.ok(profile.length > 0);
11+
// Call stop() again
12+
assert.ok(handle.stop() === undefined);

tools/doc/type-parser.mjs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -352,6 +352,7 @@ const customTypesMap = {
352352
'LockManagerSnapshot': 'https://developer.mozilla.org/en-US/docs/Web/API/LockManagerSnapshot',
353353
'CPUProfileHandle': 'v8.html#class-cpuprofilehandle',
354354
'HeapProfileHandle': 'v8.html#class-heapprofilehandle',
355+
'SyncCPUProfileHandle': 'v8.html#class-synccpuprofilehandle',
355356
};
356357

357358
const arrayPart = /(?:\[])+$/;

0 commit comments

Comments
 (0)