@@ -61,19 +61,17 @@ static void sighandler(int sig, siginfo_t* info, void* context) {
6161 }
6262
6363 auto isolate = Isolate::GetCurrent ();
64- WallProfiler* prof = nullptr ;
6564 auto prof_map = profilers.exchange (nullptr , std::memory_order_acq_rel);
6665 if (prof_map) {
66+ WallProfiler* prof = nullptr ;
6767 auto prof_it = prof_map->find (isolate);
6868 if (prof_it != prof_map->end ()) {
6969 prof = prof_it->second ;
7070 }
7171 profilers.store (prof_map, std::memory_order_release);
72- }
73- auto now = v8::base::TimeTicks::Now ();
74- old_handler (sig, info, context);
75- if (prof) {
76- prof->PushContext (now);
72+ if (prof) {
73+ prof->DoSignalHandler (sig, info, context);
74+ }
7775 }
7876}
7977#endif
@@ -301,7 +299,8 @@ bool isIdleSample(const CpuProfileNode* sample) {
301299 strncmp (" (idle)" , sample->GetFunctionNameStr (), 7 ) == 0 ;
302300}
303301
304- LabelSetsByNode WallProfiler::GetLabelSetsByNode (CpuProfile* profile) {
302+ LabelSetsByNode WallProfiler::GetLabelSetsByNode (
303+ CpuProfile* profile, RingBuffer<SampleContext>& contexts) {
305304 LabelSetsByNode labelSetsByNode;
306305
307306 auto sampleCount = profile->GetSamplesCount ();
@@ -356,12 +355,21 @@ LabelSetsByNode WallProfiler::GetLabelSetsByNode(CpuProfile* profile) {
356355 }
357356 }
358357 }
358+ while (!contexts.empty ()) {
359+ contexts.pop_front ();
360+ }
359361 return labelSetsByNode;
360362}
361363
362- WallProfiler::WallProfiler (int intervalMicros, int durationMicros)
364+ WallProfiler::WallProfiler (int intervalMicros,
365+ int durationMicros,
366+ bool includeLines_,
367+ bool withLabels_)
363368 : samplingInterval(intervalMicros),
364- contexts(durationMicros * 2 / intervalMicros) {}
369+ includeLines(includeLines_),
370+ withLabels(withLabels_),
371+ session1(durationMicros * 2 / intervalMicros),
372+ session2(durationMicros * 2 / intervalMicros) {}
365373
366374WallProfiler::~WallProfiler () {
367375 Dispose (nullptr );
@@ -414,16 +422,9 @@ void WallProfiler::Dispose(Isolate* isolate) {
414422 }
415423}
416424
417- NAN_METHOD (WallProfiler::Dispose) {
418- WallProfiler* wallProfiler =
419- Nan::ObjectWrap::Unwrap<WallProfiler>(info.Holder ());
420-
421- wallProfiler->Dispose (info.GetIsolate ());
422- }
423-
424425NAN_METHOD (WallProfiler::New) {
425- if (info.Length () != 2 ) {
426- return Nan::ThrowTypeError (" WallProfiler must have two arguments." );
426+ if (info.Length () != 4 ) {
427+ return Nan::ThrowTypeError (" WallProfiler must have four arguments." );
427428 }
428429 if (!info[0 ]->IsNumber ()) {
429430 return Nan::ThrowTypeError (" Sample rate must be a number." );
@@ -433,6 +434,14 @@ NAN_METHOD(WallProfiler::New) {
433434 return Nan::ThrowTypeError (" Duration must be a number." );
434435 }
435436
437+ if (!info[2 ]->IsBoolean ()) {
438+ return Nan::ThrowTypeError (" includeLines must be a boolean." );
439+ }
440+
441+ if (!info[3 ]->IsBoolean ()) {
442+ return Nan::ThrowTypeError (" withLabels must be a boolean." );
443+ }
444+
436445 if (info.IsConstructCall ()) {
437446 int interval = Nan::MaybeLocal<Integer>(info[0 ].As <Integer>())
438447 .ToLocalChecked ()
@@ -451,12 +460,21 @@ NAN_METHOD(WallProfiler::New) {
451460 return Nan::ThrowTypeError (" Duration must not be less than sample rate." );
452461 }
453462
454- WallProfiler* obj = new WallProfiler (interval, duration);
463+ bool includeLines = Nan::MaybeLocal<Boolean>(info[2 ].As <Boolean>())
464+ .ToLocalChecked ()
465+ ->Value ();
466+
467+ bool withLabels = Nan::MaybeLocal<Boolean>(info[3 ].As <Boolean>())
468+ .ToLocalChecked ()
469+ ->Value ();
470+
471+ WallProfiler* obj =
472+ new WallProfiler (interval, duration, includeLines, withLabels);
455473 obj->Wrap (info.This ());
456474 info.GetReturnValue ().Set (info.This ());
457475 } else {
458- const int argc = 2 ;
459- v8::Local<v8::Value> argv[argc] = {info[0 ], info[1 ]};
476+ const int argc = 4 ;
477+ v8::Local<v8::Value> argv[argc] = {info[0 ], info[1 ], info[ 2 ], info[ 3 ] };
460478 v8::Local<v8::Function> cons = Nan::New (
461479 PerIsolateData::For (info.GetIsolate ())->WallProfilerConstructor ());
462480 info.GetReturnValue ().Set (
@@ -468,93 +486,101 @@ NAN_METHOD(WallProfiler::Start) {
468486 WallProfiler* wallProfiler =
469487 Nan::ObjectWrap::Unwrap<WallProfiler>(info.Holder ());
470488
471- if (info.Length () != 3 ) {
472- return Nan::ThrowTypeError (" Start must have three arguments." );
473- }
474- if (!info[0 ]->IsString ()) {
475- return Nan::ThrowTypeError (" Profile name must be a string." );
476- }
477- if (!info[1 ]->IsBoolean ()) {
478- return Nan::ThrowTypeError (" Include lines flag must be a boolean." );
479- }
480- if (!info[2 ]->IsBoolean ()) {
481- return Nan::ThrowTypeError (" With labels flag must be a boolean." );
489+ if (info.Length () != 0 ) {
490+ return Nan::ThrowTypeError (" Start must have no arguments." );
482491 }
483492
484- Local<String> name =
485- Nan::MaybeLocal<String>(info[ 0 ]. As <String>()). ToLocalChecked ();
493+ return wallProfiler-> StartStop ( true , false , info);
494+ }
486495
487- bool includeLines =
488- Nan::MaybeLocal<Boolean>(info[1 ].As <Boolean>()).ToLocalChecked ()->Value ();
496+ NAN_METHOD (WallProfiler::Stop) {
497+ if (info.Length () != 1 ) {
498+ return Nan::ThrowTypeError (" Stop must have one arguments." );
499+ }
500+ if (!info[0 ]->IsBoolean ()) {
501+ return Nan::ThrowTypeError (" restart must be a boolean." );
502+ }
503+ bool restart =
504+ Nan::MaybeLocal<Boolean>(info[0 ].As <Boolean>()).ToLocalChecked ()->Value ();
489505
490- bool withLabels =
491- Nan::MaybeLocal<Boolean >(info[ 2 ]. As <Boolean>()). ToLocalChecked ()-> Value ( );
506+ WallProfiler* wallProfiler =
507+ Nan::ObjectWrap::Unwrap<WallProfiler >(info. Holder () );
492508
493- auto now = v8::base::TimeTicks::Now ();
494- wallProfiler->StartImpl (name, includeLines, withLabels);
495- #ifdef DD_WALL_USE_SIGPROF
496- if (withLabels) {
497- wallProfiler->PushContext (now);
498- struct sigaction sa, old_sa;
499- sa.sa_flags = SA_SIGINFO | SA_RESTART;
500- sa.sa_sigaction = &sighandler;
501- sigemptyset (&sa.sa_mask );
502- sigaction (SIGPROF, &sa, &old_sa);
503-
504- // At the end of a cycle start is called before stop,
505- // at this point old_sa.sa_sigaction is sighandler !
506- if (!old_handler) {
507- old_handler = old_sa.sa_sigaction ;
508- }
509+ wallProfiler->StartStop (restart, true , info);
510+ if (!restart) {
511+ wallProfiler->Dispose (info.GetIsolate ());
509512 }
510- #endif
511513}
512514
513- void WallProfiler::StartImpl (Local<String> name,
514- bool includeLines,
515- bool withLabels) {
516- if (includeLines) {
517- GetProfiler ()->StartProfiling (
518- name, CpuProfilingMode::kCallerLineNumbers , withLabels);
519- } else {
520- GetProfiler ()->StartProfiling (name, withLabels);
521- }
522- }
515+ Nan::NAN_METHOD_RETURN_TYPE WallProfiler::StartStop (
516+ bool start, bool stop, Nan::NAN_METHOD_ARGS_TYPE info) {
517+ auto prevSession =
518+ currentSession.exchange (nullptr , std::memory_order_relaxed);
519+ std::atomic_signal_fence (std::memory_order_release);
523520
524- NAN_METHOD (WallProfiler::Stop) {
525- if (info.Length () != 2 ) {
526- return Nan::ThrowTypeError (" Stop must have two arguments." );
527- }
528- if (!info[0 ]->IsString ()) {
529- return Nan::ThrowTypeError (" Profile name must be a string." );
521+ if (start && !stop && prevSession) {
522+ currentSession.store (prevSession, std::memory_order_relaxed);
523+ std::atomic_signal_fence (std::memory_order_release);
524+ return Nan::ThrowError (" Start called with already started profiling "
525+ " session. Call stop first." );
530526 }
531- if (!info[1 ]->IsBoolean ()) {
532- return Nan::ThrowTypeError (" Include lines must be a boolean." );
527+
528+ if (stop && !prevSession) {
529+ return Nan::ThrowError (
530+ " Stop called without started profiling session. Call start first." );
533531 }
534532
535- Local<String> name =
536- Nan::MaybeLocal<String>(info[0 ].As <String>()).ToLocalChecked ();
533+ ProfilingSession* nextSession = nullptr ;
534+ if (start) {
535+ nextSession = prevSession == &session2 ? &session1 : &session2;
537536
538- bool includeLines =
539- Nan::MaybeLocal<Boolean>(info[1 ].As <Boolean>()).ToLocalChecked ()->Value ();
537+ auto now = v8::base::TimeTicks::Now ();
538+ auto nextNameLocal = Nan::New<String>(std::to_string (now)).ToLocalChecked ();
539+ nextSession->name .Reset (info.GetIsolate (), nextNameLocal);
540+ if (includeLines) {
541+ GetProfiler ()->StartProfiling (
542+ nextNameLocal, CpuProfilingMode::kCallerLineNumbers , withLabels);
543+ } else {
544+ GetProfiler ()->StartProfiling (nextNameLocal, withLabels);
545+ }
540546
541- WallProfiler* wallProfiler =
542- Nan::ObjectWrap::Unwrap<WallProfiler>(info.Holder ());
547+ #ifdef DD_WALL_USE_SIGPROF
548+ if (withLabels) {
549+ auto labels = curLabels.load (std::memory_order_relaxed);
550+ nextSession->contexts .push_back (
551+ SampleContext (*labels, now, v8::base::TimeTicks::Now ()));
552+
553+ struct sigaction sa, old_sa;
554+ sa.sa_flags = SA_SIGINFO | SA_RESTART;
555+ sa.sa_sigaction = &sighandler;
556+ sigemptyset (&sa.sa_mask );
557+ sigaction (SIGPROF, &sa, &old_sa);
558+
559+ if (!old_handler) {
560+ old_handler = old_sa.sa_sigaction ;
561+ }
562+ }
563+ #endif
564+ }
543565
544- auto profile = wallProfiler-> StopImpl (name, includeLines) ;
566+ CpuProfile* v8_profile = nullptr ;
545567
546- info.GetReturnValue ().Set (profile);
547- }
568+ if (stop) {
569+ v8_profile =
570+ GetProfiler ()->StopProfiling (prevSession->name .Get (info.GetIsolate ()));
571+ prevSession->name .Reset ();
572+ }
573+
574+ currentSession.store (nextSession, std::memory_order_relaxed);
575+ std::atomic_signal_fence (std::memory_order_release);
548576
549- Local<Value> WallProfiler::StopImpl (Local<String> name,
550- bool includeLines) {
551- auto profiler = GetProfiler ();
552- auto v8_profile = profiler->StopProfiling (name);
553- Local<Value> profile =
554- ProfileTranslator (GetLabelSetsByNode (v8_profile))
555- .TranslateTimeProfile (v8_profile, includeLines);
556- v8_profile->Delete ();
557- return profile;
577+ if (v8_profile) {
578+ Local<Value> profile =
579+ ProfileTranslator (GetLabelSetsByNode (v8_profile, prevSession->contexts ))
580+ .TranslateTimeProfile (v8_profile, includeLines);
581+ v8_profile->Delete ();
582+ info.GetReturnValue ().Set (profile);
583+ }
558584}
559585
560586NAN_MODULE_INIT (WallProfiler::Init) {
@@ -569,7 +595,6 @@ NAN_MODULE_INIT(WallProfiler::Init) {
569595 SetLabels);
570596
571597 Nan::SetPrototypeMethod (tpl, " start" , Start);
572- Nan::SetPrototypeMethod (tpl, " dispose" , Dispose);
573598 Nan::SetPrototypeMethod (tpl, " stop" , Stop);
574599
575600 PerIsolateData::For (Isolate::GetCurrent ())
@@ -622,13 +647,17 @@ NAN_SETTER(WallProfiler::SetLabels) {
622647 profiler->SetLabels (info.GetIsolate (), value);
623648}
624649
625- void WallProfiler::PushContext (int64_t time_from) {
626- // Be careful this is called in a signal handler context therefore all
627- // operations must be async signal safe (in particular no allocations). Our
628- // ring buffer avoids allocations.
629- auto labels = curLabels.load (std::memory_order_relaxed);
650+ void WallProfiler::DoSignalHandler (int sig, siginfo_t * info, void * context) {
651+ auto currSession = currentSession.load (std::memory_order_relaxed);
630652 std::atomic_signal_fence (std::memory_order_acquire);
631- contexts.push_back (SampleContext (*labels, time_from, v8::base::TimeTicks::Now ()));
653+ if (currSession) {
654+ auto labels = curLabels.load (std::memory_order_relaxed);
655+ std::atomic_signal_fence (std::memory_order_acquire);
656+ auto start = v8::base::TimeTicks::Now ();
657+ old_handler (sig, info, context);
658+ currSession->contexts .push_back (
659+ SampleContext (*labels, start, v8::base::TimeTicks::Now ()));
660+ }
632661}
633662
634663} // namespace dd
0 commit comments