Various crash diagnostic improvements.
Register dumps, restore backtraces on Mac OS where possible, Mac demangling,
more comments, logging when using an abort hook, and more selective use of
abort hooks.
Linux:
*** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
Fatal signal 11 (SIGSEGV), code 1 (SEGV_MAPERR) fault addr 0xdeadd00d
Registers:
eax: 0x00000000 ebx: 0x5598eff4 ecx: 0x56300000 edx: 0x55992de0
edi: 0x5597e478 esi: 0xfff1f3c8 ebp: 0xfff1f3e8 esp: 0xfff1f390
eip: 0x558a82ff eflags: 0x00010246
cs: 0x00000023 ds: 0x0000002b es: 0x0000002b fs: 0x00000007
gs: 0x00000063 ss: 0x0000002b
Backtrace:
#00 art::Backtrace::Dump(std::ostream&)+0x4c [0x559357ac] (libartd.so)
#01 ??+0x3b73b0 [0x559343b0] (libartd.so)
#02 [0x55573410] (???)
#03 art::LogMessage::~LogMessage()+0x3bd [0x557e2e6d] (libartd.so)
#04 ?? [0x8088941] (dex2oatd)
#05 ?? [0x80898f7] (dex2oatd)
#06 __libc_start_main+0xe6 [0x55b06bd6] (libc.so.6)
#07 ?? [0x8085b31] (dex2oatd)
Mac OS:
*** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
Fatal signal 6 (SIGABRT), code 0 (?)
Registers:
eax: 0x00000000 ebx: 0xc008bce0 ecx: 0xc008bc5c edx: 0x906559c6
edi: 0xac0f52c0 esi: 0x00000006 ebp: 0xc008bc78 esp: 0xc008bc5c
eip: 0x906559c6 eflags: 0x00000246
cs: 0x0000000b ds: 0x00000023 es: 0x00000023 fs: 0x0000001f
gs: 0x0000000f ss: 0x00000023
Backtrace:
#00 art::Backtrace::Dump(std::ostream&) + 40 [0x001ce25a] (libartd.dylib)
#01 _ZN3artL22HandleUnexpectedSignalEiP9__siginfoPv + 1051 [0x001cdacb] (libartd.dylib)
#02 _sigtramp + 43 [0x90a4559b] (libsystem_c.dylib)
#03 0x0 + 4294967295 [0xffffffff] (???)
#04 abort + 167 [0x909e0bdd] (libsystem_c.dylib)
#05 art::Runtime::Abort() + 322 [0x00192312] (libartd.dylib)
#06 art::LogMessage::~LogMessage() + 413 [0x00135a6d] (libartd.dylib)
#07 art::dex2oat(int, char**) + 6183 [0x00091927] (dex2oatd)
#08 start + 53 [0x0008fb75] (dex2oatd)
Change-Id: I68d215f09723f236889242cfe8aa8819afea4a60
diff --git a/src/jdwp/jdwp_expand_buf.cc b/src/jdwp/jdwp_expand_buf.cc
index 96c2cfe..307167e 100644
--- a/src/jdwp/jdwp_expand_buf.cc
+++ b/src/jdwp/jdwp_expand_buf.cc
@@ -96,8 +96,7 @@
uint8_t* newPtr = (uint8_t*) realloc(pBuf->storage, pBuf->maxLen);
if (newPtr == NULL) {
- LOG(ERROR) << "realloc(" << pBuf->maxLen << ") failed";
- abort();
+ LOG(FATAL) << "realloc(" << pBuf->maxLen << ") failed";
}
pBuf->storage = newPtr;
diff --git a/src/runtime.cc b/src/runtime.cc
index 3f80260..8fe760e 100644
--- a/src/runtime.cc
+++ b/src/runtime.cc
@@ -161,11 +161,15 @@
// Call the abort hook if we have one.
if (Runtime::Current() != NULL && Runtime::Current()->abort_ != NULL) {
+ LOG(INTERNAL_FATAL) << "Calling abort hook...";
Runtime::Current()->abort_();
// notreached
+ LOG(INTERNAL_FATAL) << "Unexpectedly returned from abort hook!";
}
- // If we call abort(3) on a device, all threads in the process
+ // TODO: finish merging patches to fix abort(3) in bionic, then lose this!
+ // Bionic doesn't implement POSIX semantics for abort(3) in a multi-threaded
+ // process, so if we call abort(3) on a device, all threads in the process
// receive SIGABRT. debuggerd dumps the stack trace of the main
// thread, whether or not that was the thread that failed. By
// stuffing a value into a bogus address, we cause a segmentation
@@ -301,7 +305,10 @@
parsed->hook_vfprintf_ = vfprintf;
parsed->hook_exit_ = exit;
- parsed->hook_abort_ = abort;
+ parsed->hook_abort_ = NULL; // We don't call abort(3) by default; see Runtime::Abort.
+#if defined(__APPLE__)
+ parsed->hook_abort_ = abort; // On the Mac, abort(3) gives better results; see Runtime::InitPlatformSignalHandlers.
+#endif
// gLogVerbosity.class_linker = true; // TODO: don't check this in!
// gLogVerbosity.compiler = true; // TODO: don't check this in!
diff --git a/src/runtime_linux.cc b/src/runtime_linux.cc
index b7e7d01..75540b8 100644
--- a/src/runtime_linux.cc
+++ b/src/runtime_linux.cc
@@ -19,6 +19,7 @@
#include <cxxabi.h>
#include <execinfo.h>
#include <signal.h>
+#include <string.h>
#include "logging.h"
#include "stringprintf.h"
@@ -39,50 +40,90 @@
return result;
}
- return mangled_name + "()";
+ return mangled_name;
}
-static void Backtrace() {
- // Get the raw stack frames.
- size_t MAX_STACK_FRAMES = 64;
- void* frames[MAX_STACK_FRAMES];
- size_t frame_count = backtrace(frames, MAX_STACK_FRAMES);
+struct Backtrace {
+ void Dump(std::ostream& os) {
+ // Get the raw stack frames.
+ size_t MAX_STACK_FRAMES = 128;
+ void* frames[MAX_STACK_FRAMES];
+ size_t frame_count = backtrace(frames, MAX_STACK_FRAMES);
+ if (frame_count == 0) {
+ os << "--- backtrace(3) returned no frames";
+ return;
+ }
- // Turn them into something human-readable with symbols.
- char** symbols = backtrace_symbols(frames, frame_count);
- if (symbols == NULL) {
- PLOG(ERROR) << "backtrace_symbols failed";
- return;
- }
+ // Turn them into something human-readable with symbols.
+ char** symbols = backtrace_symbols(frames, frame_count);
+ if (symbols == NULL) {
+ os << "--- backtrace_symbols(3) failed";
+ return;
+ }
- // backtrace_symbols(3) gives us lines like this:
- // "/usr/local/google/home/enh/a1/out/host/linux-x86/bin/../lib/libartd.so(_ZN3art7Runtime5AbortEPKci+0x15b) [0xf76c5af3]"
- // "[0xf7b62057]"
- // We extract the pieces and demangle, so we can produce output like this:
- // libartd.so:-1] #00 art::Runtime::Abort(char const*, int) +0x15b [0xf770dd51]
+ // Parse the backtrace strings and demangle, so we can produce output like this:
+ // ] #00 art::Runtime::Abort(char const*, int)+0x15b [0xf770dd51] (libartd.so)
+ for (size_t i = 0; i < frame_count; ++i) {
+ std::string text(symbols[i]);
+ std::string filename("???");
+ std::string function_name;
- for (size_t i = 0; i < frame_count; ++i) {
- std::string text(symbols[i]);
- std::string filename("??");
- std::string function_name;
-
- size_t index = text.find('(');
- if (index != std::string::npos) {
+#if defined(__APPLE__)
+ // backtrace_symbols(3) gives us lines like this on Mac OS:
+ // "0 libartd.dylib 0x001cd29a _ZN3art9Backtrace4DumpERSo + 40>"
+ // "3 ??? 0xffffffff 0x0 + 4294967295>"
+ text.erase(0, 4);
+ size_t index = text.find(' ');
filename = text.substr(0, index);
+ text.erase(0, 40 - 4);
+ index = text.find(' ');
+ std::string address(text.substr(0, index));
text.erase(0, index + 1);
-
- index = text.find_first_of("+)");
+ index = text.find(' ');
function_name = Demangle(text.substr(0, index));
text.erase(0, index);
- index = text.find(')');
- text.erase(index, 1);
- }
- std::string log_line(StringPrintf("\t#%02zd ", i) + function_name + text);
- LogMessage(filename.c_str(), -1, INTERNAL_FATAL, -1).stream() << log_line;
- }
+ text += " [" + address + "]";
+#else
+ // backtrace_symbols(3) gives us lines like this on Linux:
+ // "/usr/local/google/home/enh/a1/out/host/linux-x86/bin/../lib/libartd.so(_ZN3art7Runtime5AbortEPKci+0x15b) [0xf76c5af3]"
+ // "[0xf7b62057]"
+ size_t index = text.find('(');
+ if (index != std::string::npos) {
+ filename = text.substr(0, index);
+ text.erase(0, index + 1);
- free(symbols);
+ index = text.find_first_of("+)");
+ function_name = Demangle(text.substr(0, index));
+ text.erase(0, index);
+ index = text.find(')');
+ text.erase(index, 1);
+ }
+#endif
+
+ const char* last_slash = strrchr(filename.c_str(), '/');
+ const char* so_name = (last_slash == NULL) ? filename.c_str() : last_slash + 1;
+ os << StringPrintf("\t#%02zd ", i) << function_name << text << " (" << so_name << ")\n";
+ }
+
+ free(symbols);
+ }
+};
+
+static const char* GetSignalName(int signal_number) {
+ switch (signal_number) {
+ case SIGABRT: return "SIGABRT";
+ case SIGBUS: return "SIGBUS";
+ case SIGFPE: return "SIGFPE";
+ case SIGILL: return "SIGILL";
+ case SIGPIPE: return "SIGPIPE";
+ case SIGSEGV: return "SIGSEGV";
+#if defined(STIGSTLFKT)
+ case SIGSTKFLT: return "SIGSTKFLT";
+#endif
+ case SIGTRAP: return "SIGTRAP";
+ }
+ return "??";
}
static const char* GetSignalCodeName(int signal_number, int signal_code) {
@@ -153,43 +194,84 @@
return "?";
}
-static void HandleUnexpectedSignal(int signal_number, siginfo_t* info, void*) {
- const char* signal_name = "?";
- bool has_address = false;
- if (signal_number == SIGILL) {
- signal_name = "SIGILL";
- has_address = true;
- } else if (signal_number == SIGTRAP) {
- signal_name = "SIGTRAP";
- } else if (signal_number == SIGABRT) {
- signal_name = "SIGABRT";
- } else if (signal_number == SIGBUS) {
- signal_name = "SIGBUS";
- has_address = true;
- } else if (signal_number == SIGFPE) {
- signal_name = "SIGFPE";
- has_address = true;
- } else if (signal_number == SIGSEGV) {
- signal_name = "SIGSEGV";
- has_address = true;
-#if defined(SIGSTKFLT)
- } else if (signal_number == SIGSTKFLT) {
- signal_name = "SIGSTKFLT";
+struct UContext {
+ UContext(void* raw_context) : context(reinterpret_cast<ucontext_t*>(raw_context)->uc_mcontext) {}
+
+ void Dump(std::ostream& os) {
+ // TODO: support non-x86 hosts (not urgent because this code doesn't run on targets).
+#if defined(__APPLE__)
+ DumpRegister32(os, "eax", context->__ss.__eax);
+ DumpRegister32(os, "ebx", context->__ss.__ebx);
+ DumpRegister32(os, "ecx", context->__ss.__ecx);
+ DumpRegister32(os, "edx", context->__ss.__edx);
+ os << '\n';
+
+ DumpRegister32(os, "edi", context->__ss.__edi);
+ DumpRegister32(os, "esi", context->__ss.__esi);
+ DumpRegister32(os, "ebp", context->__ss.__ebp);
+ DumpRegister32(os, "esp", context->__ss.__esp);
+ os << '\n';
+
+ DumpRegister32(os, "eip", context->__ss.__eip);
+ DumpRegister32(os, "eflags", context->__ss.__eflags);
+ os << '\n';
+
+ DumpRegister32(os, "cs", context->__ss.__cs);
+ DumpRegister32(os, "ds", context->__ss.__ds);
+ DumpRegister32(os, "es", context->__ss.__es);
+ DumpRegister32(os, "fs", context->__ss.__fs);
+ os << '\n';
+ DumpRegister32(os, "gs", context->__ss.__gs);
+ DumpRegister32(os, "ss", context->__ss.__ss);
+#else
+ DumpRegister32(os, "eax", context.gregs[REG_EAX]);
+ DumpRegister32(os, "ebx", context.gregs[REG_EBX]);
+ DumpRegister32(os, "ecx", context.gregs[REG_ECX]);
+ DumpRegister32(os, "edx", context.gregs[REG_EDX]);
+ os << '\n';
+
+ DumpRegister32(os, "edi", context.gregs[REG_EDI]);
+ DumpRegister32(os, "esi", context.gregs[REG_ESI]);
+ DumpRegister32(os, "ebp", context.gregs[REG_EBP]);
+ DumpRegister32(os, "esp", context.gregs[REG_ESP]);
+ os << '\n';
+
+ DumpRegister32(os, "eip", context.gregs[REG_EIP]);
+ DumpRegister32(os, "eflags", context.gregs[REG_EFL]);
+ os << '\n';
+
+ DumpRegister32(os, "cs", context.gregs[REG_CS]);
+ DumpRegister32(os, "ds", context.gregs[REG_DS]);
+ DumpRegister32(os, "es", context.gregs[REG_ES]);
+ DumpRegister32(os, "fs", context.gregs[REG_FS]);
+ os << '\n';
+ DumpRegister32(os, "gs", context.gregs[REG_GS]);
+ DumpRegister32(os, "ss", context.gregs[REG_SS]);
#endif
- } else if (signal_number == SIGPIPE) {
- signal_name = "SIGPIPE";
}
- // Remove ourselves as signal handler for this signal, in case of recursion.
- signal(signal_number, SIG_DFL);
+ void DumpRegister32(std::ostream& os, const char* name, uint32_t value) {
+ os << StringPrintf(" %6s: 0x%08x", name, value);
+ }
+
+ mcontext_t& context;
+};
+
+static void HandleUnexpectedSignal(int signal_number, siginfo_t* info, void* raw_context) {
+ bool has_address = (signal_number == SIGILL || signal_number == SIGBUS ||
+ signal_number == SIGFPE || signal_number == SIGSEGV);
+
+ UContext thread_context(raw_context);
+ Backtrace thread_backtrace;
LOG(INTERNAL_FATAL) << "*** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***\n"
<< StringPrintf("Fatal signal %d (%s), code %d (%s)",
- signal_number, signal_name,
+ signal_number, GetSignalName(signal_number),
info->si_code,
GetSignalCodeName(signal_number, info->si_code))
- << (has_address ? StringPrintf(" fault addr %p", info->si_addr) : "");
- Backtrace();
+ << (has_address ? StringPrintf(" fault addr %p", info->si_addr) : "") << "\n"
+ << "Registers:\n" << Dumpable<UContext>(thread_context) << "\n"
+ << "Backtrace:\n" << Dumpable<Backtrace>(thread_backtrace);
// TODO: instead, get debuggerd running on the host, try to connect, and hang around on success.
if (getenv("debug_db_uid") != NULL) {
@@ -209,7 +291,11 @@
memset(&action, 0, sizeof(action));
sigemptyset(&action.sa_mask);
action.sa_sigaction = HandleUnexpectedSignal;
- action.sa_flags = SA_RESTART | SA_SIGINFO | SA_ONSTACK;
+ action.sa_flags = SA_RESTART;
+ // Use the three-argument sa_sigaction handler.
+ action.sa_flags |= SA_SIGINFO;
+ // Remove ourselves as signal handler for this signal, in case of recursion.
+ action.sa_flags |= SA_RESETHAND;
int rc = 0;
rc += sigaction(SIGILL, &action, NULL);
@@ -217,11 +303,18 @@
rc += sigaction(SIGABRT, &action, NULL);
rc += sigaction(SIGBUS, &action, NULL);
rc += sigaction(SIGFPE, &action, NULL);
- rc += sigaction(SIGSEGV, &action, NULL);
#if defined(SIGSTKFLT)
rc += sigaction(SIGSTKFLT, &action, NULL);
#endif
rc += sigaction(SIGPIPE, &action, NULL);
+
+ // Use the alternate signal stack so we can catch stack overflows.
+ // On Mac OS 10.7, backtrace(3) is broken and will return no frames when called from the alternate stack,
+ // so we only use the alternate stack for SIGSEGV so that we at least get backtraces for other signals.
+ // (glibc does the right thing, so we could use the alternate stack for all signals there.)
+ action.sa_flags |= SA_ONSTACK;
+ rc += sigaction(SIGSEGV, &action, NULL);
+
CHECK_EQ(rc, 0);
}