diff --git a/bindings/profilers/wall.cc b/bindings/profilers/wall.cc index 7585fb1f..8e3e57bf 100644 --- a/bindings/profilers/wall.cc +++ b/bindings/profilers/wall.cc @@ -321,7 +321,8 @@ void SignalHandler::HandleProfilerSignal(int sig, auto time_from = Now(); old_handler(sig, info, context); auto time_to = Now(); - prof->PushContext(time_from, time_to, cpu_time); + double async_id = node::AsyncHooksGetExecutionAsyncId(isolate); + prof->PushContext(time_from, time_to, cpu_time, async_id); } #else class SignalHandler { @@ -388,6 +389,7 @@ ContextsByNode WallProfiler::GetContextsByNode(CpuProfile* profile, auto contextKey = Nan::New("context").ToLocalChecked(); auto timestampKey = Nan::New("timestamp").ToLocalChecked(); auto cpuTimeKey = Nan::New("cpuTime").ToLocalChecked(); + auto asyncIdKey = Nan::New("asyncId").ToLocalChecked(); auto V8toEpochOffset = GetV8ToEpochOffset(); auto lastCpuTime = startCpuTime; @@ -457,6 +459,9 @@ ContextsByNode WallProfiler::GetContextsByNode(CpuProfile* profile, Nan::New(sampleContext.cpu_time - lastCpuTime)); lastCpuTime = sampleContext.cpu_time; } + Nan::Set(timedContext, + asyncIdKey, + Nan::New(sampleContext.async_id)); Nan::Set(array, array->Length(), timedContext); } @@ -1003,14 +1008,15 @@ NAN_METHOD(WallProfiler::Dispose) { void WallProfiler::PushContext(int64_t time_from, int64_t time_to, - int64_t cpu_time) { + int64_t cpu_time, + double async_id) { // Be careful this is called in a signal handler context therefore all // operations must be async signal safe (in particular no allocations). // Our ring buffer avoids allocations. auto context = curContext_.load(std::memory_order_relaxed); std::atomic_signal_fence(std::memory_order_acquire); if (contexts_.size() < contexts_.capacity()) { - contexts_.push_back({*context, time_from, time_to, cpu_time}); + contexts_.push_back({*context, time_from, time_to, cpu_time, async_id}); std::atomic_fetch_add_explicit( reinterpret_cast*>(&fields_[kSampleCount]), 1U, diff --git a/bindings/profilers/wall.hh b/bindings/profilers/wall.hh index 91871b0f..dd2ebe77 100644 --- a/bindings/profilers/wall.hh +++ b/bindings/profilers/wall.hh @@ -82,6 +82,7 @@ class WallProfiler : public Nan::ObjectWrap { int64_t time_from; int64_t time_to; int64_t cpu_time; + double async_id; }; using ContextBuffer = std::vector; @@ -118,7 +119,10 @@ class WallProfiler : public Nan::ObjectWrap { v8::Local GetContext(v8::Isolate*); void SetContext(v8::Isolate*, v8::Local); - void PushContext(int64_t time_from, int64_t time_to, int64_t cpu_time); + void PushContext(int64_t time_from, + int64_t time_to, + int64_t cpu_time, + double async_id); Result StartImpl(); std::string StartInternal(); Result StopImpl(bool restart, v8::Local& profile); diff --git a/ts/src/profile-serializer.ts b/ts/src/profile-serializer.ts index 21d5a086..9eed3153 100644 --- a/ts/src/profile-serializer.ts +++ b/ts/src/profile-serializer.ts @@ -326,6 +326,7 @@ function updateTimeProfile(prof: TimeProfile): TimeProfile { context: {}, timestamp: BigInt(0), cpuTime: prof.nonJSThreadsCpuTime, + asyncId: -1, }, ], }; diff --git a/ts/src/v8-types.ts b/ts/src/v8-types.ts index af81922e..28e15a98 100644 --- a/ts/src/v8-types.ts +++ b/ts/src/v8-types.ts @@ -41,6 +41,7 @@ export interface TimeProfileNodeContext { context: object; timestamp: bigint; // end of sample taking; in microseconds since epoch cpuTime: number; // cpu time in nanoseconds + asyncId: number; // async_hooks.executionAsyncId() at the time of sample taking } export interface TimeProfileNode extends ProfileNode { diff --git a/ts/test/test-time-profiler.ts b/ts/test/test-time-profiler.ts index 607e1a6f..26e7664b 100644 --- a/ts/test/test-time-profiler.ts +++ b/ts/test/test-time-profiler.ts @@ -135,6 +135,10 @@ describe('Time Profiler', () => { if (!context) { return {}; } + assert( + typeof context.asyncId === 'number', + 'context.asyncId should be a number' + ); const labels: LabelSet = {}; for (const [key, value] of Object.entries(context.context)) { if (typeof value === 'string') {