@@ -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,45 @@ 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+ MaybeLocal<String> maybe_json =
116+ v8::JSON::Stringify (context, result.ToLocalChecked ());
117+
118+ if (!maybe_json.IsEmpty ()) {
119+ v8::String::Utf8Value utf8_state (isolate, maybe_json.ToLocalChecked ());
120+ if (*utf8_state) {
121+ return *utf8_state;
122+ }
123+ }
124+ }
125+
126+ return " " ;
127+ }
128+
129+ // Function to be called when an isolate's execution is interrupted
130+ static void ExecutionInterrupted (Isolate *isolate, void *data) {
131+ auto promise = static_cast <std::promise<JsStackTrace> *>(data);
132+
133+ promise->set_value ({GetStackFrames (isolate), GetThreadState (isolate)});
93134}
94135
95136// Function to capture the stack trace of a single isolate
@@ -116,12 +157,11 @@ void CaptureStackTraces(const FunctionCallbackInfo<Value> &args) {
116157 if (thread_isolate == capture_from_isolate)
117158 continue ;
118159 auto thread_name = thread_info.thread_name ;
119- auto state = thread_info.state ;
120160
121161 futures.emplace_back (std::async (
122162 std::launch::async,
123- [thread_name, state ](Isolate *isolate) -> ThreadResult {
124- return ThreadResult{thread_name, state, CaptureStackTrace (isolate)};
163+ [thread_name](Isolate *isolate) -> ThreadResult {
164+ return ThreadResult{thread_name, CaptureStackTrace (isolate)};
125165 },
126166 thread_isolate));
127167 }
@@ -137,9 +177,9 @@ void CaptureStackTraces(const FunctionCallbackInfo<Value> &args) {
137177 .ToLocalChecked ();
138178
139179 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];
180+ Array::New (capture_from_isolate, result.stack_trace . frames .size ());
181+ for (size_t i = 0 ; i < result.stack_trace . frames .size (); ++i) {
182+ const auto &frame = result.stack_trace . frames [i];
143183 Local<Object> frameObj = Object::New (capture_from_isolate);
144184 frameObj
145185 ->Set (current_context,
@@ -189,9 +229,10 @@ void CaptureStackTraces(const FunctionCallbackInfo<Value> &args) {
189229 jsFrames)
190230 .Check ();
191231
192- if (!result.state .empty ()) {
232+ if (!result.stack_trace . state .empty ()) {
193233 v8::MaybeLocal<v8::String> stateStr = v8::String::NewFromUtf8 (
194- capture_from_isolate, result.state .c_str (), NewStringType::kNormal );
234+ capture_from_isolate, result.stack_trace .state .c_str (),
235+ NewStringType::kNormal );
195236 if (!stateStr.IsEmpty ()) {
196237 v8::MaybeLocal<v8::Value> maybeStateVal =
197238 v8::JSON::Parse (current_context, stateStr.ToLocalChecked ());
@@ -243,8 +284,7 @@ void RegisterThread(const FunctionCallbackInfo<Value> &args) {
243284 std::lock_guard<std::mutex> lock (threads_mutex);
244285 auto found = threads.find (isolate);
245286 if (found == threads.end ()) {
246- threads.emplace (isolate,
247- ThreadInfo{thread_name, milliseconds::zero (), " " });
287+ threads.emplace (isolate, ThreadInfo{thread_name, milliseconds::zero ()});
248288 // Register a cleanup hook to remove this thread when the isolate is
249289 // destroyed
250290 node::AddEnvironmentCleanupHook (isolate, Cleanup, isolate);
@@ -280,32 +320,17 @@ steady_clock::time_point GetUnbiasedMonotonicTime() {
280320// Function to track a thread and set its state
281321void ThreadPoll (const FunctionCallbackInfo<Value> &args) {
282322 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- }
297323
298324 bool disable_last_seen = false ;
299- if (args.Length () > 1 && args[1 ]->IsBoolean ()) {
300- disable_last_seen = args[1 ]->BooleanValue (isolate);
325+ if (args.Length () > 0 && args[0 ]->IsBoolean ()) {
326+ disable_last_seen = args[0 ]->BooleanValue (isolate);
301327 }
302328
303329 {
304330 std::lock_guard<std::mutex> lock (threads_mutex);
305331 auto found = threads.find (isolate);
306332 if (found != threads.end ()) {
307333 auto &thread_info = found->second ;
308- thread_info.state = state_str;
309334 if (disable_last_seen) {
310335 thread_info.last_seen = milliseconds::zero ();
311336 } else {
0 commit comments