Skip to content

Commit 94f8a92

Browse files
joyeecheungUlisesGascon
authored andcommitted
src: print more information in C++ assertions
This patch: - Introduce an internal GetCurrentStackTrace() utility to get the current JavaScript stack trace with best effort. - Indent the assertion message so that is separated from the native stack trace for redability - Print the JS stack trace when it's available Previoiusly the abort message looks like this: ``` out/Release/node[24458]: ../../src/node_file.cc:2008:void node::fs::Ope n(const FunctionCallbackInfo<v8::Value> &): Assertion `(argc) >= (3)' f ailed. 1: 0x1043fb9a4 node::Abort() [node] 2: 0x1043fb6e4 node::PrintCaughtException(v8::Isolate*, v8::Local<v8:: Context>, v8::TryCatch const&) [node] 3: 0x104407708 node::fs::Open(v8::FunctionCallbackInfo<v8::Value> cons t&) [node] 4: 0x104611e74 v8::internal::MaybeHandle<v8::internal::Object> v8::int ernal::(anonymous namespace)::HandleApiCallHelper<false>(v8::internal:: Isolate*, v8::internal::Handle<v8::internal::HeapObject >, v8::internal::Handle<v8::internal::FunctionTemplateInfo>, v8::intern al::Handle<v8::internal::Object>, unsigned long*, int) [node ] 5: 0x1046116c8 v8::internal::Builtin_HandleApiCall(int, unsigned long* , v8::internal::Isolate*) [node] 6: 0x104e9cb24 Builtins_CEntry_Return1_ArgvOnStack_BuiltinExit [node] 7: 0x104e143e4 Builtins_InterpreterEntryTrampoline [node] 8: 0x104e1250c Builtins_JSEntryTrampoline [node] 9: 0x104e121f4 Builtins_JSEntry [node] 10: 0x1046ed54c v8::internal::(anonymous namespace)::Invoke(v8::interna l::Isolate*, v8::internal::(anonymous namespace)::InvokeParams const&) [node] 11: 0x1046edb60 v8::internal::Execution::CallScript(v8::internal::Isola te*, v8::internal::Handle<v8::internal::JSFunction>, v8::internal::Hand le<v8::internal::Object>, v8::internal::Handle<v8::in ternal::Object>) [node] 12: 0x1045a9fa0 v8::Script::Run(v8::Local<v8::Context>, v8::Local<v8::D ata>) [node] 13: 0x1043efb68 node::contextify::ContextifyScript::EvalMachine(v8::Loc al<v8::Context>, node::Environment*, long long, bool, bool, bool, v8::M icrotaskQueue*, v8::FunctionCallbackInfo<v8::Value> const&) [node ] 14: 0x1043ef3e0 node::contextify::ContextifyScript::RunInContext(v8::Fu nctionCallbackInfo<v8::Value> const&) [node] 15: 0x104611e74 v8::internal::MaybeHandle<v8::internal::Object> v8::int ernal::(anonymous namespace)::HandleApiCallHelper<false>(v8::internal:: Isolate*, v8::internal::Handle<v8::internal::HeapObject> , v8::internal::Handle<v8::internal::FunctionTemplateInfo>, v8::interna l::Handle<v8::internal::Object>, unsigned long*, int) [node ] 16: 0x1046116c8 v8::internal::Builtin_HandleApiCall(int, unsigned long* , v8::internal::Isolate*) [node] 17: 0x104e9cb24 Builtins_CEntry_Return1_ArgvOnStack_BuiltinExit [node] 18: 0x104e143e4 Builtins_InterpreterEntryTrampoline [node] 19: 0x104e143e4 Builtins_InterpreterEntryTrampoline [node] 20: 0x104e143e4 Builtins_InterpreterEntryTrampoline [node] 21: 0x104e143e4 Builtins_InterpreterEntryTrampoline [node] 22: 0x104e143e4 Builtins_InterpreterEntryTrampoline [node] 23: 0x104e143e4 Builtins_InterpreterEntryTrampoline [node] 24: 0x104e143e4 Builtins_InterpreterEntryTrampoline [node] 25: 0x104e1250c Builtins_JSEntryTrampoline [node] 26: 0x104e121f4 Builtins_JSEntry [node] 27: 0x1046ed54c v8::internal::(anonymous namespace)::Invoke(v8::interna l::Isolate*, v8::internal::(anonymous namespace)::InvokeParams const&) [node] 28: 0x1046ecdc8 v8::internal::Execution::Call(v8::internal::Isolate*, v 8::internal::Handle<v8::internal::Object>, v8::internal::Handle<v8::int ernal::Object>, int, v8::internal::Handle<v 8::internal::Object>*) [node] 29: 0x1045be23c v8::Function::Call(v8::Local<v8::Context>, v8::Local<v8 ::Value>, int, v8::Local<v8::Value>*) [node] 30: 0x1043df704 node::builtins::BuiltinLoader::CompileAndCall(v8::Local <v8::Context>, char const*, node::Realm*) [node] 31: 0x10446f2d4 node::Realm::ExecuteBootstrapper(char const*) [node] 32: 0x1043c3378 node::StartExecution(node::Environment*, std::__1::func tion<v8::MaybeLocal<v8::Value> (node::StartExecutionCallbackInfo const& )>) [node] 33: 0x10432dc28 node::LoadEnvironment(node::Environment*, std::__1::fun ction<v8::MaybeLocal<v8::Value> (node::StartExecutionCallbackInfo const &)>) [node] 34: 0x10443d1f4 node::NodeMainInstance::Run(node::ExitCode*, node::Envi ronment*) [node] 35: 0x10443cfd0 node::NodeMainInstance::Run() [node] 36: 0x1043c5d18 node::Start(int, char**) [node] 37: 0x19a027f28 start [/usr/lib/dyld] [1] 24458 abort out/Release/node -p "process.binding('fs').open ()" ``` Now it looks like this: ``` # out/Release/node[24856]: void node::fs::Open(const FunctionCallbac kInfo<v8::Value> &) at ../../src/node_file.cc:2008 # Assertion failed: (argc) >= (3) ----- Native stack trace ----- 1: 0x1001efe64 node::Abort() [node] 2: 0x1001efba4 node::PrintCaughtException(v8::Isolate*, v8::Local<v8:: Context>, v8::TryCatch const&) [node] 3: 0x1001fb868 node::fs::Open(v8::FunctionCallbackInfo<v8::Value> cons t&) [node] 4: 0x100405fd4 v8::internal::MaybeHandle<v8::internal::Object> v8::int ernal::(anonymous namespace)::HandleApiCallHelper<false>(v8::internal:: Isolate*, v8::internal::Handle<v8::internal::HeapObject >, v8::internal::Handle<v8::internal::FunctionTemplateInfo>, v8::intern al::Handle<v8::internal::Object>, unsigned long*, int) [node ] 5: 0x100405828 v8::internal::Builtin_HandleApiCall(int, unsigned long* , v8::internal::Isolate*) [node] 6: 0x100c90b24 Builtins_CEntry_Return1_ArgvOnStack_BuiltinExit [node] 7: 0x100c083e4 Builtins_InterpreterEntryTrampoline [node] 8: 0x100c0650c Builtins_JSEntryTrampoline [node] 9: 0x100c061f4 Builtins_JSEntry [node] 10: 0x1004e16ac v8::internal::(anonymous namespace)::Invoke(v8::interna l::Isolate*, v8::internal::(anonymous namespace)::InvokeParams const&) [node] 11: 0x1004e1cc0 v8::internal::Execution::CallScript(v8::internal::Isola te*, v8::internal::Handle<v8::internal::JSFunction>, v8::internal::Hand le<v8::internal::Object>, v8::internal::Handle<v8::in ternal::Object>) [node] 12: 0x10039e100 v8::Script::Run(v8::Local<v8::Context>, v8::Local<v8::D ata>) [node] 13: 0x1001e4028 node::contextify::ContextifyScript::EvalMachine(v8::Loc al<v8::Context>, node::Environment*, long long, bool, bool, bool, v8::M icrotaskQueue*, v8::FunctionCallbackInfo<v8::Value> const&) [node ] 14: 0x1001e38a0 node::contextify::ContextifyScript::RunInContext(v8::Fu nctionCallbackInfo<v8::Value> const&) [node] 15: 0x100405fd4 v8::internal::MaybeHandle<v8::internal::Object> v8::int ernal::(anonymous namespace)::HandleApiCallHelper<false>(v8::internal:: Isolate*, v8::internal::Handle<v8::internal::HeapObject> , v8::internal::Handle<v8::internal::FunctionTemplateInfo>, v8::interna l::Handle<v8::internal::Object>, unsigned long*, int) [node ] 16: 0x100405828 v8::internal::Builtin_HandleApiCall(int, unsigned long* , v8::internal::Isolate*) [node] 17: 0x100c90b24 Builtins_CEntry_Return1_ArgvOnStack_BuiltinExit [node] 18: 0x100c083e4 Builtins_InterpreterEntryTrampoline [node] 19: 0x100c083e4 Builtins_InterpreterEntryTrampoline [node] 20: 0x100c083e4 Builtins_InterpreterEntryTrampoline [node] 21: 0x100c083e4 Builtins_InterpreterEntryTrampoline [node] 22: 0x100c083e4 Builtins_InterpreterEntryTrampoline [node] 23: 0x100c083e4 Builtins_InterpreterEntryTrampoline [node] 24: 0x100c083e4 Builtins_InterpreterEntryTrampoline [node] 25: 0x100c0650c Builtins_JSEntryTrampoline [node] 26: 0x100c061f4 Builtins_JSEntry [node] 27: 0x1004e16ac v8::internal::(anonymous namespace)::Invoke(v8::interna l::Isolate*, v8::internal::(anonymous namespace)::InvokeParams const&) [node] 28: 0x1004e0f28 v8::internal::Execution::Call(v8::internal::Isolate*, v 8::internal::Handle<v8::internal::Object>, v8::internal::Handle<v8::int ernal::Object>, int, v8::internal::Handle<v 8::internal::Object>*) [node] 29: 0x1003b239c v8::Function::Call(v8::Local<v8::Context>, v8::Local<v8 ::Value>, int, v8::Local<v8::Value>*) [node] 30: 0x1001d3bc4 node::builtins::BuiltinLoader::CompileAndCall(v8::Local <v8::Context>, char const*, node::Realm*) [node] 31: 0x100263434 node::Realm::ExecuteBootstrapper(char const*) [node] 32: 0x1001b7838 node::StartExecution(node::Environment*, std::__1::func tion<v8::MaybeLocal<v8::Value> (node::StartExecutionCallbackInfo const& )>) [node] 33: 0x100121c28 node::LoadEnvironment(node::Environment*, std::__1::fun ction<v8::MaybeLocal<v8::Value> (node::StartExecutionCallbackInfo const &)>) [node] 34: 0x100231354 node::NodeMainInstance::Run(node::ExitCode*, node::Envi ronment*) [node] 35: 0x100231130 node::NodeMainInstance::Run() [node] 36: 0x1001ba1d8 node::Start(int, char**) [node] 37: 0x19a027f28 start [/usr/lib/dyld] ----- JavaScript stack trace ----- 1: [eval]:1:23 2: runScriptInThisContext (node:internal/vm:144:10) 3: node:internal/process/execution:109:14 4: [eval]-wrapper:6:24 5: runScript (node:internal/process/execution:92:62) 6: evalScript (node:internal/process/execution:123:10) 7: node:internal/main/eval_string:51:3 [1] 24856 abort out/Release/node -p "process.binding('fs').open ()" ``` PR-URL: #50242 Reviewed-By: Chengzhong Wu <[email protected]> Reviewed-By: Stephen Belanger <[email protected]>
1 parent 77e8361 commit 94f8a92

