@@ -323,7 +323,7 @@ void SignalHandler::HandleProfilerSignal(int sig,
323
323
auto time_from = Now ();
324
324
old_handler (sig, info, context);
325
325
auto time_to = Now ();
326
- prof->PushContext (time_from, time_to, cpu_time);
326
+ prof->PushContext (time_from, time_to, cpu_time, isolate );
327
327
}
328
328
#else
329
329
class SignalHandler {
@@ -472,6 +472,14 @@ ContextsByNode WallProfiler::GetContextsByNode(CpuProfile* profile,
472
472
return contextsByNode;
473
473
}
474
474
475
+ void GCPrologueCallback (Isolate* isolate, GCType type, GCCallbackFlags flags, void * data) {
476
+ static_cast <WallProfiler*>(data)->OnGCStart ();
477
+ }
478
+
479
+ void GCEpilogueCallback (Isolate* isolate, GCType type, GCCallbackFlags flags, void * data) {
480
+ static_cast <WallProfiler*>(data)->OnGCEnd ();
481
+ }
482
+
475
483
WallProfiler::WallProfiler (std::chrono::microseconds samplingPeriod,
476
484
std::chrono::microseconds duration,
477
485
bool includeLines,
@@ -509,6 +517,11 @@ WallProfiler::WallProfiler(std::chrono::microseconds samplingPeriod,
509
517
#endif
510
518
jsArray_ = v8::Global<v8::Uint32Array>(isolate, jsArray);
511
519
std::fill (fields_, fields_ + kFieldCount , 0 );
520
+
521
+ cpedSymbol_ = v8::Global<v8::Symbol>(isolate, v8::Symbol::New (isolate));
522
+ gcCount = 0 ;
523
+ isolate->AddGCPrologueCallback (&GCPrologueCallback, this );
524
+ isolate->AddGCEpilogueCallback (&GCEpilogueCallback, this );
512
525
}
513
526
514
527
WallProfiler::~WallProfiler () {
@@ -522,6 +535,10 @@ void WallProfiler::Dispose(Isolate* isolate) {
522
535
523
536
g_profilers.RemoveProfiler (isolate, this );
524
537
}
538
+ if (isolate != nullptr ) {
539
+ isolate->RemoveGCPrologueCallback (&GCPrologueCallback, this );
540
+ isolate->RemoveGCEpilogueCallback (&GCEpilogueCallback, this );
541
+ }
525
542
}
526
543
527
544
NAN_METHOD (WallProfiler::New) {
@@ -953,25 +970,99 @@ v8::CpuProfiler* WallProfiler::CreateV8CpuProfiler() {
953
970
}
954
971
955
972
v8::Local<v8::Value> WallProfiler::GetContext (Isolate* isolate) {
956
- auto context = GetContextPtr ();
973
+ auto context = GetContextPtr (isolate );
957
974
if (!context) return v8::Undefined (isolate);
958
975
return context->Get (isolate);
959
976
}
960
977
978
+ struct PersistentContextPtr {
979
+ ContextPtr ptr1;
980
+ ContextPtr ptr2;
981
+ std::atomic<ContextPtr*> currentPtr;
982
+ Persistent<Object> per;
983
+
984
+ void Set (Isolate* isolate, Local<Value> value) {
985
+ auto newPtr = currentPtr.load (std::memory_order_relaxed) == &ptr1 ? &ptr2 : &ptr1;
986
+ if (!value->IsNullOrUndefined ()) {
987
+ *newPtr = std::make_shared<Global<Value>>(isolate, value);
988
+ } else {
989
+ newPtr->reset ();
990
+ }
991
+ std::atomic_signal_fence (std::memory_order_release);
992
+ currentPtr.store (newPtr, std::memory_order_relaxed);
993
+ }
994
+
995
+ ContextPtr Get () {
996
+ auto ptr = currentPtr.load (std::memory_order_relaxed);
997
+ std::atomic_signal_fence (std::memory_order_acquire);
998
+ return ptr ? *ptr : std::shared_ptr<Global<Value>>();
999
+ }
1000
+ };
1001
+
961
1002
void WallProfiler::SetContext (Isolate* isolate, Local<Value> value) {
962
- std::atomic_signal_fence (std::memory_order_release);
963
- std::atomic_store_explicit (
964
- &curContext_,
965
- value->IsNullOrUndefined ()
966
- ? std::shared_ptr<Global<Value>>()
967
- : std::make_shared<Global<Value>>(isolate, value),
968
- std::memory_order_relaxed);
1003
+ auto cped = isolate->GetContinuationPreservedEmbedderData ();
1004
+ // No Node AsyncContextFrame in this continuation yet
1005
+ if (!cped->IsObject ()) return ;
1006
+
1007
+ auto cpedObj = cped.As <Object>();
1008
+ auto localSymbol = cpedSymbol_.Get (isolate);
1009
+ auto v8Ctx = isolate->GetCurrentContext ();
1010
+ auto maybeProfData = cpedObj->Get (v8Ctx, localSymbol);
1011
+ if (maybeProfData.IsEmpty ()) return ;
1012
+ auto profData = maybeProfData.ToLocalChecked ();
1013
+
1014
+ PersistentContextPtr* contextPtr = nullptr ;
1015
+ if (profData->IsUndefined ()) {
1016
+ contextPtr = new PersistentContextPtr ();
1017
+
1018
+ auto maybeSetResult = cpedObj->Set (v8Ctx, localSymbol, External::New (isolate, contextPtr));
1019
+ if (maybeSetResult.IsNothing ()) {
1020
+ delete contextPtr;
1021
+ return ;
1022
+ }
1023
+
1024
+ // Register a callback to delete contextPtr when the CPED object is GCed
1025
+ contextPtr->per .Reset (isolate, cpedObj);
1026
+ contextPtr->per .SetWeak (contextPtr, [](const WeakCallbackInfo<PersistentContextPtr>& data) {
1027
+ auto &per = data.GetParameter ()->per ;
1028
+ if (!per.IsEmpty ()) {
1029
+ per.ClearWeak ();
1030
+ per.Reset ();
1031
+ }
1032
+ // Using SetSecondPassCallback as shared_ptr can trigger ~Global and any V8 API use needs to be in the second pass
1033
+ data.SetSecondPassCallback ([](const WeakCallbackInfo<PersistentContextPtr>& data) {
1034
+ delete data.GetParameter ();
1035
+ });
1036
+ }, WeakCallbackType::kParameter );
1037
+ } else {
1038
+ contextPtr = static_cast <PersistentContextPtr*>(profData.As <External>()->Value ());
1039
+ }
1040
+
1041
+ contextPtr->Set (isolate, value);
1042
+
969
1043
}
970
1044
971
- ContextPtr WallProfiler::GetContextPtr () {
972
- auto contextPtr = atomic_load_explicit (&curContext_, std::memory_order_relaxed);
973
- std::atomic_signal_fence (std::memory_order_acquire);
974
- return contextPtr;
1045
+ ContextPtr WallProfiler::GetContextPtrSignalSafe (Isolate* isolate) {
1046
+ if (gcCount == 0 ) {
1047
+ auto handleScope = HandleScope (isolate);
1048
+ return GetContextPtr (isolate);
1049
+ }
1050
+ return gcContext;
1051
+ }
1052
+
1053
+ ContextPtr WallProfiler::GetContextPtr (Isolate* isolate) {
1054
+ auto cped = isolate->GetContinuationPreservedEmbedderData (); // signal safe?
1055
+ if (!cped->IsObject ()) return std::shared_ptr<Global<Value>>();
1056
+
1057
+ auto cpedObj = cped.As <Object>();
1058
+ auto localSymbol = cpedSymbol_.Get (isolate); // signal safe?
1059
+ auto maybeProfData = cpedObj->Get (isolate->GetEnteredOrMicrotaskContext (), localSymbol); // signal safe?
1060
+ if (maybeProfData.IsEmpty ()) return std::shared_ptr<Global<Value>>();
1061
+ auto profData = maybeProfData.ToLocalChecked ();
1062
+
1063
+ if (profData->IsUndefined ()) return std::shared_ptr<Global<Value>>();
1064
+
1065
+ return static_cast <PersistentContextPtr*>(profData.As <External>()->Value ())->Get ();
975
1066
}
976
1067
977
1068
NAN_GETTER (WallProfiler::GetContext) {
@@ -1001,12 +1092,13 @@ NAN_METHOD(WallProfiler::Dispose) {
1001
1092
1002
1093
void WallProfiler::PushContext (int64_t time_from,
1003
1094
int64_t time_to,
1004
- int64_t cpu_time) {
1095
+ int64_t cpu_time,
1096
+ Isolate* isolate) {
1005
1097
// Be careful this is called in a signal handler context therefore all
1006
1098
// operations must be async signal safe (in particular no allocations).
1007
1099
// Our ring buffer avoids allocations.
1008
1100
if (contexts_.size () < contexts_.capacity ()) {
1009
- contexts_.push_back ({GetContextPtr ( ), time_from, time_to, cpu_time});
1101
+ contexts_.push_back ({GetContextPtrSignalSafe (isolate ), time_from, time_to, cpu_time});
1010
1102
std::atomic_fetch_add_explicit (
1011
1103
reinterpret_cast <std::atomic<uint32_t >*>(&fields_[kSampleCount ]),
1012
1104
1U ,
0 commit comments