1+ #include < node.h>
2+ #include < mutex>
3+
4+ using namespace v8 ;
5+ using namespace node ;
6+
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+
13+ static const int kMaxStackFrames = 255 ;
14+ static const int kMaxStackJsonSize = 10240 ;
15+
16+ static void ExecutionInterrupted (Isolate *isolate, void *data)
17+ {
18+ char *buffer = static_cast <char *>(data);
19+
20+ v8::RegisterState state;
21+ v8::SampleInfo info;
22+ void *samples[kMaxStackFrames ];
23+
24+ uint32_t pos = 0 ;
25+
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 );
34+ if (stack.IsEmpty ())
35+ {
36+ snprintf (buffer, kMaxStackJsonSize , " []" );
37+ return ;
38+ }
39+
40+ pos += snprintf (&buffer[pos], kMaxStackJsonSize , " [" );
41+ int count = stack->GetFrameCount ();
42+
43+ for (int i = 0 ; i < count; i++)
44+ {
45+ Local<StackFrame> frame = stack->GetFrame (isolate, i);
46+ Local<String> fn_name = frame->GetFunctionName ();
47+
48+ if (frame->IsEval ())
49+ {
50+ fn_name = String::NewFromUtf8 (isolate, " [eval]" , NewStringType::kInternalized ).ToLocalChecked ();
51+ }
52+ else if (fn_name.IsEmpty () || fn_name->Length () == 0 )
53+ {
54+ fn_name = String::NewFromUtf8 (isolate, " ?" , NewStringType::kInternalized ).ToLocalChecked ();
55+ }
56+ else if (frame->IsConstructor ())
57+ {
58+ fn_name = String::NewFromUtf8 (isolate, " [constructor]" , NewStringType::kInternalized ).ToLocalChecked ();
59+ }
60+
61+ String::Utf8Value function_name (isolate, fn_name);
62+ String::Utf8Value script_name (isolate, frame->GetScriptName ());
63+ const int line_number = frame->GetLineNumber ();
64+ const int column = frame->GetColumn ();
65+
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);
72+
73+ if (i < count - 1 )
74+ {
75+ pos += snprintf (&buffer[pos], kMaxStackJsonSize , " ," );
76+ }
77+ }
78+
79+ pos += snprintf (&buffer[pos], kMaxStackJsonSize , " ]" );
80+
81+ {
82+ std::lock_guard<std::mutex> lock (interrupt_mutex);
83+ interrupt_done = true ;
84+ }
85+ interrupt_cv.notify_one ();
86+ }
87+
88+ void CaptureStackTrace (const FunctionCallbackInfo<Value> &args)
89+ {
90+ char buffer[kMaxStackJsonSize ] = {0 };
91+
92+ if (auto isolate = main_thread_isolate)
93+ {
94+ // Reset the interrupt_done flag
95+ {
96+ std::lock_guard<std::mutex> lock (interrupt_mutex);
97+ interrupt_done = false ;
98+ }
99+
100+ isolate->RequestInterrupt (ExecutionInterrupted, buffer);
101+
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; });
106+ }
107+
108+ Local<String> result = String::NewFromUtf8 (args.GetIsolate (), buffer, NewStringType::kNormal ).ToLocalChecked ();
109+ args.GetReturnValue ().Set (result);
110+ }
111+
112+ void SetMainIsolate (const FunctionCallbackInfo<Value> &args)
113+ {
114+ main_thread_isolate = args.GetIsolate ();
115+ }
116+
117+ extern " C" NODE_MODULE_EXPORT void
118+ NODE_MODULE_INITIALIZER (Local<Object> exports,
119+ Local<Value> module ,
120+ Local<Context> context)
121+ {
122+ Isolate *isolate = context->GetIsolate ();
123+
124+ exports->Set (context,
125+ String::NewFromUtf8 (isolate, " captureStackTrace" , NewStringType::kInternalized ).ToLocalChecked (),
126+ FunctionTemplate::New (isolate, CaptureStackTrace)->GetFunction (context).ToLocalChecked ())
127+ .Check ();
128+
129+ exports->Set (context,
130+ String::NewFromUtf8 (isolate, " setMainIsolate" , NewStringType::kInternalized ).ToLocalChecked (),
131+ FunctionTemplate::New (isolate, SetMainIsolate)->GetFunction (context).ToLocalChecked ())
132+ .Check ();
133+ }
0 commit comments