@@ -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
@@ -104,37 +89,77 @@ Local<Array> CaptureStackTrace(Isolate *isolate) {
10489// Function to capture stack traces from all registered threads
10590void CaptureStackTraces (const FunctionCallbackInfo<Value> &args) {
10691 auto capture_from_isolate = args.GetIsolate ();
92+ auto current_context = capture_from_isolate->GetCurrentContext ();
10793
108- using ThreadResult = std::tuple<std::string, Local<Array> >;
94+ using ThreadResult = std::tuple<std::string, JsStackTrace >;
10995 std::vector<std::future<ThreadResult>> futures;
11096
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));
97+ {
98+ std::lock_guard<std::mutex> lock (threads_mutex);
99+ for (auto [thread_isolate, thread_info] : threads) {
100+ if (thread_isolate == capture_from_isolate)
101+ continue ;
102+ auto thread_name = thread_info. thread_name ;
103+
104+ futures. emplace_back ( std::async (
105+ std::launch:: async,
106+ [thread_name](Isolate *isolate) -> ThreadResult {
107+ return std::make_tuple (thread_name, CaptureStackTrace (isolate));
108+ },
109+ thread_isolate));
110+ }
125111 }
126112
127- // We wait for all futures to complete and collect their results into a
128- // JavaScript object
129113 Local<Object> result = Object::New (capture_from_isolate);
114+
130115 for (auto &future : futures) {
131116 auto [thread_name, frames] = future.get ();
132-
133117 auto key = String::NewFromUtf8 (capture_from_isolate, thread_name.c_str (),
134118 NewStringType::kNormal )
135119 .ToLocalChecked ();
136120
137- result->Set (capture_from_isolate->GetCurrentContext (), key, frames).Check ();
121+ Local<Array> jsFrames = Array::New (capture_from_isolate, 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 (current_context,
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 (current_context,
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 (current_context,
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 (current_context,
153+ String::NewFromUtf8 (capture_from_isolate, " colno" ,
154+ NewStringType::kInternalized )
155+ .ToLocalChecked (),
156+ Integer::New (capture_from_isolate, f.colno ))
157+ .Check ();
158+ jsFrames->Set (current_context, static_cast <uint32_t >(i), frameObj)
159+ .Check ();
160+ }
161+
162+ result->Set (current_context, key, jsFrames).Check ();
138163 }
139164
140165 args.GetReturnValue ().Set (result);
0 commit comments