Skip to content

Commit 57f766f

Browse files
authored
fix!: re-create CPU profiler each time a CPU profile is collected to work around V8 CPU profiler memory leak. (#142)
* work around V8 memory leak * address comments
1 parent 1b83cf4 commit 57f766f

File tree

1 file changed

+33
-2
lines changed

1 file changed

+33
-2
lines changed

bindings/profiler.cc

Lines changed: 33 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -96,8 +96,14 @@ NAN_METHOD(GetAllocationProfile) {
9696
}
9797

9898
// Time profiler
99-
100-
#if NODE_MODULE_VERSION > NODE_8_0_MODULE_VERSION
99+
#if NODE_MODULE_VERSION >= NODE_12_0_MODULE_VERSION
100+
// For Node 12 and Node 14, a new CPU profiler object will be created each
101+
// time profiling is started to work around
102+
// https://bugs.chromium.org/p/v8/issues/detail?id=11051.
103+
CpuProfiler* cpuProfiler;
104+
// Default sampling interval is 1000us.
105+
int samplingIntervalUS = 1000;
106+
#elif NODE_MODULE_VERSION > NODE_8_0_MODULE_VERSION
101107
// This profiler exists for the lifetime of the program. Not calling
102108
// CpuProfiler::Dispose() is intentional.
103109
CpuProfiler* cpuProfiler = CpuProfiler::New(v8::Isolate::GetCurrent());
@@ -264,6 +270,17 @@ NAN_METHOD(StartProfiling) {
264270
return Nan::ThrowTypeError("Second argument must be a boolean.");
265271
}
266272

273+
#if NODE_MODULE_VERSION >= NODE_12_0_MODULE_VERSION
274+
// Since the CPU profiler is created and destroyed each time a CPU
275+
// profile is collected, there cannot be multiple CPU profiling requests
276+
// inflight in parallel.
277+
if (cpuProfiler) {
278+
return Nan::ThrowError("CPU profiler is already started.");
279+
}
280+
cpuProfiler = CpuProfiler::New(v8::Isolate::GetCurrent());
281+
cpuProfiler->SetSamplingInterval(samplingIntervalUS);
282+
#endif
283+
267284
Local<String> name =
268285
Nan::MaybeLocal<String>(info[0].As<String>()).ToLocalChecked();
269286

@@ -289,6 +306,11 @@ NAN_METHOD(StartProfiling) {
289306
// Signature:
290307
// stopProfiling(runName: string, includeLineInfo: boolean): TimeProfile
291308
NAN_METHOD(StopProfiling) {
309+
#if NODE_MODULE_VERSION >= NODE_12_0_MODULE_VERSION
310+
if (!cpuProfiler) {
311+
return Nan::ThrowError("StopProfiling called without an active CPU profiler.");
312+
}
313+
#endif
292314
if (info.Length() != 2) {
293315
return Nan::ThrowTypeError("StopProfling must have two arguments.");
294316
}
@@ -307,6 +329,11 @@ NAN_METHOD(StopProfiling) {
307329
Local<Value> translated_profile =
308330
TranslateTimeProfile(profile, includeLineInfo);
309331
profile->Delete();
332+
#if NODE_MODULE_VERSION >= NODE_12_0_MODULE_VERSION
333+
// Dispose of CPU profiler to work around memory leak.
334+
cpuProfiler->Dispose();
335+
cpuProfiler = NULL;
336+
#endif
310337
info.GetReturnValue().Set(translated_profile);
311338
}
312339

@@ -318,7 +345,11 @@ NAN_METHOD(SetSamplingInterval) {
318345
#else
319346
int us = info[0].As<Integer>()->IntegerValue();
320347
#endif
348+
#if NODE_MODULE_VERSION >= NODE_12_0_MODULE_VERSION
349+
samplingIntervalUS = us;
350+
#else
321351
cpuProfiler->SetSamplingInterval(us);
352+
#endif
322353
}
323354

324355
NAN_MODULE_INIT(InitAll) {

0 commit comments

Comments
 (0)