[vm] Improve crash output from gen_snapshot

* Don't compile it with omit frame pointer even in PRODUCT.
* Dump the first frame even if we give up on unwinding.
* Dump compiler state even if we give up on unwinding.
* Don't abort stack dump if PC for the outermost frame is 0 (can happen
when calling pure virtual method for example).

TEST=manually tested by making compiler crash during code generation

Change-Id: Iddeafbdad8e8588f19197d5dd49af14b8fc63134
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/278341
Reviewed-by: Alexander Markov <alexmarkov@google.com>
Commit-Queue: Slava Egorov <vegorov@google.com>
This commit is contained in:
Vyacheslav Egorov 2023-01-04 18:14:56 +00:00 committed by Commit Queue
parent 792aedec74
commit 15230e41e5
2 changed files with 40 additions and 10 deletions

View file

@ -83,6 +83,9 @@ config("add_empty_macho_section_config") {
config("dart_precompiler_config") {
defines = []
defines += [ "DART_PRECOMPILER" ]
if (is_clang) {
cflags = [ "-fno-omit-frame-pointer" ]
}
}
config("dart_os_config") {

View file

@ -377,9 +377,9 @@ static bool GetAndValidateThreadStackBounds(OSThread* os_thread,
return ValidateThreadStackBounds(fp, sp, *stack_lower, *stack_upper);
}
// Some simple sanity checking of |pc|, |fp|, and |sp|.
static bool InitialRegisterCheck(uintptr_t pc, uintptr_t fp, uintptr_t sp) {
if ((sp == 0) || (fp == 0) || (pc == 0)) {
// Some simple sanity checking of |fp|, and |sp|.
static bool InitialStackRegistersCheck(uintptr_t fp, uintptr_t sp) {
if ((sp == 0) || (fp == 0)) {
// None of these registers should be zero.
return false;
}
@ -393,6 +393,17 @@ static bool InitialRegisterCheck(uintptr_t pc, uintptr_t fp, uintptr_t sp) {
return true;
}
#if !defined(PRODUCT)
// Some simple sanity checking of |pc|, |fp|, and |sp|.
static bool InitialRegisterCheck(uintptr_t pc, uintptr_t fp, uintptr_t sp) {
if (pc == 0) {
return false;
}
return InitialStackRegistersCheck(fp, sp);
}
#endif // !defined(PRODUCT)
void Profiler::DumpStackTrace(void* context) {
if (context == NULL) {
DumpStackTrace(/*for_crash=*/true);
@ -444,6 +455,15 @@ void Profiler::DumpStackTrace(bool for_crash) {
DumpStackTrace(sp, fp, pc, for_crash);
}
static void DumpCompilerState(Thread* thread) {
#if !defined(DART_PRECOMPILED_RUNTIME)
if (thread != nullptr && thread->execution_state() == Thread::kThreadInVM &&
thread->HasCompilerState()) {
thread->compiler_state().ReportCrash();
}
#endif
}
void Profiler::DumpStackTrace(uword sp, uword fp, uword pc, bool for_crash) {
if (for_crash) {
// Allow only one stack trace to prevent recursively printing stack traces
@ -496,9 +516,15 @@ void Profiler::DumpStackTrace(uword sp, uword fp, uword pc, bool for_crash) {
vm_source == nullptr
? 0
: reinterpret_cast<uword>(vm_source->snapshot_instructions));
OS::PrintErr("fp=%" Px ", sp=%" Px ", pc=%" Px "\n", fp, sp, pc);
if (!InitialRegisterCheck(pc, fp, sp)) {
OS::PrintErr("Stack dump aborted because InitialRegisterCheck failed.\n");
if (!InitialStackRegistersCheck(fp, sp)) {
OS::PrintErr(
"Stack dump aborted because InitialStackRegistersCheck failed.\n");
if (pc != 0) { // At the very least dump the top frame.
DumpStackFrame(0, pc, fp);
}
DumpCompilerState(thread);
return;
}
@ -508,6 +534,10 @@ void Profiler::DumpStackTrace(uword sp, uword fp, uword pc, bool for_crash) {
&stack_upper)) {
OS::PrintErr(
"Stack dump aborted because GetAndValidateThreadStackBounds failed.\n");
if (pc != 0) { // At the very least dump the top frame.
DumpStackFrame(0, pc, fp);
}
DumpCompilerState(thread);
return;
}
@ -524,13 +554,10 @@ void Profiler::DumpStackTrace(uword sp, uword fp, uword pc, bool for_crash) {
StackFrame::DumpCurrentTrace();
} else if (thread->execution_state() == Thread::kThreadInVM) {
StackFrame::DumpCurrentTrace();
#if !defined(DART_PRECOMPILED_RUNTIME)
if (thread->HasCompilerState()) {
thread->compiler_state().ReportCrash();
}
#endif
}
}
DumpCompilerState(thread);
}
#endif // !defined(PRODUCT) || defined(DART_PRECOMPILER)