bug 717758 - handle abort() in Breakpad on OS X. r=mento
authorTed Mielczarek <ted@mielczarek.org>
Mon, 16 Sep 2013 14:44:24 -0400
changeset 147741 62cba8b2fd7d4686c92bb322b6f3904236df3c83
parent 147740 10dbb333ca831a2e628a2e2a31b0e88e590b4300
child 147742 742c8c54c8fc2f74a6a73bb0bbf0c9c2255344c7
push id25314
push userkwierso@gmail.com
push dateWed, 18 Sep 2013 23:48:47 +0000
treeherdermozilla-central@dc09d922d41f [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmento
bugs717758
milestone27.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
bug 717758 - handle abort() in Breakpad on OS X. r=mento
toolkit/crashreporter/google-breakpad/src/client/mac/handler/exception_handler.cc
toolkit/crashreporter/google-breakpad/src/client/mac/handler/minidump_generator.cc
toolkit/crashreporter/google-breakpad/src/client/mac/tests/exception_handler_test.cc
--- a/toolkit/crashreporter/google-breakpad/src/client/mac/handler/exception_handler.cc
+++ b/toolkit/crashreporter/google-breakpad/src/client/mac/handler/exception_handler.cc
@@ -621,17 +621,16 @@ void ExceptionHandler::SignalHandler(int
 #endif
 }
 
 bool ExceptionHandler::InstallHandler() {
   // If a handler is already installed, something is really wrong.
   if (gProtectedData.handler != NULL) {
     return false;
   }
-#if TARGET_OS_IPHONE
   if (!IsOutOfProcess()) {
     struct sigaction sa;
     memset(&sa, 0, sizeof(sa));
     sigemptyset(&sa.sa_mask);
     sigaddset(&sa.sa_mask, SIGABRT);
     sa.sa_sigaction = ExceptionHandler::SignalHandler;
     sa.sa_flags = SA_SIGINFO;
 
@@ -641,17 +640,16 @@ bool ExceptionHandler::InstallHandler() 
     }
     old_handler_.swap(old);
     gProtectedData.handler = this;
 #if USE_PROTECTED_ALLOCATIONS
     assert(((size_t)(gProtectedData.protected_buffer) & PAGE_MASK) == 0);
     mprotect(gProtectedData.protected_buffer, PAGE_SIZE, PROT_READ);
 #endif
   }
-#endif
 
   try {
 #if USE_PROTECTED_ALLOCATIONS
     previous_ = new (gBreakpadAllocator->Allocate(sizeof(ExceptionParameters)) )
       ExceptionParameters();
 #else
     previous_ = new ExceptionParameters();
 #endif
--- a/toolkit/crashreporter/google-breakpad/src/client/mac/handler/minidump_generator.cc
+++ b/toolkit/crashreporter/google-breakpad/src/client/mac/handler/minidump_generator.cc
@@ -783,18 +783,31 @@ bool MinidumpGenerator::GetThreadState(t
       case CPU_TYPE_ARM: {
         size_t final_size =
             std::min(static_cast<size_t>(*count), sizeof(arm_thread_state_t));
         memcpy(state, &task_context_->uc_mcontext->__ss, final_size);
         *count = final_size;
         return true;
       }
 #endif
+#ifdef HAS_X86_SUPPORT
+    case CPU_TYPE_I386:
+    case CPU_TYPE_X86_64: {
+        size_t state_size = cpu_type_ == CPU_TYPE_I386 ?
+            sizeof(i386_thread_state_t) : sizeof(x86_thread_state64_t);
+        size_t final_size =
+            std::min(static_cast<size_t>(*count), state_size);
+        memcpy(state, &task_context_->uc_mcontext->__ss, final_size);
+        *count = final_size;
+        return true;
+      }
+#endif
     }
   }
+
   thread_state_flavor_t flavor;
   switch (cpu_type_) {
 #ifdef HAS_ARM_SUPPORT
     case CPU_TYPE_ARM:
       flavor = ARM_THREAD_STATE;
       break;
 #endif
 #ifdef HAS_PPC_SUPPORT
--- a/toolkit/crashreporter/google-breakpad/src/client/mac/tests/exception_handler_test.cc
+++ b/toolkit/crashreporter/google-breakpad/src/client/mac/tests/exception_handler_test.cc
@@ -117,37 +117,71 @@ void ExceptionHandlerTest::InProcessCras
   }
   // In the parent process.
   ASSERT_NE(-1, pid);
   // Wait for the background process to return the minidump file.
   close(fds[1]);
   char minidump_file[PATH_MAX];
   ssize_t nbytes = read(fds[0], minidump_file, sizeof(minidump_file));
   ASSERT_NE(0, nbytes);
-  // Ensure that minidump file exists and is > 0 bytes.
-  struct stat st;
-  ASSERT_EQ(0, stat(minidump_file, &st));
-  ASSERT_LT(0, st.st_size);
+
+  Minidump minidump(minidump_file);
+  ASSERT_TRUE(minidump.Read());
+
+  MinidumpException* exception = minidump.GetException();
+  ASSERT_TRUE(exception);
+
+  const MDRawExceptionStream* raw_exception = exception->exception();
+  ASSERT_TRUE(raw_exception);
+
+  if (aborting) {
+    EXPECT_EQ(MD_EXCEPTION_MAC_SOFTWARE,
+              raw_exception->exception_record.exception_code);
+    EXPECT_EQ(MD_EXCEPTION_CODE_MAC_ABORT,
+              raw_exception->exception_record.exception_flags);
+  } else {
+    EXPECT_EQ(MD_EXCEPTION_MAC_BAD_ACCESS,
+              raw_exception->exception_record.exception_code);
+#if defined(__x86_64__)
+    EXPECT_EQ(MD_EXCEPTION_CODE_MAC_INVALID_ADDRESS,
+              raw_exception->exception_record.exception_flags);
+#elif defined(__i386__)
+    EXPECT_EQ(MD_EXCEPTION_CODE_MAC_PROTECTION_FAILURE,
+              raw_exception->exception_record.exception_flags);
+#endif
+  }
+
+  const MinidumpContext* context = exception->GetContext();
+  ASSERT_TRUE(context);
+
+  uint64_t instruction_pointer;
+  ASSERT_TRUE(context->GetInstructionPointer(&instruction_pointer));
+
+  // Ideally would like to sanity check that abort() is on the stack
+  // but that's hard.
+  MinidumpMemoryList* memory_list = minidump.GetMemoryList();
+  ASSERT_TRUE(memory_list);
+  MinidumpMemoryRegion* region =
+      memory_list->GetMemoryRegionForAddress(instruction_pointer);
+  EXPECT_TRUE(region);
 
   // Child process should have exited with a zero status.
   int ret;
   ASSERT_EQ(pid, waitpid(pid, &ret, 0));
   EXPECT_NE(0, WIFEXITED(ret));
   EXPECT_EQ(0, WEXITSTATUS(ret));
 }
 
 TEST_F(ExceptionHandlerTest, InProcess) {
   InProcessCrash(false);
 }
 
-#if TARGET_OS_IPHONE
 TEST_F(ExceptionHandlerTest, InProcessAbort) {
   InProcessCrash(true);
 }
-#endif
 
 static bool DumpNameMDCallback(const char *dump_dir, const char *file_name,
                                void *context, bool success) {
   ExceptionHandlerTest *self = reinterpret_cast<ExceptionHandlerTest*>(context);
   if (dump_dir && file_name) {
     self->lastDumpName = dump_dir;
     self->lastDumpName += "/";
     self->lastDumpName += file_name;