@@ -16,16 +16,14 @@ using namespace v8;
1616using namespace node ;
1717using namespace std ::chrono;
1818
19- static const int kMaxStackFrames = 255 ;
19+ static const int kMaxStackFrames = 50 ;
2020
2121// Structure to hold information for each thread/isolate
2222struct ThreadInfo {
2323 // Thread name
2424 std::string thread_name;
2525 // Last time this thread was seen in milliseconds since epoch
2626 milliseconds last_seen;
27- // Some JSON serialized state for the thread
28- std::string state;
2927};
3028
3129static std::mutex threads_mutex;
@@ -41,21 +39,26 @@ struct JsStackFrame {
4139};
4240
4341// Type alias for a vector of JsStackFrame
44- using JsStackTrace = std::vector<JsStackFrame>;
42+ using JsStackFrames = std::vector<JsStackFrame>;
43+
44+ struct JsStackTrace {
45+ // The frames in the stack trace
46+ std::vector<JsStackFrame> frames;
47+ // JSON serialized string of the state
48+ std::string state;
49+ };
4550
4651struct ThreadResult {
4752 std::string thread_name;
48- std::string state;
49- JsStackTrace stack_frames;
53+ JsStackTrace stack_trace;
5054};
5155
52- // Function to be called when an isolate's execution is interrupted
53- static void ExecutionInterrupted (Isolate *isolate, void *data) {
54- auto promise = static_cast <std::promise<JsStackTrace> *>(data);
56+ // Function to get stack frames from a V8 stack trace
57+ JsStackFrames GetStackFrames (Isolate *isolate) {
5558 auto stack = StackTrace::CurrentStackTrace (isolate, kMaxStackFrames ,
5659 StackTrace::kDetailed );
5760
58- JsStackTrace frames;
61+ JsStackFrames frames;
5962 if (!stack.IsEmpty ()) {
6063 for (int i = 0 ; i < stack->GetFrameCount (); i++) {
6164 auto frame = stack->GetFrame (isolate, i);
@@ -89,7 +92,47 @@ static void ExecutionInterrupted(Isolate *isolate, void *data) {
8992 }
9093 }
9194
92- promise->set_value (frames);
95+ return frames;
96+ }
97+
98+ // Function to fetch the thread state from the isolate by calling a global
99+ // function called __get_thread_state_callback__
100+ std::string GetThreadState (Isolate *isolate) {
101+ auto callback_name =
102+ v8::String::NewFromUtf8 (isolate, " __get_thread_state_callback__" ,
103+ v8::NewStringType::kNormal )
104+ .ToLocalChecked ();
105+ auto context = isolate->GetCurrentContext ();
106+ auto callback =
107+ context->Global ()->Get (context, callback_name).ToLocalChecked ();
108+
109+ if (callback->IsFunction ()) {
110+ v8::TryCatch try_catch (isolate);
111+
112+ auto result = Local<Function>::Cast (callback)->Call (
113+ context, Undefined (isolate), 0 , {});
114+
115+ if (!try_catch.HasCaught () && !result.IsEmpty ()) {
116+ MaybeLocal<String> maybe_json =
117+ v8::JSON::Stringify (context, result.ToLocalChecked ());
118+
119+ if (!maybe_json.IsEmpty ()) {
120+ v8::String::Utf8Value utf8_state (isolate, maybe_json.ToLocalChecked ());
121+ if (*utf8_state) {
122+ return *utf8_state;
123+ }
124+ }
125+ }
126+ }
127+
128+ return " " ;
129+ }
130+
131+ // Function to be called when an isolate's execution is interrupted
132+ static void ExecutionInterrupted (Isolate *isolate, void *data) {
133+ auto promise = static_cast <std::promise<JsStackTrace> *>(data);
134+
135+ promise->set_value ({GetStackFrames (isolate), GetThreadState (isolate)});
93136}
94137
95138// Function to capture the stack trace of a single isolate
@@ -116,12 +159,11 @@ void CaptureStackTraces(const FunctionCallbackInfo<Value> &args) {
116159 if (thread_isolate == capture_from_isolate)
117160 continue ;
118161 auto thread_name = thread_info.thread_name ;
119- auto state = thread_info.state ;
120162
121163 futures.emplace_back (std::async (
122164 std::launch::async,
123- [thread_name, state ](Isolate *isolate) -> ThreadResult {
124- return ThreadResult{thread_name, state, CaptureStackTrace (isolate)};
165+ [thread_name](Isolate *isolate) -> ThreadResult {
166+ return ThreadResult{thread_name, CaptureStackTrace (isolate)};
125167 },
126168 thread_isolate));
127169 }
@@ -137,9 +179,9 @@ void CaptureStackTraces(const FunctionCallbackInfo<Value> &args) {
137179 .ToLocalChecked ();
138180
139181 Local<Array> jsFrames =
140- Array::New (capture_from_isolate, result.stack_frames .size ());
141- for (size_t i = 0 ; i < result.stack_frames .size (); ++i) {
142- const auto &frame = result.stack_frames [i];
182+ Array::New (capture_from_isolate, result.stack_trace . frames .size ());
183+ for (size_t i = 0 ; i < result.stack_trace . frames .size (); ++i) {
184+ const auto &frame = result.stack_trace . frames [i];
143185 Local<Object> frameObj = Object::New (capture_from_isolate);
144186 frameObj
145187 ->Set (current_context,
@@ -189,9 +231,10 @@ void CaptureStackTraces(const FunctionCallbackInfo<Value> &args) {
189231 jsFrames)
190232 .Check ();
191233
192- if (!result.state .empty ()) {
234+ if (!result.stack_trace . state .empty ()) {
193235 v8::MaybeLocal<v8::String> stateStr = v8::String::NewFromUtf8 (
194- capture_from_isolate, result.state .c_str (), NewStringType::kNormal );
236+ capture_from_isolate, result.stack_trace .state .c_str (),
237+ NewStringType::kNormal );
195238 if (!stateStr.IsEmpty ()) {
196239 v8::MaybeLocal<v8::Value> maybeStateVal =
197240 v8::JSON::Parse (current_context, stateStr.ToLocalChecked ());
@@ -243,8 +286,7 @@ void RegisterThread(const FunctionCallbackInfo<Value> &args) {
243286 std::lock_guard<std::mutex> lock (threads_mutex);
244287 auto found = threads.find (isolate);
245288 if (found == threads.end ()) {
246- threads.emplace (isolate,
247- ThreadInfo{thread_name, milliseconds::zero (), " " });
289+ threads.emplace (isolate, ThreadInfo{thread_name, milliseconds::zero ()});
248290 // Register a cleanup hook to remove this thread when the isolate is
249291 // destroyed
250292 node::AddEnvironmentCleanupHook (isolate, Cleanup, isolate);
@@ -280,32 +322,17 @@ steady_clock::time_point GetUnbiasedMonotonicTime() {
280322// Function to track a thread and set its state
281323void ThreadPoll (const FunctionCallbackInfo<Value> &args) {
282324 auto isolate = args.GetIsolate ();
283- auto context = isolate->GetCurrentContext ();
284-
285- std::string state_str;
286- if (args.Length () > 0 && args[0 ]->IsValue ()) {
287- MaybeLocal<String> maybe_json = v8::JSON::Stringify (context, args[0 ]);
288- if (!maybe_json.IsEmpty ()) {
289- v8::String::Utf8Value utf8_state (isolate, maybe_json.ToLocalChecked ());
290- state_str = *utf8_state ? *utf8_state : " " ;
291- } else {
292- state_str = " " ;
293- }
294- } else {
295- state_str = " " ;
296- }
297325
298326 bool disable_last_seen = false ;
299- if (args.Length () > 1 && args[1 ]->IsBoolean ()) {
300- disable_last_seen = args[1 ]->BooleanValue (isolate);
327+ if (args.Length () > 0 && args[0 ]->IsBoolean ()) {
328+ disable_last_seen = args[0 ]->BooleanValue (isolate);
301329 }
302330
303331 {
304332 std::lock_guard<std::mutex> lock (threads_mutex);
305333 auto found = threads.find (isolate);
306334 if (found != threads.end ()) {
307335 auto &thread_info = found->second ;
308- thread_info.state = state_str;
309336 if (disable_last_seen) {
310337 thread_info.last_seen = milliseconds::zero ();
311338 } else {
0 commit comments