Merge "Various crash diagnostic improvements." into ics-mr1-plus-art
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);
}