11#include < chrono>
22#include < future>
33#include < mutex>
4+ #include < sstream>
45#include < node.h>
56
67using namespace v8 ;
@@ -23,76 +24,60 @@ static std::unordered_map<v8::Isolate *, ThreadInfo> threads = {};
2324
2425// Function to be called when an isolate's execution is interrupted
2526static void ExecutionInterrupted (Isolate *isolate, void *data) {
26- auto promise = static_cast <std::promise<Local<Array> > *>(data);
27+ auto promise = static_cast <std::promise<std::string > *>(data);
2728 auto stack = StackTrace::CurrentStackTrace (isolate, kMaxStackFrames ,
2829 StackTrace::kDetailed );
2930
3031 if (stack.IsEmpty ()) {
31- promise->set_value (Array::New (isolate, 0 ) );
32+ promise->set_value (" " );
3233 return ;
3334 }
3435
35- auto frames = Array::New (isolate, stack-> GetFrameCount ()) ;
36+ std::ostringstream stack_stream ;
3637
3738 for (int i = 0 ; i < stack->GetFrameCount (); i++) {
3839 auto frame = stack->GetFrame (isolate, i);
3940 auto fn_name = frame->GetFunctionName ();
4041
42+ std::string function_name;
4143 if (frame->IsEval ()) {
42- fn_name =
43- String::NewFromUtf8 (isolate, " [eval]" , NewStringType::kInternalized )
44- .ToLocalChecked ();
44+ function_name = " [eval]" ;
4545 } else if (fn_name.IsEmpty () || fn_name->Length () == 0 ) {
46- fn_name = String::NewFromUtf8 (isolate, " ?" , NewStringType::kInternalized )
47- .ToLocalChecked ();
46+ function_name = " ?" ;
4847 } else if (frame->IsConstructor ()) {
49- fn_name = String::NewFromUtf8 (isolate, " [constructor]" ,
50- NewStringType::kInternalized )
51- .ToLocalChecked ();
48+ function_name = " [constructor]" ;
49+ } else {
50+ v8::String::Utf8Value utf8_fn (isolate, fn_name);
51+ function_name = *utf8_fn ? *utf8_fn : " ?" ;
5252 }
5353
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 ();
54+ std::string filename;
55+ auto script_name = frame->GetScriptName ();
56+ if (!script_name.IsEmpty ()) {
57+ v8::String::Utf8Value utf8_filename (isolate, script_name);
58+ filename = *utf8_filename ? *utf8_filename : " <unknown>" ;
59+ } else {
60+ filename = " <unknown>" ;
61+ }
7862
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 ();
63+ int line_number = frame->GetLineNumber ();
64+ int column_number = frame->GetColumn ();
65+
66+ // Build stack trace line in JavaScript format: " at functionName (filename:line:column)"
67+ stack_stream << " at " << function_name << " (" << filename << " :"
68+ << line_number << " :" << column_number << " )" ;
8669
87- frames->Set (isolate->GetCurrentContext (), i, frame_obj).Check ();
70+ if (i < stack->GetFrameCount () - 1 ) {
71+ stack_stream << " \n " ;
72+ }
8873 }
8974
90- promise->set_value (frames );
75+ promise->set_value (stack_stream. str () );
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+ std::string CaptureStackTrace (Isolate *isolate) {
80+ std::promise<std::string > promise;
9681 auto future = promise.get_future ();
9782
9883 // The v8 isolate must be interrupted to capture the stack trace
@@ -105,7 +90,7 @@ 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, std::string >;
10994 std::vector<std::future<ThreadResult>> futures;
11095
11196 // We collect the futures into a vec so they can be processed in parallel
@@ -128,13 +113,17 @@ void CaptureStackTraces(const FunctionCallbackInfo<Value> &args) {
128113 // JavaScript object
129114 Local<Object> result = Object::New (capture_from_isolate);
130115 for (auto &future : futures) {
131- auto [thread_name, frames ] = future.get ();
116+ auto [thread_name, stack_string ] = future.get ();
132117
133118 auto key = String::NewFromUtf8 (capture_from_isolate, thread_name.c_str (),
134119 NewStringType::kNormal )
135120 .ToLocalChecked ();
136121
137- result->Set (capture_from_isolate->GetCurrentContext (), key, frames).Check ();
122+ auto value = String::NewFromUtf8 (capture_from_isolate, stack_string.c_str (),
123+ NewStringType::kNormal )
124+ .ToLocalChecked ();
125+
126+ result->Set (capture_from_isolate->GetCurrentContext (), key, value).Check ();
138127 }
139128
140129 args.GetReturnValue ().Set (result);
0 commit comments