@@ -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,13 +355,23 @@ 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 curLabels.store (&labels1, std::memory_order_relaxed);
374+ currentSession.store (nullptr , std::memory_order_relaxed);
366375}
367376
368377WallProfiler::~WallProfiler () {
@@ -416,16 +425,9 @@ void WallProfiler::Dispose(Isolate* isolate) {
416425 }
417426}
418427
419- NAN_METHOD (WallProfiler::Dispose) {
420- WallProfiler* wallProfiler =
421- Nan::ObjectWrap::Unwrap<WallProfiler>(info.Holder ());
422-
423- wallProfiler->Dispose (info.GetIsolate ());
424- }
425-
426428NAN_METHOD (WallProfiler::New) {
427- if (info.Length () != 2 ) {
428- return Nan::ThrowTypeError (" WallProfiler must have two arguments." );
429+ if (info.Length () != 4 ) {
430+ return Nan::ThrowTypeError (" WallProfiler must have four arguments." );
429431 }
430432 if (!info[0 ]->IsNumber ()) {
431433 return Nan::ThrowTypeError (" Sample rate must be a number." );
@@ -435,6 +437,14 @@ NAN_METHOD(WallProfiler::New) {
435437 return Nan::ThrowTypeError (" Duration must be a number." );
436438 }
437439
440+ if (!info[2 ]->IsBoolean ()) {
441+ return Nan::ThrowTypeError (" includeLines must be a boolean." );
442+ }
443+
444+ if (!info[3 ]->IsBoolean ()) {
445+ return Nan::ThrowTypeError (" withLabels must be a boolean." );
446+ }
447+
438448 if (info.IsConstructCall ()) {
439449 int interval = Nan::MaybeLocal<Integer>(info[0 ].As <Integer>())
440450 .ToLocalChecked ()
@@ -453,12 +463,21 @@ NAN_METHOD(WallProfiler::New) {
453463 return Nan::ThrowTypeError (" Duration must not be less than sample rate." );
454464 }
455465
456- WallProfiler* obj = new WallProfiler (interval, duration);
466+ bool includeLines = Nan::MaybeLocal<Boolean>(info[2 ].As <Boolean>())
467+ .ToLocalChecked ()
468+ ->Value ();
469+
470+ bool withLabels = Nan::MaybeLocal<Boolean>(info[3 ].As <Boolean>())
471+ .ToLocalChecked ()
472+ ->Value ();
473+
474+ WallProfiler* obj =
475+ new WallProfiler (interval, duration, includeLines, withLabels);
457476 obj->Wrap (info.This ());
458477 info.GetReturnValue ().Set (info.This ());
459478 } else {
460- const int argc = 2 ;
461- v8::Local<v8::Value> argv[argc] = {info[0 ], info[1 ]};
479+ const int argc = 4 ;
480+ v8::Local<v8::Value> argv[argc] = {info[0 ], info[1 ], info[ 2 ], info[ 3 ] };
462481 v8::Local<v8::Function> cons = Nan::New (
463482 PerIsolateData::For (info.GetIsolate ())->WallProfilerConstructor ());
464483 info.GetReturnValue ().Set (
@@ -470,93 +489,110 @@ NAN_METHOD(WallProfiler::Start) {
470489 WallProfiler* wallProfiler =
471490 Nan::ObjectWrap::Unwrap<WallProfiler>(info.Holder ());
472491
473- if (info.Length () != 3 ) {
474- return Nan::ThrowTypeError (" Start must have three arguments." );
475- }
476- if (!info[0 ]->IsString ()) {
477- return Nan::ThrowTypeError (" Profile name must be a string." );
478- }
479- if (!info[1 ]->IsBoolean ()) {
480- return Nan::ThrowTypeError (" Include lines flag must be a boolean." );
481- }
482- if (!info[2 ]->IsBoolean ()) {
483- return Nan::ThrowTypeError (" With labels flag must be a boolean." );
492+ if (info.Length () != 0 ) {
493+ return Nan::ThrowTypeError (" Start must have no arguments." );
484494 }
485495
486- Local<String> name =
487- Nan::MaybeLocal<String>(info[ 0 ]. As <String>()). ToLocalChecked ();
496+ return wallProfiler-> StartStop ( true , false , info);
497+ }
488498
489- bool includeLines =
490- Nan::MaybeLocal<Boolean>(info[1 ].As <Boolean>()).ToLocalChecked ()->Value ();
499+ NAN_METHOD (WallProfiler::Stop) {
500+ if (info.Length () != 1 ) {
501+ return Nan::ThrowTypeError (" Stop must have one arguments." );
502+ }
503+ if (!info[0 ]->IsBoolean ()) {
504+ return Nan::ThrowTypeError (" restart must be a boolean." );
505+ }
506+ bool restart =
507+ Nan::MaybeLocal<Boolean>(info[0 ].As <Boolean>()).ToLocalChecked ()->Value ();
491508
492- bool withLabels =
493- Nan::MaybeLocal<Boolean >(info[ 2 ]. As <Boolean>()). ToLocalChecked ()-> Value ( );
509+ WallProfiler* wallProfiler =
510+ Nan::ObjectWrap::Unwrap<WallProfiler >(info. Holder () );
494511
495- auto now = v8::base::TimeTicks::Now ();
496- wallProfiler->StartImpl (name, includeLines, withLabels);
497- #ifdef DD_WALL_USE_SIGPROF
498- if (withLabels) {
499- wallProfiler->PushContext (now);
500- struct sigaction sa, old_sa;
501- sa.sa_flags = SA_SIGINFO | SA_RESTART;
502- sa.sa_sigaction = &sighandler;
503- sigemptyset (&sa.sa_mask );
504- sigaction (SIGPROF, &sa, &old_sa);
505-
506- // At the end of a cycle start is called before stop,
507- // at this point old_sa.sa_sigaction is sighandler !
508- if (!old_handler) {
509- old_handler = old_sa.sa_sigaction ;
510- }
512+ wallProfiler->StartStop (restart, true , info);
513+ if (!restart) {
514+ wallProfiler->Dispose (info.GetIsolate ());
511515 }
512- #endif
513516}
514517
515- void WallProfiler::StartImpl (Local<String> name,
516- bool includeLines,
517- bool withLabels) {
518- if (includeLines) {
519- GetProfiler ()->StartProfiling (
520- name, CpuProfilingMode::kCallerLineNumbers , withLabels);
521- } else {
522- GetProfiler ()->StartProfiling (name, withLabels);
523- }
524- }
518+ Nan::NAN_METHOD_RETURN_TYPE WallProfiler::StartStop (
519+ bool start, bool stop, Nan::NAN_METHOD_ARGS_TYPE info) {
520+ auto prevSession =
521+ currentSession.exchange (nullptr , std::memory_order_relaxed);
522+ std::atomic_signal_fence (std::memory_order_release);
525523
526- NAN_METHOD (WallProfiler::Stop) {
527- if (info.Length () != 2 ) {
528- return Nan::ThrowTypeError (" Stop must have two arguments." );
529- }
530- if (!info[0 ]->IsString ()) {
531- return Nan::ThrowTypeError (" Profile name must be a string." );
524+ if (start && !stop && prevSession) {
525+ currentSession.store (prevSession, std::memory_order_relaxed);
526+ std::atomic_signal_fence (std::memory_order_release);
527+ return Nan::ThrowError (" Start called with already started profiling "
528+ " session. Call stop first." );
532529 }
533- if (!info[1 ]->IsBoolean ()) {
534- return Nan::ThrowTypeError (" Include lines must be a boolean." );
530+
531+ if (stop && !prevSession) {
532+ return Nan::ThrowError (
533+ " Stop called without started profiling session. Call start first." );
535534 }
536535
537- Local<String> name =
538- Nan::MaybeLocal<String>(info[0 ].As <String>()).ToLocalChecked ();
536+ ProfilingSession* nextSession = nullptr ;
537+ if (start) {
538+ nextSession = prevSession == &session2 ? &session1 : &session2;
539539
540- bool includeLines =
541- Nan::MaybeLocal<Boolean>(info[1 ].As <Boolean>()).ToLocalChecked ()->Value ();
540+ auto now = v8::base::TimeTicks::Now ();
541+ auto nameLocal = Nan::New<String>(std::to_string (now)).ToLocalChecked ();
542+ nextSession->name .Reset (info.GetIsolate (), nameLocal);
543+ CpuProfilingMode mode = includeLines
544+ ? CpuProfilingMode::kCallerLineNumbers
545+ : CpuProfilingMode::kLeafNodeLineNumbers ;
546+ auto status = GetProfiler ()->StartProfiling (nameLocal, mode, withLabels);
547+ switch (status) {
548+ case CpuProfilingStatus::kStarted :
549+ break ;
550+ case CpuProfilingStatus::kAlreadyStarted :
551+ return Nan::ThrowError (" Failed to start V8 profiler; already started" );
552+ case CpuProfilingStatus::kErrorTooManyProfilers :
553+ return Nan::ThrowError (
554+ " Failed to start V8 profiler; too many profilers" );
555+ default :
556+ return Nan::ThrowError (" Failed to start V8 profiler; unknown error" );
557+ }
542558
543- WallProfiler* wallProfiler =
544- Nan::ObjectWrap::Unwrap<WallProfiler>(info.Holder ());
559+ #ifdef DD_WALL_USE_SIGPROF
560+ if (withLabels) {
561+ auto labels = curLabels.load (std::memory_order_relaxed);
562+ nextSession->contexts .push_back (
563+ SampleContext (*labels, now, v8::base::TimeTicks::Now ()));
564+
565+ struct sigaction sa, old_sa;
566+ sa.sa_flags = SA_SIGINFO | SA_RESTART;
567+ sa.sa_sigaction = &sighandler;
568+ sigemptyset (&sa.sa_mask );
569+ sigaction (SIGPROF, &sa, &old_sa);
570+
571+ if (!old_handler) {
572+ old_handler = old_sa.sa_sigaction ;
573+ }
574+ }
575+ #endif
576+ }
545577
546- auto profile = wallProfiler-> StopImpl (name, includeLines) ;
578+ CpuProfile* v8_profile = nullptr ;
547579
548- info.GetReturnValue ().Set (profile);
549- }
580+ if (stop) {
581+ v8_profile =
582+ GetProfiler ()->StopProfiling (prevSession->name .Get (info.GetIsolate ()));
583+ prevSession->name .Reset ();
584+ }
585+
586+ currentSession.store (nextSession, std::memory_order_relaxed);
587+ std::atomic_signal_fence (std::memory_order_release);
550588
551- Local<Value> WallProfiler::StopImpl (Local<String> name,
552- bool includeLines) {
553- auto profiler = GetProfiler ();
554- auto v8_profile = profiler->StopProfiling (name);
555- Local<Value> profile =
556- ProfileTranslator (GetLabelSetsByNode (v8_profile))
557- .TranslateTimeProfile (v8_profile, includeLines);
558- v8_profile->Delete ();
559- return profile;
589+ if (v8_profile) {
590+ Local<Value> profile =
591+ ProfileTranslator (GetLabelSetsByNode (v8_profile, prevSession->contexts ))
592+ .TranslateTimeProfile (v8_profile, includeLines);
593+ v8_profile->Delete ();
594+ info.GetReturnValue ().Set (profile);
595+ }
560596}
561597
562598NAN_MODULE_INIT (WallProfiler::Init) {
@@ -571,7 +607,6 @@ NAN_MODULE_INIT(WallProfiler::Init) {
571607 SetLabels);
572608
573609 Nan::SetPrototypeMethod (tpl, " start" , Start);
574- Nan::SetPrototypeMethod (tpl, " dispose" , Dispose);
575610 Nan::SetPrototypeMethod (tpl, " stop" , Stop);
576611
577612 PerIsolateData::For (Isolate::GetCurrent ())
@@ -624,13 +659,17 @@ NAN_SETTER(WallProfiler::SetLabels) {
624659 profiler->SetLabels (info.GetIsolate (), value);
625660}
626661
627- void WallProfiler::PushContext (int64_t time_from) {
628- // Be careful this is called in a signal handler context therefore all
629- // operations must be async signal safe (in particular no allocations). Our
630- // ring buffer avoids allocations.
631- auto labels = curLabels.load (std::memory_order_relaxed);
662+ void WallProfiler::DoSignalHandler (int sig, siginfo_t * info, void * context) {
663+ auto currSession = currentSession.load (std::memory_order_relaxed);
632664 std::atomic_signal_fence (std::memory_order_acquire);
633- contexts.push_back (SampleContext (*labels, time_from, v8::base::TimeTicks::Now ()));
665+ if (currSession) {
666+ auto labels = curLabels.load (std::memory_order_relaxed);
667+ std::atomic_signal_fence (std::memory_order_acquire);
668+ auto start = v8::base::TimeTicks::Now ();
669+ old_handler (sig, info, context);
670+ currSession->contexts .push_back (
671+ SampleContext (*labels, start, v8::base::TimeTicks::Now ()));
672+ }
634673}
635674
636675} // namespace dd
0 commit comments