13 files changed

+176
-58
lines changed

src/debug_utils.cc

+23-2
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,9 @@ namespace per_process {
5959
EnabledDebugList enabled_debug_list;
6060
}
6161

62+
using v8::Local;
63+
using v8::StackTrace;
64+
6265
void EnabledDebugList::Parse(std::shared_ptr<KVStore> env_vars,
6366
v8::Isolate* isolate) {
6467
std::string cats;
@@ -303,7 +306,8 @@ std::string NativeSymbolDebuggingContext::SymbolInfo::Display() const {
303306
return oss.str();
304307
}
305308

306-
void DumpBacktrace(FILE* fp) {
309+
void DumpNativeBacktrace(FILE* fp) {
310+
fprintf(fp, "----- Native stack trace -----\n\n");
307311
auto sym_ctx = NativeSymbolDebuggingContext::New();
308312
void* frames[256];
309313
const int size = sym_ctx->GetStackTrace(frames, arraysize(frames));
@@ -314,6 +318,22 @@ void DumpBacktrace(FILE* fp) {
314318
}
315319
}
316320

321+
void DumpJavaScriptBacktrace(FILE* fp) {
322+
v8::Isolate* isolate = v8::Isolate::GetCurrent();
323+
if (isolate == nullptr) {
324+
return;
325+
}
326+
327+
Local<StackTrace> stack;
328+
if (!GetCurrentStackTrace(isolate).ToLocal(&stack)) {
329+
return;
330+
}
331+
332+
fprintf(fp, "\n----- JavaScript stack trace -----\n\n");
333+
PrintStackTrace(isolate, stack, StackTracePrefix::kNumber);
334+
fprintf(fp, "\n");
335+
}
336+
317337
void CheckedUvLoopClose(uv_loop_t* loop) {
318338
if (uv_loop_close(loop) == 0) return;
319339

@@ -514,5 +534,6 @@ void FWrite(FILE* file, const std::string& str) {
514534
} // namespace node
515535

516536
extern "C" void __DumpBacktrace(FILE* fp) {
517-
node::DumpBacktrace(fp);
537+
node::DumpNativeBacktrace(fp);
538+
node::DumpJavaScriptBacktrace(fp);
518539
}

src/env.cc

+2-1
Original file line numberDiff line numberDiff line change
@@ -1662,7 +1662,8 @@ void AsyncHooks::FailWithCorruptedAsyncStack(double expected_async_id) {
16621662
"actual: %.f, expected: %.f)\n",
16631663
async_id_fields_.GetValue(kExecutionAsyncId),
16641664
expected_async_id);
1665-
DumpBacktrace(stderr);
1665+
DumpNativeBacktrace(stderr);
1666+
DumpJavaScriptBacktrace(stderr);
16661667
fflush(stderr);
16671668
// TODO(joyeecheung): should this exit code be more specific?
16681669
if (!env()->abort_on_uncaught_exception()) Exit(ExitCode::kGenericUserError);

src/node_errors.cc

+67-14
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
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 {
1516
using errors::TryCatchScope;
1617
using v8::Boolean;
1718
using v8::Context;
19+
using v8::EscapableHandleScope;
1820
using v8::Exception;
1921
using v8::Function;
2022
using 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

246294
std::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";

src/node_internals.h

+11-1
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,17 @@ void GetSockOrPeerName(const v8::FunctionCallbackInfo<v8::Value>& args) {
7979
args.GetReturnValue().Set(err);
8080
}
8181

82-
void PrintStackTrace(v8::Isolate* isolate, v8::Local<v8::StackTrace> stack);
82+
constexpr int kMaxFrameCountForLogging = 10;
83+
v8::MaybeLocal<v8::StackTrace> GetCurrentStackTrace(
84+
v8::Isolate* isolate, int frame_count = kMaxFrameCountForLogging);
85+
86+
enum class StackTracePrefix {
87+
kAt, // " at "
88+
kNumber
89+
};
90+
void PrintStackTrace(v8::Isolate* isolate,
91+
v8::Local<v8::StackTrace> stack,
92+
StackTracePrefix prefix = StackTracePrefix::kAt);
8393
void PrintCaughtException(v8::Isolate* isolate,
8494
v8::Local<v8::Context> context,
8595
const v8::TryCatch& try_catch);

src/node_platform.cc

+1-1
Original file line numberDiff line numberDiff line change
@@ -560,7 +560,7 @@ v8::TracingController* NodePlatform::GetTracingController() {
560560
Platform::StackTracePrinter NodePlatform::GetStackTracePrinter() {
561561
return []() {
562562
fprintf(stderr, "\n");
563-
DumpBacktrace(stderr);
563+
DumpNativeBacktrace(stderr);
564564
fflush(stderr);
565565
};
566566
}

src/node_report.cc

+4-17
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@
2727
constexpr int NODE_REPORT_VERSION = 3;
2828
constexpr int NANOS_PER_SEC = 1000 * 1000 * 1000;
2929
constexpr double SEC_PER_MICROS = 1e-6;
30-
constexpr int MAX_FRAME_COUNT = 10;
30+
constexpr int MAX_FRAME_COUNT = node::kMaxFrameCountForLogging;
3131

3232
namespace node {
3333
using node::worker::Worker;
@@ -458,14 +458,13 @@ static void PrintEmptyJavaScriptStack(JSONWriter* writer) {
458458
static void PrintJavaScriptStack(JSONWriter* writer,
459459
Isolate* isolate,
460460
const char* trigger) {
461-
// Can not capture the stacktrace when the isolate is in a OOM state or no
462-
// context is entered.
463-
if (!strcmp(trigger, "OOMError") || !isolate->InContext()) {
461+
HandleScope scope(isolate);
462+
Local<v8::StackTrace> stack;
463+
if (!GetCurrentStackTrace(isolate, MAX_FRAME_COUNT).ToLocal(&stack)) {
464464
PrintEmptyJavaScriptStack(writer);
465465
return;
466466
}
467467

468-
HandleScope scope(isolate);
469468
RegisterState state;
470469
state.pc = nullptr;
471470
state.fp = &state;
@@ -476,18 +475,6 @@ static void PrintJavaScriptStack(JSONWriter* writer,
476475
void* samples[MAX_FRAME_COUNT];
477476
isolate->GetStackSample(state, samples, MAX_FRAME_COUNT, &info);
478477

479-
constexpr StackTrace::StackTraceOptions stack_trace_options =
480-
static_cast<StackTrace::StackTraceOptions>(
481-
StackTrace::kDetailed |
482-
StackTrace::kExposeFramesAcrossSecurityOrigins);
483-
Local<StackTrace> stack = StackTrace::CurrentStackTrace(
484-
isolate, MAX_FRAME_COUNT, stack_trace_options);
485-
486-
if (stack->GetFrameCount() == 0) {
487-
PrintEmptyJavaScriptStack(writer);
488-
return;
489-
}
490-
491478
writer->json_keyvalue("message", trigger);
492479
writer->json_arraystart("stack");
493480
for (int i = 0; i < stack->GetFrameCount(); i++) {

src/util.h

+2-1
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,8 @@ struct AssertionInfo {
115115
};
116116
[[noreturn]] void NODE_EXTERN_PRIVATE Assert(const AssertionInfo& info);
117117
[[noreturn]] void NODE_EXTERN_PRIVATE Abort();
118-
void DumpBacktrace(FILE* fp);
118+
void DumpNativeBacktrace(FILE* fp);
119+
void DumpJavaScriptBacktrace(FILE* fp);
119120

120121
// Windows 8+ does not like abort() in Release mode
121122
#ifdef _WIN32

test/abort/test-abort-backtrace.js

+14-11
Original file line numberDiff line numberDiff line change
@@ -10,19 +10,22 @@ if (process.argv[2] === 'child') {
1010
const stderr = child.stderr.toString();
1111

1212
assert.strictEqual(child.stdout.toString(), '');
13-
// Stderr will be empty for systems that don't support backtraces.
14-
if (stderr !== '') {
15-
const frames = stderr.trimRight().split('\n').map((s) => s.trim());
13+
const { nativeStack, jsStack } = common.getPrintedStackTrace(stderr);
1614

17-
if (!frames.every((frame, index) => frame.startsWith(`${index + 1}:`))) {
18-
assert.fail(`Each frame should start with a frame number:\n${stderr}`);
19-
}
15+
if (!nativeStack.every((frame, index) => frame.startsWith(`${index + 1}:`))) {
16+
assert.fail(`Each frame should start with a frame number:\n${stderr}`);
17+
}
2018

21-
if (!common.isWindows) {
22-
const { getBinaryPath } = require('../common/shared-lib-util');
23-
if (!frames.some((frame) => frame.includes(`[${getBinaryPath()}]`))) {
24-
assert.fail(`Some frames should include the binary name:\n${stderr}`);
25-
}
19+
// For systems that don't support backtraces, the native stack is
20+
// going to be empty.
21+
if (!common.isWindows && nativeStack.length > 0) {
22+
const { getBinaryPath } = require('../common/shared-lib-util');
23+
if (!nativeStack.some((frame) => frame.includes(`[${getBinaryPath()}]`))) {
24+
assert.fail(`Some native stack frame include the binary name:\n${stderr}`);
2625
}
2726
}
27+
28+
if (jsStack.length > 0) {
29+
assert(jsStack.some((frame) => frame.includes(__filename)));
30+
}
2831
}

test/abort/test-abort-fatal-error.js

+1-6
Original file line numberDiff line numberDiff line change
@@ -38,10 +38,5 @@ exec(cmdline, function(err, stdout, stderr) {
3838
assert(false, 'this test should fail');
3939
}
4040

41-
if (err.code !== 134 && err.signal !== 'SIGABRT') {
42-
console.log(stdout);
43-
console.log(stderr);
44-
console.log(err);
45-
assert(false, err);
46-
}
41+
assert(common.nodeProcessAborted(err.code, err.signal));
4742
});

test/abort/test-addon-uv-handle-leak.js

+9-2
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,9 @@ if (process.argv[2] === 'child') {
8888

8989
while (lines.length > 0) {
9090
const line = lines.shift().trim();
91+
if (line.length === 0) {
92+
continue; // Skip empty lines.
93+
}
9194

9295
switch (state) {
9396
case 'initial':
@@ -96,7 +99,7 @@ if (process.argv[2] === 'child') {
9699
break;
97100
case 'handle-start':
98101
if (/^uv loop at \[.+\] has \d+ open handles in total$/.test(line)) {
99-
state = 'assertion-failure';
102+
state = 'source-line';
100103
break;
101104
}
102105
assert.match(line, /^\[.+\] timer( \(active\))?$/);
@@ -116,8 +119,12 @@ if (process.argv[2] === 'child') {
116119
}
117120
state = 'handle-start';
118121
break;
122+
case 'source-line':
123+
assert.match(line, /CheckedUvLoopClose/);
124+
state = 'assertion-failure';
125+
break;
119126
case 'assertion-failure':
120-
assert.match(line, /Assertion .+ failed/);
127+
assert.match(line, /Assertion failed:/);
121128
state = 'done';
122129
break;
123130
case 'done':

0 commit comments

Comments
 (0)