11#include < cerrno>
22#include < cstdarg>
3+ #include < sstream>
34
45#include " debug_utils-inl.h"
56#include " node_errors.h"
@@ -15,6 +16,7 @@ namespace node {
1516using errors::TryCatchScope;
1617using v8::Boolean;
1718using v8::Context;
19+ using v8::EscapableHandleScope;
1820using v8::Exception;
1921using v8::Function;
2022using v8::FunctionCallbackInfo;
@@ -185,23 +187,65 @@ static std::string GetErrorSource(Isolate* isolate,
185187 return buf + std::string (underline_buf, off);
186188}
187189
188- static std::string FormatStackTrace (Isolate* isolate, Local<StackTrace> stack) {
190+ static std::atomic<bool > is_in_oom{false };
191+ static std::atomic<bool > is_retrieving_js_stacktrace{false };
192+ MaybeLocal<StackTrace> GetCurrentStackTrace (Isolate* isolate, int frame_count) {
193+ if (isolate == nullptr ) {
194+ return MaybeLocal<StackTrace>();
195+ }
196+ // Generating JavaScript stack trace can result in V8 fatal error,
197+ // which can re-enter this function.
198+ if (is_retrieving_js_stacktrace.load ()) {
199+ return MaybeLocal<StackTrace>();
200+ }
201+
202+ // Can not capture the stacktrace when the isolate is in a OOM state or no
203+ // context is entered.
204+ if (is_in_oom.load () || !isolate->InContext ()) {
205+ return MaybeLocal<StackTrace>();
206+ }
207+
208+ constexpr StackTrace::StackTraceOptions options =
209+ static_cast <StackTrace::StackTraceOptions>(
210+ StackTrace::kDetailed |
211+ StackTrace::kExposeFramesAcrossSecurityOrigins );
212+
213+ is_retrieving_js_stacktrace.store (true );
214+ EscapableHandleScope scope (isolate);
215+ Local<StackTrace> stack =
216+ StackTrace::CurrentStackTrace (isolate, frame_count, options);
217+
218+ is_retrieving_js_stacktrace.store (false );
219+ if (stack->GetFrameCount () == 0 ) {
220+ return MaybeLocal<StackTrace>();
221+ }
222+
223+ return scope.Escape (stack);
224+ }
225+
226+ static std::string FormatStackTrace (
227+ Isolate* isolate,
228+ Local<StackTrace> stack,
229+ StackTracePrefix prefix = StackTracePrefix::kAt ) {
189230 std::string result;
190231 for (int i = 0 ; i < stack->GetFrameCount (); i++) {
191232 Local<StackFrame> stack_frame = stack->GetFrame (isolate, i);
192233 node::Utf8Value fn_name_s (isolate, stack_frame->GetFunctionName ());
193234 node::Utf8Value script_name (isolate, stack_frame->GetScriptName ());
194235 const int line_number = stack_frame->GetLineNumber ();
195236 const int column = stack_frame->GetColumn ();
196-
237+ std::string prefix_str = prefix == StackTracePrefix::kAt
238+ ? " at "
239+ : std::to_string (i + 1 ) + " : " ;
197240 if (stack_frame->IsEval ()) {
198241 if (stack_frame->GetScriptId () == Message::kNoScriptIdInfo ) {
199- result += SPrintF (" at [eval]:%i:%i\n " , line_number, column);
242+ result += SPrintF (" %s [eval]:%i:%i\n " , prefix_str , line_number, column);
200243 } else {
201244 std::vector<char > buf (script_name.length () + 64 );
202245 snprintf (buf.data (),
203246 buf.size (),
204- " at [eval] (%s:%i:%i)\n " ,
247+ " %s[eval] (%s:%i:%i)\n " ,
248+ prefix_str.c_str (),
205249 *script_name,
206250 line_number,
207251 column);
@@ -214,7 +258,8 @@ static std::string FormatStackTrace(Isolate* isolate, Local<StackTrace> stack) {
214258 std::vector<char > buf (script_name.length () + 64 );
215259 snprintf (buf.data (),
216260 buf.size (),
217- " at %s:%i:%i\n " ,
261+ " %s%s:%i:%i\n " ,
262+ prefix_str.c_str (),
218263 *script_name,
219264 line_number,
220265 column);
@@ -223,7 +268,8 @@ static std::string FormatStackTrace(Isolate* isolate, Local<StackTrace> stack) {
223268 std::vector<char > buf (fn_name_s.length () + script_name.length () + 64 );
224269 snprintf (buf.data (),
225270 buf.size (),
226- " at %s (%s:%i:%i)\n " ,
271+ " %s%s (%s:%i:%i)\n " ,
272+ prefix_str.c_str (),
227273 *fn_name_s,
228274 *script_name,
229275 line_number,
@@ -239,8 +285,10 @@ static void PrintToStderrAndFlush(const std::string& str) {
239285 fflush (stderr);
240286}
241287
242- void PrintStackTrace (Isolate* isolate, Local<StackTrace> stack) {
243- PrintToStderrAndFlush (FormatStackTrace (isolate, stack));
288+ void PrintStackTrace (Isolate* isolate,
289+ Local<StackTrace> stack,
290+ StackTracePrefix prefix) {
291+ PrintToStderrAndFlush (FormatStackTrace (isolate, stack, prefix));
244292}
245293
246294std::string FormatCaughtException (Isolate* isolate,
@@ -329,7 +377,8 @@ void AppendExceptionLine(Environment* env,
329377}
330378
331379[[noreturn]] void Abort () {
332- DumpBacktrace (stderr);
380+ DumpNativeBacktrace (stderr);
381+ DumpJavaScriptBacktrace (stderr);
333382 fflush (stderr);
334383 ABORT_NO_BACKTRACE ();
335384}
@@ -338,14 +387,15 @@ void AppendExceptionLine(Environment* env,
338387 std::string name = GetHumanReadableProcessName ();
339388
340389 fprintf (stderr,
341- " %s: %s:%s%s Assertion `%s' failed.\n " ,
390+ " \n "
391+ " # %s: %s at %s\n "
392+ " # Assertion failed: %s\n\n " ,
342393 name.c_str (),
343- info.file_line ,
344- info.function ,
345- *info.function ? " :" : " " ,
394+ info.function ? info.function : " (unknown function)" ,
395+ info.file_line ? info.file_line : " (unknown source location)" ,
346396 info.message );
347- fflush (stderr);
348397
398+ fflush (stderr);
349399 Abort ();
350400}
351401
@@ -528,6 +578,9 @@ static void ReportFatalException(Environment* env,
528578
529579[[noreturn]] void OOMErrorHandler (const char * location,
530580 const v8::OOMDetails& details) {
581+ // We should never recover from this handler so once it's true it's always
582+ // true.
583+ is_in_oom.store (true );
531584 const char * message =
532585 details.is_heap_oom ? " Allocation failed - JavaScript heap out of memory"
533586 : " Allocation failed - process out of memory" ;
0 commit comments