11#include < node.h>
22#include < mutex>
3+ #include < map>
4+ #include < sstream>
5+ #include < future>
36
47using namespace v8 ;
58using namespace node ;
69
7- static v8::Isolate *main_thread_isolate;
8-
9- static std::mutex interrupt_mutex;
10- static std::condition_variable interrupt_cv;
11- static bool interrupt_done = false ;
12-
1310static const int kMaxStackFrames = 255 ;
14- static const int kMaxStackJsonSize = 10240 ;
11+
12+ static std::unordered_map<v8::Isolate *, int > threads = {};
1513
1614static void ExecutionInterrupted (Isolate *isolate, void *data)
1715{
18- char *buffer = static_cast <char *>(data);
19-
20- v8::RegisterState state;
21- v8::SampleInfo info;
22- void *samples[kMaxStackFrames ];
16+ auto promise = static_cast <std::promise<std::string> *>(data);
2317
24- uint32_t pos = 0 ;
18+ Local<StackTrace> stack = StackTrace::CurrentStackTrace (isolate, kMaxStackFrames , StackTrace:: kDetailed ) ;
2519
26- // Initialise the register state
27- state.pc = nullptr ;
28- state.fp = &state;
29- state.sp = &state;
30-
31- isolate->GetStackSample (state, samples, kMaxStackFrames , &info);
32-
33- Local<StackTrace> stack = StackTrace::CurrentStackTrace (isolate, 255 , StackTrace::kDetailed );
3420 if (stack.IsEmpty ())
3521 {
36- snprintf (buffer, kMaxStackJsonSize , " []" );
22+ promise-> set_value ( " []" );
3723 return ;
3824 }
3925
40- pos += snprintf (&buffer[pos], kMaxStackJsonSize , " [" );
41- int count = stack->GetFrameCount ();
26+ std::ostringstream out;
4227
43- for (int i = 0 ; i < count; i++)
28+ out << " [" ;
29+ auto count = stack->GetFrameCount ();
30+
31+ for (auto i = 0 ; i < count; i++)
4432 {
45- Local<StackFrame> frame = stack->GetFrame (isolate, i);
46- Local<String> fn_name = frame->GetFunctionName ();
33+ auto frame = stack->GetFrame (isolate, i);
34+ auto fn_name = frame->GetFunctionName ();
4735
4836 if (frame->IsEval ())
4937 {
@@ -60,74 +48,113 @@ static void ExecutionInterrupted(Isolate *isolate, void *data)
6048
6149 String::Utf8Value function_name (isolate, fn_name);
6250 String::Utf8Value script_name (isolate, frame->GetScriptName ());
63- const int line_number = frame->GetLineNumber ();
64- const int column = frame->GetColumn ();
51+ auto line_number = frame->GetLineNumber ();
52+ auto column = frame->GetColumn ();
6553
66- pos += snprintf (&buffer[pos], kMaxStackJsonSize ,
67- " {\" function\" :\" %s\" ,\" filename\" :\" %s\" ,\" lineno\" :%d,\" colno\" :%d}" ,
68- *function_name,
69- *script_name,
70- line_number,
71- column);
54+ out << " {\" function\" :\" " << *function_name
55+ << " \" ,\" filename\" :\" " << *script_name
56+ << " \" ,\" lineno\" :" << line_number
57+ << " ,\" colno\" :" << column << " }" ;
7258
7359 if (i < count - 1 )
7460 {
75- pos += snprintf (&buffer[pos], kMaxStackJsonSize , " ," ) ;
61+ out << " ," ;
7662 }
7763 }
7864
79- pos += snprintf (&buffer[pos], kMaxStackJsonSize , " ]" ) ;
65+ out << " ]" ;
8066
81- {
82- std::lock_guard<std::mutex> lock (interrupt_mutex);
83- interrupt_done = true ;
84- }
85- interrupt_cv.notify_one ();
67+ promise->set_value (out.str ());
8668}
8769
88- void CaptureStackTrace (const FunctionCallbackInfo<Value> &args )
70+ std::string CaptureStackTrace (Isolate *isolate )
8971{
90- char buffer[kMaxStackJsonSize ] = {0 };
72+ std::promise<std::string> promise;
73+ auto future = promise.get_future ();
74+
75+ isolate->RequestInterrupt (ExecutionInterrupted, &promise);
76+ return future.get ();
77+ }
9178
92- if (auto isolate = main_thread_isolate)
79+ void CaptureStackTraces (const FunctionCallbackInfo<Value> &args)
80+ {
81+ bool exclude_workers = args.Length () == 1 && args[0 ]->IsBoolean () && args[0 ].As <Boolean>()->Value ();
82+ auto capture_from_isolate = args.GetIsolate ();
83+
84+ std::vector<std::future<std::string>> futures;
85+
86+ for (auto &thread : threads)
9387 {
94- // Reset the interrupt_done flag
88+ auto thread_isolate = thread.first ;
89+ if (thread_isolate != capture_from_isolate)
9590 {
96- std::lock_guard<std::mutex> lock (interrupt_mutex);
97- interrupt_done = false ;
91+ int thread_id = thread.second ;
92+
93+ if (exclude_workers && thread_id != -1 )
94+ {
95+ continue ;
96+ }
97+
98+ auto thread_name = thread_id == -1 ? " main" : " worker-" + std::to_string (thread_id);
99+
100+ futures.emplace_back (std::async (std::launch::async, [thread_name](Isolate *isolate)
101+ { return " \" " + thread_name + " \" :" + CaptureStackTrace (isolate); }, thread_isolate));
98102 }
103+ }
99104
100- isolate-> RequestInterrupt (ExecutionInterrupted, buffer) ;
105+ std::ostringstream out ;
101106
102- // Wait for the interrupt to complete
103- std::unique_lock<std::mutex> lock (interrupt_mutex);
104- interrupt_cv.wait (lock, []
105- { return interrupt_done; });
107+ auto count = futures.size ();
108+ out << " {" ;
109+ for (auto &future : futures)
110+ {
111+ out << future.get ();
112+ if (--count > 0 )
113+ {
114+ out << " ," ;
115+ }
106116 }
117+ out << " }" ;
107118
108- Local<String> result = String::NewFromUtf8 (args.GetIsolate (), buffer, NewStringType::kNormal ).ToLocalChecked ();
109- args.GetReturnValue ().Set (result);
119+ args.GetReturnValue ().Set (String::NewFromUtf8 (capture_from_isolate, out.str ().c_str (), NewStringType::kNormal ).ToLocalChecked ());
110120}
111121
112- void SetMainIsolate ( const FunctionCallbackInfo<Value> &args )
122+ void Cleanup ( void *arg )
113123{
114- main_thread_isolate = args.GetIsolate ();
124+ auto isolate = static_cast <Isolate *>(arg);
125+ threads.erase (isolate);
126+ }
127+
128+ void RegisterThread (const FunctionCallbackInfo<Value> &args)
129+ {
130+ auto isolate = args.GetIsolate ();
131+
132+ if (args.Length () != 1 || !args[0 ]->IsNumber ())
133+ {
134+ isolate->ThrowException (Exception::Error (String::NewFromUtf8 (isolate, " registerThread() requires a single threadId argument" , NewStringType::kInternalized ).ToLocalChecked ()));
135+ return ;
136+ }
137+
138+ int thread_id = args[0 ].As <Number>()->Value ();
139+
140+ threads.emplace (isolate, thread_id);
141+ node::AddEnvironmentCleanupHook (isolate, Cleanup, isolate);
115142}
116143
117144extern " C" NODE_MODULE_EXPORT void
118145NODE_MODULE_INITIALIZER (Local<Object> exports,
119146 Local<Value> module ,
120147 Local<Context> context)
121148{
122- Isolate * isolate = context->GetIsolate ();
149+ auto isolate = context->GetIsolate ();
123150
124151 exports->Set (context,
125152 String::NewFromUtf8 (isolate, " captureStackTrace" , NewStringType::kInternalized ).ToLocalChecked (),
126- FunctionTemplate::New (isolate, CaptureStackTrace )->GetFunction (context).ToLocalChecked ())
153+ FunctionTemplate::New (isolate, CaptureStackTraces )->GetFunction (context).ToLocalChecked ())
127154 .Check ();
128155
129156 exports->Set (context,
130- String::NewFromUtf8 (isolate, " setMainIsolate " , NewStringType::kInternalized ).ToLocalChecked (),
131- FunctionTemplate::New (isolate, SetMainIsolate )->GetFunction (context).ToLocalChecked ())
157+ String::NewFromUtf8 (isolate, " registerThread " , NewStringType::kInternalized ).ToLocalChecked (),
158+ FunctionTemplate::New (isolate, RegisterThread )->GetFunction (context).ToLocalChecked ())
132159 .Check ();
133160}
0 commit comments