22#include < future>
33#include < mutex>
44#include < node.h>
5+ #include < sstream>
56
67using namespace v8 ;
78using namespace node ;
@@ -23,76 +24,61 @@ 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+ // Build stack trace line in JavaScript format:
43+ // " at functionName(filename:line:column)"
44+ stack_stream << " at " ;
4145 if (frame->IsEval ()) {
42- fn_name =
43- String::NewFromUtf8 (isolate, " [eval]" , NewStringType::kInternalized )
44- .ToLocalChecked ();
46+ stack_stream << " [eval]" ;
4547 } else if (fn_name.IsEmpty () || fn_name->Length () == 0 ) {
46- fn_name = String::NewFromUtf8 (isolate, " ?" , NewStringType::kInternalized )
47- .ToLocalChecked ();
48+ stack_stream << " ?" ;
4849 } else if (frame->IsConstructor ()) {
49- fn_name = String::NewFromUtf8 (isolate, " [constructor]" ,
50- NewStringType::kInternalized )
51- .ToLocalChecked ();
50+ stack_stream << " [constructor]" ;
51+ } else {
52+ v8::String::Utf8Value utf8_fn (isolate, fn_name);
53+ stack_stream << (*utf8_fn ? *utf8_fn : " ?" );
5254 }
5355
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 ();
56+ stack_stream << " (" ;
7857
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 ();
58+ auto script_name = frame-> GetScriptName ();
59+ if (!script_name. IsEmpty ()) {
60+ v8::String::Utf8Value utf8_filename (isolate, script_name);
61+ stack_stream << (*utf8_filename ? *utf8_filename : " <unknown> " );
62+ } else {
63+ stack_stream << " <unknown> " ;
64+ }
8665
87- frames->Set (isolate->GetCurrentContext (), i, frame_obj).Check ();
66+ int line_number = frame->GetLineNumber ();
67+ int column_number = frame->GetColumn ();
68+
69+ stack_stream << " :" << line_number << " :" << column_number << " )" ;
70+
71+ if (i < stack->GetFrameCount () - 1 ) {
72+ stack_stream << " \n " ;
73+ }
8874 }
8975
90- promise->set_value (frames );
76+ promise->set_value (stack_stream. str () );
9177}
9278
9379// Function to capture the stack trace of a single isolate
94- Local<Array> CaptureStackTrace (Isolate *isolate) {
95- std::promise<Local<Array> > promise;
80+ std::string CaptureStackTrace (Isolate *isolate) {
81+ std::promise<std::string > promise;
9682 auto future = promise.get_future ();
9783
9884 // The v8 isolate must be interrupted to capture the stack trace
@@ -105,7 +91,7 @@ Local<Array> CaptureStackTrace(Isolate *isolate) {
10591void CaptureStackTraces (const FunctionCallbackInfo<Value> &args) {
10692 auto capture_from_isolate = args.GetIsolate ();
10793
108- using ThreadResult = std::tuple<std::string, Local<Array> >;
94+ using ThreadResult = std::tuple<std::string, std::string >;
10995 std::vector<std::future<ThreadResult>> futures;
11096
11197 // We collect the futures into a vec so they can be processed in parallel
@@ -128,13 +114,17 @@ void CaptureStackTraces(const FunctionCallbackInfo<Value> &args) {
128114 // JavaScript object
129115 Local<Object> result = Object::New (capture_from_isolate);
130116 for (auto &future : futures) {
131- auto [thread_name, frames ] = future.get ();
117+ auto [thread_name, stack_string ] = future.get ();
132118
133119 auto key = String::NewFromUtf8 (capture_from_isolate, thread_name.c_str (),
134120 NewStringType::kNormal )
135121 .ToLocalChecked ();
136122
137- result->Set (capture_from_isolate->GetCurrentContext (), key, frames).Check ();
123+ auto value = String::NewFromUtf8 (capture_from_isolate, stack_string.c_str (),
124+ NewStringType::kNormal )
125+ .ToLocalChecked ();
126+
127+ result->Set (capture_from_isolate->GetCurrentContext (), key, value).Check ();
138128 }
139129
140130 args.GetReturnValue ().Set (result);
0 commit comments