@@ -291,6 +291,9 @@ void SignalHandler::HandleProfilerSignal(int sig,
291
291
return ;
292
292
}
293
293
auto isolate = Isolate::GetCurrent ();
294
+ if (!isolate || isolate->IsDead ()) {
295
+ return ;
296
+ }
294
297
WallProfiler* prof = g_profilers.GetProfiler (isolate);
295
298
296
299
if (!prof) {
@@ -315,9 +318,7 @@ void SignalHandler::HandleProfilerSignal(int sig,
315
318
auto time_from = Now ();
316
319
old_handler (sig, info, context);
317
320
auto time_to = Now ();
318
- int64_t async_id = -1 ;
319
- // don't capture for now until we work out the issues with GC and thread start
320
- // static_cast<int64_t>(node::AsyncHooksGetExecutionAsyncId(isolate));
321
+ auto async_id = prof->GetAsyncId (isolate);
321
322
prof->PushContext (time_from, time_to, cpu_time, async_id);
322
323
}
323
324
#else
@@ -473,11 +474,13 @@ std::shared_ptr<ContextsByNode> WallProfiler::GetContextsByNode(
473
474
sampleContext.context .get ()->Get (isolate))
474
475
.Check ();
475
476
}
476
- timedContext
477
- ->Set (v8Context,
478
- asyncIdKey,
479
- NewNumberFromInt64 (isolate, sampleContext.async_id ))
480
- .Check ();
477
+ if (collectAsyncId_) {
478
+ timedContext
479
+ ->Set (v8Context,
480
+ asyncIdKey,
481
+ NewNumberFromInt64 (isolate, sampleContext.async_id ))
482
+ .Check ();
483
+ }
481
484
}
482
485
}
483
486
array->Set (v8Context, array->Length (), timedContext).Check ();
@@ -492,12 +495,27 @@ std::shared_ptr<ContextsByNode> WallProfiler::GetContextsByNode(
492
495
return contextsByNode;
493
496
}
494
497
498
+ void GCPrologueCallback (Isolate* isolate,
499
+ GCType type,
500
+ GCCallbackFlags flags,
501
+ void * data) {
502
+ static_cast <WallProfiler*>(data)->OnGCStart (isolate);
503
+ }
504
+
505
+ void GCEpilogueCallback (Isolate* isolate,
506
+ GCType type,
507
+ GCCallbackFlags flags,
508
+ void * data) {
509
+ static_cast <WallProfiler*>(data)->OnGCEnd ();
510
+ }
511
+
495
512
WallProfiler::WallProfiler (std::chrono::microseconds samplingPeriod,
496
513
std::chrono::microseconds duration,
497
514
bool includeLines,
498
515
bool withContexts,
499
516
bool workaroundV8Bug,
500
517
bool collectCpuTime,
518
+ bool collectAsyncId,
501
519
bool isMainThread)
502
520
: samplingPeriod_(samplingPeriod),
503
521
includeLines_ (includeLines),
@@ -509,14 +527,17 @@ WallProfiler::WallProfiler(std::chrono::microseconds samplingPeriod,
509
527
// event just after triggers the issue.
510
528
workaroundV8Bug_ = workaroundV8Bug && DD_WALL_USE_SIGPROF && detectV8Bug_;
511
529
collectCpuTime_ = collectCpuTime && withContexts;
530
+ collectAsyncId_ = collectAsyncId && withContexts;
512
531
513
532
if (withContexts_) {
514
533
contexts_.reserve (duration * 2 / samplingPeriod);
515
534
}
516
535
517
536
curContext_.store (&context1_, std::memory_order_relaxed);
518
537
collectionMode_.store (CollectionMode::kNoCollect , std::memory_order_relaxed);
538
+ gcCount.store (0 , std::memory_order_relaxed);
519
539
540
+ // TODO: bind to this isolate? Would fix the Dispose(nullptr) issue.
520
541
auto isolate = v8::Isolate::GetCurrent ();
521
542
v8::Local<v8::ArrayBuffer> buffer =
522
543
v8::ArrayBuffer::New (isolate, sizeof (uint32_t ) * kFieldCount );
@@ -526,6 +547,11 @@ WallProfiler::WallProfiler(std::chrono::microseconds samplingPeriod,
526
547
fields_ = static_cast <uint32_t *>(buffer->GetBackingStore ()->Data ());
527
548
jsArray_ = v8::Global<v8::Uint32Array>(isolate, jsArray);
528
549
std::fill (fields_, fields_ + kFieldCount , 0 );
550
+
551
+ if (collectAsyncId_) {
552
+ isolate->AddGCPrologueCallback (&GCPrologueCallback, this );
553
+ isolate->AddGCEpilogueCallback (&GCEpilogueCallback, this );
554
+ }
529
555
}
530
556
531
557
WallProfiler::~WallProfiler () {
@@ -538,9 +564,22 @@ void WallProfiler::Dispose(Isolate* isolate) {
538
564
cpuProfiler_ = nullptr ;
539
565
540
566
g_profilers.RemoveProfiler (isolate, this );
567
+
568
+ if (isolate != nullptr && collectAsyncId_) {
569
+ isolate->RemoveGCPrologueCallback (&GCPrologueCallback, this );
570
+ isolate->RemoveGCEpilogueCallback (&GCEpilogueCallback, this );
571
+ }
541
572
}
542
573
}
543
574
575
+ #define DD_WALL_PROFILER_GET_BOOLEAN_CONFIG (name ) \
576
+ auto name##Value = \
577
+ Nan::Get (arg, Nan::New<v8::String>(#name).ToLocalChecked()); \
578
+ if (name##Value.IsEmpty() || !name##Value.ToLocalChecked()->IsBoolean ()) { \
579
+ return Nan::ThrowTypeError (#name " must be a boolean." ); \
580
+ } \
581
+ bool name = name##Value.ToLocalChecked().As<v8::Boolean>()->Value ();
582
+
544
583
NAN_METHOD (WallProfiler::New) {
545
584
if (info.Length () != 1 || !info[0 ]->IsObject ()) {
546
585
return Nan::ThrowTypeError (" WallProfiler must have one object argument." );
@@ -579,50 +618,12 @@ NAN_METHOD(WallProfiler::New) {
579
618
return Nan::ThrowTypeError (" Duration must not be less than sample rate." );
580
619
}
581
620
582
- auto lineNumbersValue =
583
- Nan::Get (arg, Nan::New<v8::String>(" lineNumbers" ).ToLocalChecked ());
584
- if (lineNumbersValue.IsEmpty () ||
585
- !lineNumbersValue.ToLocalChecked ()->IsBoolean ()) {
586
- return Nan::ThrowTypeError (" lineNumbers must be a boolean." );
587
- }
588
- bool lineNumbers =
589
- lineNumbersValue.ToLocalChecked ().As <v8::Boolean>()->Value ();
590
-
591
- auto withContextsValue =
592
- Nan::Get (arg, Nan::New<v8::String>(" withContexts" ).ToLocalChecked ());
593
- if (withContextsValue.IsEmpty () ||
594
- !withContextsValue.ToLocalChecked ()->IsBoolean ()) {
595
- return Nan::ThrowTypeError (" withContext must be a boolean." );
596
- }
597
- bool withContexts =
598
- withContextsValue.ToLocalChecked ().As <v8::Boolean>()->Value ();
599
-
600
- auto workaroundV8BugValue =
601
- Nan::Get (arg, Nan::New<v8::String>(" workaroundV8Bug" ).ToLocalChecked ());
602
- if (workaroundV8BugValue.IsEmpty () ||
603
- !workaroundV8BugValue.ToLocalChecked ()->IsBoolean ()) {
604
- return Nan::ThrowTypeError (" workaroundV8Bug must be a boolean." );
605
- }
606
- bool workaroundV8Bug =
607
- workaroundV8BugValue.ToLocalChecked ().As <v8::Boolean>()->Value ();
608
-
609
- auto collectCpuTimeValue =
610
- Nan::Get (arg, Nan::New<v8::String>(" collectCpuTime" ).ToLocalChecked ());
611
- if (collectCpuTimeValue.IsEmpty () ||
612
- !collectCpuTimeValue.ToLocalChecked ()->IsBoolean ()) {
613
- return Nan::ThrowTypeError (" collectCpuTime must be a boolean." );
614
- }
615
- bool collectCpuTime =
616
- collectCpuTimeValue.ToLocalChecked ().As <v8::Boolean>()->Value ();
617
-
618
- auto isMainThreadValue =
619
- Nan::Get (arg, Nan::New<v8::String>(" isMainThread" ).ToLocalChecked ());
620
- if (isMainThreadValue.IsEmpty () ||
621
- !isMainThreadValue.ToLocalChecked ()->IsBoolean ()) {
622
- return Nan::ThrowTypeError (" isMainThread must be a boolean." );
623
- }
624
- bool isMainThread =
625
- isMainThreadValue.ToLocalChecked ().As <v8::Boolean>()->Value ();
621
+ DD_WALL_PROFILER_GET_BOOLEAN_CONFIG (lineNumbers);
622
+ DD_WALL_PROFILER_GET_BOOLEAN_CONFIG (withContexts);
623
+ DD_WALL_PROFILER_GET_BOOLEAN_CONFIG (workaroundV8Bug);
624
+ DD_WALL_PROFILER_GET_BOOLEAN_CONFIG (collectCpuTime);
625
+ DD_WALL_PROFILER_GET_BOOLEAN_CONFIG (collectAsyncId);
626
+ DD_WALL_PROFILER_GET_BOOLEAN_CONFIG (isMainThread);
626
627
627
628
if (withContexts && !DD_WALL_USE_SIGPROF) {
628
629
return Nan::ThrowTypeError (" Contexts are not supported." );
@@ -632,6 +633,10 @@ NAN_METHOD(WallProfiler::New) {
632
633
return Nan::ThrowTypeError (" Cpu time collection requires contexts." );
633
634
}
634
635
636
+ if (collectAsyncId && !withContexts) {
637
+ return Nan::ThrowTypeError (" Async ID collection requires contexts." );
638
+ }
639
+
635
640
if (lineNumbers && withContexts) {
636
641
// Currently custom contexts are not compatible with caller line
637
642
// information, because it's not possible to associate context with line
@@ -657,6 +662,7 @@ NAN_METHOD(WallProfiler::New) {
657
662
withContexts,
658
663
workaroundV8Bug,
659
664
collectCpuTime,
665
+ collectAsyncId,
660
666
isMainThread);
661
667
obj->Wrap (info.This ());
662
668
info.GetReturnValue ().Set (info.This ());
@@ -668,6 +674,8 @@ NAN_METHOD(WallProfiler::New) {
668
674
}
669
675
}
670
676
677
+ #undef DD_WALL_PROFILER_GET_BOOLEAN_CONFIG
678
+
671
679
NAN_METHOD (WallProfiler::Start) {
672
680
WallProfiler* wallProfiler =
673
681
Nan::ObjectWrap::Unwrap<WallProfiler>(info.Holder ());
@@ -1019,6 +1027,45 @@ NAN_METHOD(WallProfiler::Dispose) {
1019
1027
delete profiler;
1020
1028
}
1021
1029
1030
+ int64_t GetAsyncIdNoGC (v8::Isolate* isolate) {
1031
+ return isolate->InContext ()
1032
+ ? static_cast <int64_t >(
1033
+ node::AsyncHooksGetExecutionAsyncId (isolate))
1034
+ : -1 ;
1035
+ }
1036
+
1037
+ int64_t WallProfiler::GetAsyncId (v8::Isolate* isolate) {
1038
+ if (!collectAsyncId_) {
1039
+ return -1 ;
1040
+ }
1041
+ auto curGcCount = gcCount.load (std::memory_order_relaxed);
1042
+ std::atomic_signal_fence (std::memory_order_acquire);
1043
+ if (curGcCount > 0 ) {
1044
+ return gcAsyncId;
1045
+ }
1046
+ return GetAsyncIdNoGC (isolate);
1047
+ }
1048
+
1049
+ void WallProfiler::OnGCStart (v8::Isolate* isolate) {
1050
+ auto curCount = gcCount.load (std::memory_order_relaxed);
1051
+ std::atomic_signal_fence (std::memory_order_acquire);
1052
+ if (curCount == 0 ) {
1053
+ gcAsyncId = GetAsyncIdNoGC (isolate);
1054
+ }
1055
+ gcCount.store (curCount + 1 , std::memory_order_relaxed);
1056
+ std::atomic_signal_fence (std::memory_order_release);
1057
+ }
1058
+
1059
+ void WallProfiler::OnGCEnd () {
1060
+ auto newCount = gcCount.load (std::memory_order_relaxed) - 1 ;
1061
+ std::atomic_signal_fence (std::memory_order_acquire);
1062
+ gcCount.store (newCount, std::memory_order_relaxed);
1063
+ std::atomic_signal_fence (std::memory_order_release);
1064
+ if (newCount == 0 ) {
1065
+ gcAsyncId = -1 ;
1066
+ }
1067
+ }
1068
+
1022
1069
void WallProfiler::PushContext (int64_t time_from,
1023
1070
int64_t time_to,
1024
1071
int64_t cpu_time,
0 commit comments