Skip to content

Commit f4618c5

Browse files
committed
v8: add cpu profile APIs
1 parent 961554c commit f4618c5

File tree

4 files changed

+87
-1
lines changed

4 files changed

+87
-1
lines changed

lib/v8.js

Lines changed: 29 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,31 @@ function setFlagsFromString(flags) {
166169
_setFlagsFromString(flags);
167170
}
168171

172+
class CPUProfileHandle {
173+
#id = null;
174+
175+
constructor(id) {
176+
this.#id = id;
177+
}
178+
179+
stop() {
180+
return _stopCpuProfile(this.#id);
181+
};
182+
183+
[SymbolDispose]() {
184+
this.stop();
185+
}
186+
}
187+
188+
/**
189+
* Starting CPU Profile.
190+
* @returns {CPUProfileHandle}
191+
*/
192+
function startCpuProfile() {
193+
const id = _startCpuProfile();
194+
return new CPUProfileHandle(id);
195+
}
196+
169197
/**
170198
* Return whether this string uses one byte as underlying representation or not.
171199
* @param {string} content
@@ -478,4 +506,5 @@ module.exports = {
478506
setHeapSnapshotNearHeapLimit,
479507
GCProfiler,
480508
isStringOneByteRepresentation,
509+
startCpuProfile,
481510
};

src/node_v8.cc

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,13 +28,18 @@
2828
#include "node_external_reference.h"
2929
#include "util-inl.h"
3030
#include "v8.h"
31+
#include "v8-profiler.h"
3132

3233
namespace node {
3334
namespace v8_utils {
3435
using v8::Array;
3536
using v8::BigInt;
3637
using v8::CFunction;
3738
using v8::Context;
39+
using v8::CpuProfile;
40+
using v8::CpuProfiler;
41+
using v8::CpuProfilingResult;
42+
using v8::CpuProfilingStatus;
3843
using v8::FunctionCallbackInfo;
3944
using v8::FunctionTemplate;
4045
using v8::HandleScope;
@@ -47,6 +52,8 @@ using v8::Local;
4752
using v8::LocalVector;
4853
using v8::MaybeLocal;
4954
using v8::Name;
55+
using v8::NewStringType;
56+
using v8::Number;
5057
using v8::Object;
5158
using v8::ScriptCompiler;
5259
using v8::String;
@@ -243,6 +250,36 @@ void SetFlagsFromString(const FunctionCallbackInfo<Value>& args) {
243250
V8::SetFlagsFromString(*flags, static_cast<size_t>(flags.length()));
244251
}
245252

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

722+
SetMethod(context, target, "startCpuProfile", StartCpuProfile);
723+
SetMethod(context, target, "stopCpuProfile", StopCpuProfile);
724+
685725
// Export symbols used by v8.isStringOneByteRepresentation()
686726
SetFastMethodNoSideEffect(context,
687727
target,
@@ -726,6 +766,8 @@ void RegisterExternalReferences(ExternalReferenceRegistry* registry) {
726766
registry->Register(GetCppHeapStatistics);
727767
registry->Register(IsStringOneByteRepresentation);
728768
registry->Register(fast_is_string_one_byte_representation_);
769+
registry->Register(StartCpuProfile);
770+
registry->Register(StopCpuProfile);
729771
}
730772

731773
} // namespace v8_utils

src/node_v8.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
#include "node_snapshotable.h"
1111
#include "util.h"
1212
#include "v8.h"
13-
13+
#include "node_errors.h"
1414
namespace node {
1515
class Environment;
1616
struct InternalFieldInfoBase;
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
'use strict';
2+
require('../common');
3+
const assert = require('assert');
4+
const v8 = require('v8');
5+
6+
{
7+
const handle = v8.startCpuProfile();
8+
JSON.parse( handle.stop());
9+
assert.throws(() => handle.stop(), { code: "ERR_CPU_PROFILE_NOT_STARTED" });
10+
}
11+
12+
{
13+
// It will be stopped automatically when process exits
14+
v8.startCpuProfile();
15+
}

0 commit comments

Comments
 (0)