@@ -21,78 +21,60 @@ 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+
2432// Function to be called when an isolate's execution is interrupted
2533static void ExecutionInterrupted (Isolate *isolate, void *data) {
26- auto promise = static_cast <std::promise<Local<Array >> *>(data);
34+ auto promise = static_cast <std::promise<std::vector<JsStackFrame >> *>(data);
2735 auto stack = StackTrace::CurrentStackTrace (isolate, kMaxStackFrames ,
2836 StackTrace::kDetailed );
2937
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 ();
38+ std::vector<JsStackFrame> frames;
39+ if (!stack.IsEmpty ()) {
40+ for (int i = 0 ; i < stack->GetFrameCount (); i++) {
41+ auto frame = stack->GetFrame (isolate, i);
42+ auto fn_name = frame->GetFunctionName ();
43+
44+ std::string function_name;
45+ if (frame->IsEval ()) {
46+ function_name = " [eval]" ;
47+ } else if (fn_name.IsEmpty () || fn_name->Length () == 0 ) {
48+ function_name = " ?" ;
49+ } else if (frame->IsConstructor ()) {
50+ function_name = " [constructor]" ;
51+ } else {
52+ v8::String::Utf8Value utf8_fn (isolate, fn_name);
53+ function_name = *utf8_fn ? *utf8_fn : " ?" ;
54+ }
55+
56+ std::string filename;
57+ auto script_name = frame->GetScriptName ();
58+ if (!script_name.IsEmpty ()) {
59+ v8::String::Utf8Value utf8_filename (isolate, script_name);
60+ filename = *utf8_filename ? *utf8_filename : " <unknown>" ;
61+ } else {
62+ filename = " <unknown>" ;
63+ }
64+
65+ int lineno = frame->GetLineNumber ();
66+ int colno = frame->GetColumn ();
67+
68+ frames.push_back (JsStackFrame{function_name, filename, lineno, colno});
5269 }
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 ();
8870 }
8971
9072 promise->set_value (frames);
9173}
9274
9375// Function to capture the stack trace of a single isolate
94- Local<Array > CaptureStackTrace (Isolate *isolate) {
95- std::promise<Local<Array >> promise;
76+ std::vector<JsStackFrame > CaptureStackTrace (Isolate *isolate) {
77+ std::promise<std::vector<JsStackFrame >> promise;
9678 auto future = promise.get_future ();
9779
9880 // The v8 isolate must be interrupted to capture the stack trace
@@ -105,36 +87,79 @@ Local<Array> CaptureStackTrace(Isolate *isolate) {
10587void CaptureStackTraces (const FunctionCallbackInfo<Value> &args) {
10688 auto capture_from_isolate = args.GetIsolate ();
10789
108- using ThreadResult = std::tuple<std::string, Local<Array >>;
90+ using ThreadResult = std::tuple<std::string, std::vector<JsStackFrame >>;
10991 std::vector<std::future<ThreadResult>> futures;
11092
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));
93+ {
94+ std::lock_guard<std::mutex> lock (threads_mutex);
95+ for (auto [thread_isolate, thread_info] : threads) {
96+ if (thread_isolate == capture_from_isolate)
97+ continue ;
98+ auto thread_name = thread_info. thread_name ;
99+
100+ futures. emplace_back ( std::async (
101+ std::launch:: async,
102+ [thread_name](Isolate *isolate) -> ThreadResult {
103+ return std::make_tuple (thread_name, CaptureStackTrace (isolate));
104+ },
105+ thread_isolate));
106+ }
125107 }
126108
127- // We wait for all futures to complete and collect their results into a
128- // JavaScript object
129109 Local<Object> result = Object::New (capture_from_isolate);
110+
130111 for (auto &future : futures) {
131112 auto [thread_name, frames] = future.get ();
132-
133113 auto key = String::NewFromUtf8 (capture_from_isolate, thread_name.c_str (),
134114 NewStringType::kNormal )
135115 .ToLocalChecked ();
136116
137- result->Set (capture_from_isolate->GetCurrentContext (), key, frames).Check ();
117+ Local<Array> jsFrames =
118+ Array::New (capture_from_isolate, static_cast <int >(frames.size ()));
119+ for (size_t i = 0 ; i < frames.size (); ++i) {
120+ const auto &f = frames[i];
121+ Local<Object> frameObj = Object::New (capture_from_isolate);
122+ frameObj
123+ ->Set (capture_from_isolate->GetCurrentContext (),
124+ String::NewFromUtf8 (capture_from_isolate, " function" ,
125+ NewStringType::kInternalized )
126+ .ToLocalChecked (),
127+ String::NewFromUtf8 (capture_from_isolate,
128+ f.function_name .c_str (),
129+ NewStringType::kNormal )
130+ .ToLocalChecked ())
131+ .Check ();
132+ frameObj
133+ ->Set (capture_from_isolate->GetCurrentContext (),
134+ String::NewFromUtf8 (capture_from_isolate, " filename" ,
135+ NewStringType::kInternalized )
136+ .ToLocalChecked (),
137+ String::NewFromUtf8 (capture_from_isolate, f.filename .c_str (),
138+ NewStringType::kNormal )
139+ .ToLocalChecked ())
140+ .Check ();
141+ frameObj
142+ ->Set (capture_from_isolate->GetCurrentContext (),
143+ String::NewFromUtf8 (capture_from_isolate, " lineno" ,
144+ NewStringType::kInternalized )
145+ .ToLocalChecked (),
146+ Integer::New (capture_from_isolate, f.lineno ))
147+ .Check ();
148+ frameObj
149+ ->Set (capture_from_isolate->GetCurrentContext (),
150+ String::NewFromUtf8 (capture_from_isolate, " colno" ,
151+ NewStringType::kInternalized )
152+ .ToLocalChecked (),
153+ Integer::New (capture_from_isolate, f.colno ))
154+ .Check ();
155+ jsFrames
156+ ->Set (capture_from_isolate->GetCurrentContext (),
157+ static_cast <uint32_t >(i), frameObj)
158+ .Check ();
159+ }
160+
161+ result->Set (capture_from_isolate->GetCurrentContext (), key, jsFrames)
162+ .Check ();
138163 }
139164
140165 args.GetReturnValue ().Set (result);
0 commit comments