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);
 }