@@ -16,16 +16,14 @@ using namespace v8;
16
16
using namespace node ;
17
17
using namespace std ::chrono;
18
18
19
- static const int kMaxStackFrames = 255 ;
19
+ static const int kMaxStackFrames = 50 ;
20
20
21
21
// Structure to hold information for each thread/isolate
22
22
struct ThreadInfo {
23
23
// Thread name
24
24
std::string thread_name;
25
25
// Last time this thread was seen in milliseconds since epoch
26
26
milliseconds last_seen;
27
- // Some JSON serialized state for the thread
28
- std::string state;
29
27
};
30
28
31
29
static std::mutex threads_mutex;
@@ -41,21 +39,26 @@ struct JsStackFrame {
41
39
};
42
40
43
41
// 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
+ };
45
50
46
51
struct ThreadResult {
47
52
std::string thread_name;
48
- std::string state;
49
- JsStackTrace stack_frames;
53
+ JsStackTrace stack_trace;
50
54
};
51
55
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) {
55
58
auto stack = StackTrace::CurrentStackTrace (isolate, kMaxStackFrames ,
56
59
StackTrace::kDetailed );
57
60
58
- JsStackTrace frames;
61
+ JsStackFrames frames;
59
62
if (!stack.IsEmpty ()) {
60
63
for (int i = 0 ; i < stack->GetFrameCount (); i++) {
61
64
auto frame = stack->GetFrame (isolate, i);
@@ -89,7 +92,45 @@ static void ExecutionInterrupted(Isolate *isolate, void *data) {
89
92
}
90
93
}
91
94
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)});
93
134
}
94
135
95
136
// Function to capture the stack trace of a single isolate
@@ -116,12 +157,11 @@ void CaptureStackTraces(const FunctionCallbackInfo<Value> &args) {
116
157
if (thread_isolate == capture_from_isolate)
117
158
continue ;
118
159
auto thread_name = thread_info.thread_name ;
119
- auto state = thread_info.state ;
120
160
121
161
futures.emplace_back (std::async (
122
162
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)};
125
165
},
126
166
thread_isolate));
127
167
}
@@ -137,9 +177,9 @@ void CaptureStackTraces(const FunctionCallbackInfo<Value> &args) {
137
177
.ToLocalChecked ();
138
178
139
179
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];
143
183
Local<Object> frameObj = Object::New (capture_from_isolate);
144
184
frameObj
145
185
->Set (current_context,
@@ -189,9 +229,10 @@ void CaptureStackTraces(const FunctionCallbackInfo<Value> &args) {
189
229
jsFrames)
190
230
.Check ();
191
231
192
- if (!result.state .empty ()) {
232
+ if (!result.stack_trace . state .empty ()) {
193
233
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 );
195
236
if (!stateStr.IsEmpty ()) {
196
237
v8::MaybeLocal<v8::Value> maybeStateVal =
197
238
v8::JSON::Parse (current_context, stateStr.ToLocalChecked ());
@@ -243,8 +284,7 @@ void RegisterThread(const FunctionCallbackInfo<Value> &args) {
243
284
std::lock_guard<std::mutex> lock (threads_mutex);
244
285
auto found = threads.find (isolate);
245
286
if (found == threads.end ()) {
246
- threads.emplace (isolate,
247
- ThreadInfo{thread_name, milliseconds::zero (), " " });
287
+ threads.emplace (isolate, ThreadInfo{thread_name, milliseconds::zero ()});
248
288
// Register a cleanup hook to remove this thread when the isolate is
249
289
// destroyed
250
290
node::AddEnvironmentCleanupHook (isolate, Cleanup, isolate);
@@ -280,32 +320,17 @@ steady_clock::time_point GetUnbiasedMonotonicTime() {
280
320
// Function to track a thread and set its state
281
321
void ThreadPoll (const FunctionCallbackInfo<Value> &args) {
282
322
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
- }
297
323
298
324
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);
301
327
}
302
328
303
329
{
304
330
std::lock_guard<std::mutex> lock (threads_mutex);
305
331
auto found = threads.find (isolate);
306
332
if (found != threads.end ()) {
307
333
auto &thread_info = found->second ;
308
- thread_info.state = state_str;
309
334
if (disable_last_seen) {
310
335
thread_info.last_seen = milliseconds::zero ();
311
336
} else {
0 commit comments