@@ -21,78 +21,63 @@ static std::mutex threads_mutex;
2121// Map to hold all registered threads and their information
2222static std::unordered_map<v8::Isolate *, ThreadInfo> threads = {};
2323
24+ // Structure to hold stack frame information
25+ struct JsStackFrame {
26+ std::string function_name;
27+ std::string filename;
28+ int lineno;
29+ int colno;
30+ };
31+
32+ // Type alias for a vector of JsStackFrame
33+ using JsStackTrace = std::vector<JsStackFrame>;
34+
2435// Function to be called when an isolate's execution is interrupted
2536static void ExecutionInterrupted (Isolate *isolate, void *data) {
26- auto promise = static_cast <std::promise<Local<Array> > *>(data);
37+ auto promise = static_cast <std::promise<JsStackTrace > *>(data);
2738 auto stack = StackTrace::CurrentStackTrace (isolate, kMaxStackFrames ,
2839 StackTrace::kDetailed );
2940
30- if (stack.IsEmpty ()) {
31- promise->set_value (Array::New (isolate, 0 ));
32- return ;
33- }
34-
35- auto frames = Array::New (isolate, stack->GetFrameCount ());
36-
37- for (int i = 0 ; i < stack->GetFrameCount (); i++) {
38- auto frame = stack->GetFrame (isolate, i);
39- auto fn_name = frame->GetFunctionName ();
40-
41- if (frame->IsEval ()) {
42- fn_name =
43- String::NewFromUtf8 (isolate, " [eval]" , NewStringType::kInternalized )
44- .ToLocalChecked ();
45- } else if (fn_name.IsEmpty () || fn_name->Length () == 0 ) {
46- fn_name = String::NewFromUtf8 (isolate, " ?" , NewStringType::kInternalized )
47- .ToLocalChecked ();
48- } else if (frame->IsConstructor ()) {
49- fn_name = String::NewFromUtf8 (isolate, " [constructor]" ,
50- NewStringType::kInternalized )
51- .ToLocalChecked ();
41+ JsStackTrace frames;
42+ if (!stack.IsEmpty ()) {
43+ for (int i = 0 ; i < stack->GetFrameCount (); i++) {
44+ auto frame = stack->GetFrame (isolate, i);
45+ auto fn_name = frame->GetFunctionName ();
46+
47+ std::string function_name;
48+ if (frame->IsEval ()) {
49+ function_name = " [eval]" ;
50+ } else if (fn_name.IsEmpty () || fn_name->Length () == 0 ) {
51+ function_name = " ?" ;
52+ } else if (frame->IsConstructor ()) {
53+ function_name = " [constructor]" ;
54+ } else {
55+ v8::String::Utf8Value utf8_fn (isolate, fn_name);
56+ function_name = *utf8_fn ? *utf8_fn : " ?" ;
57+ }
58+
59+ std::string filename;
60+ auto script_name = frame->GetScriptName ();
61+ if (!script_name.IsEmpty ()) {
62+ v8::String::Utf8Value utf8_filename (isolate, script_name);
63+ filename = *utf8_filename ? *utf8_filename : " <unknown>" ;
64+ } else {
65+ filename = " <unknown>" ;
66+ }
67+
68+ int lineno = frame->GetLineNumber ();
69+ int colno = frame->GetColumn ();
70+
71+ frames.push_back (JsStackFrame{function_name, filename, lineno, colno});
5272 }
53-
54- auto frame_obj = Object::New (isolate);
55- frame_obj
56- ->Set (isolate->GetCurrentContext (),
57- String::NewFromUtf8 (isolate, " function" ,
58- NewStringType::kInternalized )
59- .ToLocalChecked (),
60- fn_name)
61- .Check ();
62-
63- frame_obj
64- ->Set (isolate->GetCurrentContext (),
65- String::NewFromUtf8 (isolate, " filename" ,
66- NewStringType::kInternalized )
67- .ToLocalChecked (),
68- frame->GetScriptName ())
69- .Check ();
70-
71- frame_obj
72- ->Set (
73- isolate->GetCurrentContext (),
74- String::NewFromUtf8 (isolate, " lineno" , NewStringType::kInternalized )
75- .ToLocalChecked (),
76- Integer::New (isolate, frame->GetLineNumber ()))
77- .Check ();
78-
79- frame_obj
80- ->Set (
81- isolate->GetCurrentContext (),
82- String::NewFromUtf8 (isolate, " colno" , NewStringType::kInternalized )
83- .ToLocalChecked (),
84- Integer::New (isolate, frame->GetColumn ()))
85- .Check ();
86-
87- frames->Set (isolate->GetCurrentContext (), i, frame_obj).Check ();
8873 }
8974
9075 promise->set_value (frames);
9176}
9277
9378// Function to capture the stack trace of a single isolate
94- Local<Array> CaptureStackTrace (Isolate *isolate) {
95- std::promise<Local<Array> > promise;
79+ JsStackTrace CaptureStackTrace (Isolate *isolate) {
80+ std::promise<JsStackTrace > promise;
9681 auto future = promise.get_future ();
9782
9883 // The v8 isolate must be interrupted to capture the stack trace
@@ -105,36 +90,79 @@ Local<Array> CaptureStackTrace(Isolate *isolate) {
10590void CaptureStackTraces (const FunctionCallbackInfo<Value> &args) {
10691 auto capture_from_isolate = args.GetIsolate ();
10792
108- using ThreadResult = std::tuple<std::string, Local<Array> >;
93+ using ThreadResult = std::tuple<std::string, JsStackTrace >;
10994 std::vector<std::future<ThreadResult>> futures;
11095
111- // We collect the futures into a vec so they can be processed in parallel
112- std::lock_guard<std::mutex> lock (threads_mutex);
113- for (auto [thread_isolate, thread_info] : threads) {
114- if (thread_isolate == capture_from_isolate)
115- continue ;
116-
117- auto thread_name = thread_info. thread_name ;
118-
119- futures. emplace_back ( std::async (
120- std::launch::async,
121- [thread_name](Isolate *isolate) -> ThreadResult {
122- return std::make_tuple (thread_name, CaptureStackTrace (isolate));
123- },
124- thread_isolate));
96+ {
97+ std::lock_guard<std::mutex> lock (threads_mutex);
98+ for (auto [thread_isolate, thread_info] : threads) {
99+ if (thread_isolate == capture_from_isolate)
100+ continue ;
101+ auto thread_name = thread_info. thread_name ;
102+
103+ futures. emplace_back ( std::async (
104+ std::launch:: async,
105+ [thread_name](Isolate *isolate) -> ThreadResult {
106+ return std::make_tuple (thread_name, CaptureStackTrace (isolate));
107+ },
108+ thread_isolate));
109+ }
125110 }
126111
127- // We wait for all futures to complete and collect their results into a
128- // JavaScript object
129112 Local<Object> result = Object::New (capture_from_isolate);
113+
130114 for (auto &future : futures) {
131115 auto [thread_name, frames] = future.get ();
132-
133116 auto key = String::NewFromUtf8 (capture_from_isolate, thread_name.c_str (),
134117 NewStringType::kNormal )
135118 .ToLocalChecked ();
136119
137- result->Set (capture_from_isolate->GetCurrentContext (), key, frames).Check ();
120+ Local<Array> jsFrames =
121+ Array::New (capture_from_isolate, static_cast <int >(frames.size ()));
122+ for (size_t i = 0 ; i < frames.size (); ++i) {
123+ const auto &f = frames[i];
124+ Local<Object> frameObj = Object::New (capture_from_isolate);
125+ frameObj
126+ ->Set (capture_from_isolate->GetCurrentContext (),
127+ String::NewFromUtf8 (capture_from_isolate, " function" ,
128+ NewStringType::kInternalized )
129+ .ToLocalChecked (),
130+ String::NewFromUtf8 (capture_from_isolate,
131+ f.function_name .c_str (),
132+ NewStringType::kNormal )
133+ .ToLocalChecked ())
134+ .Check ();
135+ frameObj
136+ ->Set (capture_from_isolate->GetCurrentContext (),
137+ String::NewFromUtf8 (capture_from_isolate, " filename" ,
138+ NewStringType::kInternalized )
139+ .ToLocalChecked (),
140+ String::NewFromUtf8 (capture_from_isolate, f.filename .c_str (),
141+ NewStringType::kNormal )
142+ .ToLocalChecked ())
143+ .Check ();
144+ frameObj
145+ ->Set (capture_from_isolate->GetCurrentContext (),
146+ String::NewFromUtf8 (capture_from_isolate, " lineno" ,
147+ NewStringType::kInternalized )
148+ .ToLocalChecked (),
149+ Integer::New (capture_from_isolate, f.lineno ))
150+ .Check ();
151+ frameObj
152+ ->Set (capture_from_isolate->GetCurrentContext (),
153+ String::NewFromUtf8 (capture_from_isolate, " colno" ,
154+ NewStringType::kInternalized )
155+ .ToLocalChecked (),
156+ Integer::New (capture_from_isolate, f.colno ))
157+ .Check ();
158+ jsFrames
159+ ->Set (capture_from_isolate->GetCurrentContext (),
160+ static_cast <uint32_t >(i), frameObj)
161+ .Check ();
162+ }
163+
164+ result->Set (capture_from_isolate->GetCurrentContext (), key, jsFrames)
165+ .Check ();
138166 }
139167
140168 args.GetReturnValue ().Set (result);
0 commit comments