|
19 | 19 | #include <limits>
|
20 | 20 | #include <memory>
|
21 | 21 | #include <mutex>
|
| 22 | +#include <type_traits> |
22 | 23 | #include <vector>
|
23 | 24 |
|
24 | 25 | #include <nan.h>
|
@@ -120,6 +121,18 @@ class ProtectedProfilerMap {
|
120 | 121 | return profiler;
|
121 | 122 | }
|
122 | 123 |
|
| 124 | + WallProfiler* RemoveProfilerForIsolate(const v8::Isolate* isolate) { |
| 125 | + return UpdateProfilers([isolate](auto map) { |
| 126 | + auto it = map->find(isolate); |
| 127 | + if (it != map->end()) { |
| 128 | + auto profiler = it->second; |
| 129 | + map->erase(it); |
| 130 | + return profiler; |
| 131 | + } |
| 132 | + return static_cast<WallProfiler*>(nullptr); |
| 133 | + }); |
| 134 | + } |
| 135 | + |
123 | 136 | bool RemoveProfiler(const v8::Isolate* isolate, WallProfiler* profiler) {
|
124 | 137 | return UpdateProfilers([isolate, profiler, this](auto map) {
|
125 | 138 | terminatedWorkersCpu_ += profiler->GetAndResetThreadCpu();
|
@@ -177,8 +190,10 @@ class ProtectedProfilerMap {
|
177 | 190 | }
|
178 | 191 |
|
179 | 192 | private:
|
| 193 | + using ProfilerMap = std::unordered_map<const Isolate*, WallProfiler*>; |
| 194 | + |
180 | 195 | template <typename F>
|
181 |
| - bool UpdateProfilers(F updateFn) { |
| 196 | + std::invoke_result_t<F, ProfilerMap*> UpdateProfilers(F updateFn) { |
182 | 197 | // use mutex to prevent two isolates of updating profilers concurrently
|
183 | 198 | std::lock_guard<std::mutex> lock(update_mutex_);
|
184 | 199 |
|
@@ -207,7 +222,6 @@ class ProtectedProfilerMap {
|
207 | 222 | return res;
|
208 | 223 | }
|
209 | 224 |
|
210 |
| - using ProfilerMap = std::unordered_map<const Isolate*, WallProfiler*>; |
211 | 225 | mutable std::atomic<ProfilerMap*> profilers_;
|
212 | 226 | std::mutex update_mutex_;
|
213 | 227 | bool init_ = false;
|
@@ -366,6 +380,27 @@ static int64_t GetV8ToEpochOffset() {
|
366 | 380 | return V8toEpochOffset;
|
367 | 381 | }
|
368 | 382 |
|
| 383 | +void WallProfiler::CleanupHook(void* data) { |
| 384 | + auto isolate = static_cast<Isolate*>(data); |
| 385 | + auto prof = g_profilers.RemoveProfilerForIsolate(isolate); |
| 386 | + if (prof) { |
| 387 | + prof->Cleanup(isolate); |
| 388 | + delete prof; |
| 389 | + } |
| 390 | +} |
| 391 | + |
| 392 | +// This is only called when isolate is terminated without `beforeExit` |
| 393 | +// notification. |
| 394 | +void WallProfiler::Cleanup(Isolate* isolate) { |
| 395 | + if (started_) { |
| 396 | + cpuProfiler_->Stop(profileId_); |
| 397 | + if (interceptSignal()) { |
| 398 | + SignalHandler::DecreaseUseCount(); |
| 399 | + } |
| 400 | + Dispose(isolate, false); |
| 401 | + } |
| 402 | +} |
| 403 | + |
369 | 404 | ContextsByNode WallProfiler::GetContextsByNode(CpuProfile* profile,
|
370 | 405 | ContextBuffer& contexts,
|
371 | 406 | int64_t startCpuTime) {
|
@@ -547,21 +582,22 @@ WallProfiler::WallProfiler(std::chrono::microseconds samplingPeriod,
|
547 | 582 | }
|
548 | 583 | }
|
549 | 584 |
|
550 |
| -WallProfiler::~WallProfiler() { |
551 |
| - Dispose(nullptr); |
552 |
| -} |
553 |
| - |
554 |
| -void WallProfiler::Dispose(Isolate* isolate) { |
| 585 | +void WallProfiler::Dispose(Isolate* isolate, bool removeFromMap) { |
555 | 586 | if (cpuProfiler_ != nullptr) {
|
556 | 587 | cpuProfiler_->Dispose();
|
557 | 588 | cpuProfiler_ = nullptr;
|
558 | 589 |
|
559 |
| - g_profilers.RemoveProfiler(isolate, this); |
| 590 | + if (removeFromMap) { |
| 591 | + g_profilers.RemoveProfiler(isolate, this); |
| 592 | + } |
560 | 593 |
|
561 |
| - if (isolate != nullptr && collectAsyncId_) { |
| 594 | + if (collectAsyncId_) { |
562 | 595 | isolate->RemoveGCPrologueCallback(&GCPrologueCallback, this);
|
563 | 596 | isolate->RemoveGCEpilogueCallback(&GCEpilogueCallback, this);
|
564 | 597 | }
|
| 598 | + |
| 599 | + node::RemoveEnvironmentCleanupHook( |
| 600 | + isolate, &WallProfiler::CleanupHook, isolate); |
565 | 601 | }
|
566 | 602 | }
|
567 | 603 |
|
@@ -702,17 +738,19 @@ Result WallProfiler::StartImpl() {
|
702 | 738 | : CollectionMode::kNoCollect);
|
703 | 739 | collectionMode_.store(collectionMode, std::memory_order_relaxed);
|
704 | 740 | started_ = true;
|
| 741 | + auto isolate = Isolate::GetCurrent(); |
| 742 | + node::AddEnvironmentCleanupHook(isolate, &WallProfiler::CleanupHook, isolate); |
705 | 743 | return {};
|
706 | 744 | }
|
707 | 745 |
|
708 |
| -std::string WallProfiler::StartInternal() { |
| 746 | +v8::ProfilerId WallProfiler::StartInternal() { |
709 | 747 | // Reuse the same names for the profiles because strings used for profile
|
710 | 748 | // names are not released until v8::CpuProfiler object is destroyed.
|
711 | 749 | // https://github.com/nodejs/node/blob/b53c51995380b1f8d642297d848cab6010d2909c/deps/v8/src/profiler/profile-generator.h#L516
|
712 | 750 | char buf[128];
|
713 | 751 | snprintf(buf, sizeof(buf), "pprof-%" PRId64, (profileIdx_++) % 2);
|
714 | 752 | v8::Local<v8::String> title = Nan::New<String>(buf).ToLocalChecked();
|
715 |
| - cpuProfiler_->StartProfiling( |
| 753 | + auto result = cpuProfiler_->Start( |
716 | 754 | title,
|
717 | 755 | includeLines_ ? CpuProfilingMode::kCallerLineNumbers
|
718 | 756 | : CpuProfilingMode::kLeafNodeLineNumbers,
|
@@ -752,7 +790,7 @@ std::string WallProfiler::StartInternal() {
|
752 | 790 | cpuProfiler_->CollectSample(v8::Isolate::GetCurrent());
|
753 | 791 | }
|
754 | 792 |
|
755 |
| - return buf; |
| 793 | + return result.id; |
756 | 794 | }
|
757 | 795 |
|
758 | 796 | NAN_METHOD(WallProfiler::Stop) {
|
@@ -837,12 +875,11 @@ Result WallProfiler::StopImpl(bool restart, v8::Local<v8::Value>& profile) {
|
837 | 875 | std::atomic_signal_fence(std::memory_order_acquire);
|
838 | 876 | }
|
839 | 877 |
|
840 |
| - if (withContexts_ || workaroundV8Bug_) { |
| 878 | + if (interceptSignal()) { |
841 | 879 | SignalHandler::DecreaseUseCount();
|
842 | 880 | }
|
843 | 881 |
|
844 |
| - auto v8_profile = cpuProfiler_->StopProfiling( |
845 |
| - Nan::New<String>(oldProfileId).ToLocalChecked()); |
| 882 | + auto v8_profile = cpuProfiler_->Stop(oldProfileId); |
846 | 883 |
|
847 | 884 | ContextBuffer contexts;
|
848 | 885 | if (withContexts_) {
|
@@ -896,7 +933,7 @@ Result WallProfiler::StopImpl(bool restart, v8::Local<v8::Value>& profile) {
|
896 | 933 | v8_profile->Delete();
|
897 | 934 |
|
898 | 935 | if (!restart) {
|
899 |
| - Dispose(v8::Isolate::GetCurrent()); |
| 936 | + Dispose(v8::Isolate::GetCurrent(), true); |
900 | 937 | } else if (workaroundV8Bug_) {
|
901 | 938 | waitForSignal(callCount + 1);
|
902 | 939 | collectionMode_.store(withContexts_ ? CollectionMode::kCollectContexts
|
@@ -1017,6 +1054,10 @@ NAN_METHOD(WallProfiler::V8ProfilerStuckEventLoopDetected) {
|
1017 | 1054 |
|
1018 | 1055 | NAN_METHOD(WallProfiler::Dispose) {
|
1019 | 1056 | auto profiler = Nan::ObjectWrap::Unwrap<WallProfiler>(info.This());
|
| 1057 | + // Profiler must already be stopped when this is called. |
| 1058 | + if (profiler->started_) { |
| 1059 | + return Nan::ThrowTypeError("Profiler is still running, stop it first."); |
| 1060 | + } |
1020 | 1061 | delete profiler;
|
1021 | 1062 | }
|
1022 | 1063 |
|
|
0 commit comments