Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 9 additions & 3 deletions bindings/profilers/wall.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -388,6 +389,7 @@ ContextsByNode WallProfiler::GetContextsByNode(CpuProfile* profile,
auto contextKey = Nan::New<v8::String>("context").ToLocalChecked();
auto timestampKey = Nan::New<v8::String>("timestamp").ToLocalChecked();
auto cpuTimeKey = Nan::New<v8::String>("cpuTime").ToLocalChecked();
auto asyncIdKey = Nan::New<v8::String>("asyncId").ToLocalChecked();
auto V8toEpochOffset = GetV8ToEpochOffset();
auto lastCpuTime = startCpuTime;

Expand Down Expand Up @@ -457,6 +459,9 @@ ContextsByNode WallProfiler::GetContextsByNode(CpuProfile* profile,
Nan::New<v8::Number>(sampleContext.cpu_time - lastCpuTime));
lastCpuTime = sampleContext.cpu_time;
}
Nan::Set(timedContext,
asyncIdKey,
Nan::New<v8::Number>(sampleContext.async_id));
Nan::Set(array, array->Length(), timedContext);
}

Expand Down Expand Up @@ -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<std::atomic<uint32_t>*>(&fields_[kSampleCount]),
1U,
Expand Down
6 changes: 5 additions & 1 deletion bindings/profilers/wall.hh
Original file line number Diff line number Diff line change
Expand Up @@ -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<SampleContext>;
Expand Down Expand Up @@ -118,7 +119,10 @@ class WallProfiler : public Nan::ObjectWrap {

v8::Local<v8::Value> GetContext(v8::Isolate*);
void SetContext(v8::Isolate*, v8::Local<v8::Value>);
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<v8::Value>& profile);
Expand Down
1 change: 1 addition & 0 deletions ts/src/profile-serializer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -326,6 +326,7 @@ function updateTimeProfile(prof: TimeProfile): TimeProfile {
context: {},
timestamp: BigInt(0),
cpuTime: prof.nonJSThreadsCpuTime,
asyncId: -1,
},
],
};
Expand Down
1 change: 1 addition & 0 deletions ts/src/v8-types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
4 changes: 4 additions & 0 deletions ts/test/test-time-profiler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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') {
Expand Down