Skip to content

Commit 9b41a4c

Browse files
committed
Fix profiler shutdown when isolate is terminated
When an isolate is terminated abruptly (eg. by Worker.terminate()), the profiler is not stopped and not removed from the isolate->profiler map. This can lead to a crash. This commit adds a cleanup hook with `node::AddEnvironmentCleanupHook` that is called when the isolate is destroyed. The cleanup hook stops the profiler and removes it from the profiler map.
1 parent bfa2637 commit 9b41a4c

File tree

2 files changed

+38
-8
lines changed

2 files changed

+38
-8
lines changed

bindings/profilers/wall.cc

Lines changed: 32 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -366,6 +366,27 @@ static int64_t GetV8ToEpochOffset() {
366366
return V8toEpochOffset;
367367
}
368368

369+
void WallProfiler::CleanupHook(void* data) {
370+
auto isolate = static_cast<Isolate*>(data);
371+
auto prof = g_profilers.GetProfiler(isolate);
372+
if (prof) {
373+
prof->Cleanup(isolate);
374+
delete prof;
375+
}
376+
}
377+
378+
// This is only called when isolate is terminated without `beforeExit`
379+
// notification.
380+
void WallProfiler::Cleanup(Isolate* isolate) {
381+
if (started_) {
382+
cpuProfiler_->Stop(profileId_);
383+
if (interceptSignal()) {
384+
SignalHandler::DecreaseUseCount();
385+
}
386+
Dispose(isolate);
387+
}
388+
}
389+
369390
ContextsByNode WallProfiler::GetContextsByNode(CpuProfile* profile,
370391
ContextBuffer& contexts,
371392
int64_t startCpuTime) {
@@ -562,6 +583,9 @@ void WallProfiler::Dispose(Isolate* isolate) {
562583
isolate->RemoveGCPrologueCallback(&GCPrologueCallback, this);
563584
isolate->RemoveGCEpilogueCallback(&GCEpilogueCallback, this);
564585
}
586+
587+
node::RemoveEnvironmentCleanupHook(
588+
isolate, &WallProfiler::CleanupHook, isolate);
565589
}
566590
}
567591

@@ -702,17 +726,19 @@ Result WallProfiler::StartImpl() {
702726
: CollectionMode::kNoCollect);
703727
collectionMode_.store(collectionMode, std::memory_order_relaxed);
704728
started_ = true;
729+
auto isolate = Isolate::GetCurrent();
730+
node::AddEnvironmentCleanupHook(isolate, &WallProfiler::CleanupHook, isolate);
705731
return {};
706732
}
707733

708-
std::string WallProfiler::StartInternal() {
734+
v8::ProfilerId WallProfiler::StartInternal() {
709735
// Reuse the same names for the profiles because strings used for profile
710736
// names are not released until v8::CpuProfiler object is destroyed.
711737
// https://github.com/nodejs/node/blob/b53c51995380b1f8d642297d848cab6010d2909c/deps/v8/src/profiler/profile-generator.h#L516
712738
char buf[128];
713739
snprintf(buf, sizeof(buf), "pprof-%" PRId64, (profileIdx_++) % 2);
714740
v8::Local<v8::String> title = Nan::New<String>(buf).ToLocalChecked();
715-
cpuProfiler_->StartProfiling(
741+
auto result = cpuProfiler_->Start(
716742
title,
717743
includeLines_ ? CpuProfilingMode::kCallerLineNumbers
718744
: CpuProfilingMode::kLeafNodeLineNumbers,
@@ -752,7 +778,7 @@ std::string WallProfiler::StartInternal() {
752778
cpuProfiler_->CollectSample(v8::Isolate::GetCurrent());
753779
}
754780

755-
return buf;
781+
return result.id;
756782
}
757783

758784
NAN_METHOD(WallProfiler::Stop) {
@@ -837,12 +863,11 @@ Result WallProfiler::StopImpl(bool restart, v8::Local<v8::Value>& profile) {
837863
std::atomic_signal_fence(std::memory_order_acquire);
838864
}
839865

840-
if (withContexts_ || workaroundV8Bug_) {
866+
if (interceptSignal()) {
841867
SignalHandler::DecreaseUseCount();
842868
}
843869

844-
auto v8_profile = cpuProfiler_->StopProfiling(
845-
Nan::New<String>(oldProfileId).ToLocalChecked());
870+
auto v8_profile = cpuProfiler_->Stop(oldProfileId);
846871

847872
ContextBuffer contexts;
848873
if (withContexts_) {
@@ -1016,6 +1041,7 @@ NAN_METHOD(WallProfiler::V8ProfilerStuckEventLoopDetected) {
10161041
}
10171042

10181043
NAN_METHOD(WallProfiler::Dispose) {
1044+
// Profiler should already be stopped when this is called.
10191045
auto profiler = Nan::ObjectWrap::Unwrap<WallProfiler>(info.This());
10201046
delete profiler;
10211047
}

bindings/profilers/wall.hh

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ class WallProfiler : public Nan::ObjectWrap {
6262

6363
std::atomic<CollectionMode> collectionMode_;
6464
std::atomic<uint64_t> noCollectCallCount_;
65-
std::string profileId_;
65+
v8::ProfilerId profileId_;
6666
uint64_t profileIdx_ = 0;
6767
bool includeLines_ = false;
6868
bool withContexts_ = false;
@@ -104,6 +104,8 @@ class WallProfiler : public Nan::ObjectWrap {
104104
int64_t startCpuTime);
105105

106106
bool waitForSignal(uint64_t targetCallCount = 0);
107+
static void CleanupHook(void* data);
108+
void Cleanup(v8::Isolate* isolate);
107109

108110
public:
109111
/**
@@ -129,7 +131,7 @@ class WallProfiler : public Nan::ObjectWrap {
129131
int64_t cpu_time,
130132
double async_id);
131133
Result StartImpl();
132-
std::string StartInternal();
134+
v8::ProfilerId StartInternal();
133135
Result StopImpl(bool restart, v8::Local<v8::Value>& profile);
134136

135137
CollectionMode collectionMode() {
@@ -143,6 +145,8 @@ class WallProfiler : public Nan::ObjectWrap {
143145

144146
bool collectCpuTime() const { return collectCpuTime_; }
145147

148+
bool interceptSignal() const { return withContexts_ || workaroundV8Bug_; }
149+
146150
int v8ProfilerStuckEventLoopDetected() const {
147151
return v8ProfilerStuckEventLoopDetected_;
148152
}

0 commit comments

Comments
 (0)