diff options
Diffstat (limited to 'src/client/linux/handler')
-rw-r--r-- | src/client/linux/handler/exception_handler.cc | 35 | ||||
-rw-r--r-- | src/client/linux/handler/exception_handler.h | 17 | ||||
-rw-r--r-- | src/client/linux/handler/exception_handler_unittest.cc | 66 |
3 files changed, 117 insertions, 1 deletions
diff --git a/src/client/linux/handler/exception_handler.cc b/src/client/linux/handler/exception_handler.cc index 6f0b8de4..aad2a0e0 100644 --- a/src/client/linux/handler/exception_handler.cc +++ b/src/client/linux/handler/exception_handler.cc @@ -529,7 +529,7 @@ bool ExceptionHandler::WriteMinidump() { static_cast<void>(ftruncate(minidump_descriptor_.fd(), 0)); } - // Allow ourselves to be dumped. + // Allow this process to be dumped. sys_prctl(PR_SET_DUMPABLE, 1); CrashContext context; @@ -543,6 +543,22 @@ bool ExceptionHandler::WriteMinidump() { #endif context.tid = sys_gettid(); + // Add an exception stream to the minidump for better reporting. + memset(&context.siginfo, 0, sizeof(context.siginfo)); + context.siginfo.si_signo = MD_EXCEPTION_CODE_LIN_DUMP_REQUESTED; +#if defined(__i386__) + context.siginfo.si_addr = + reinterpret_cast<void*>(context.context.uc_mcontext.gregs[REG_EIP]); +#elif defined(__x86_64__) + context.siginfo.si_addr = + reinterpret_cast<void*>(context.context.uc_mcontext.gregs[REG_RIP]); +#elif defined(__arm__) + context.siginfo.si_addr = + reinterpret_cast<void*>(context.context.uc_mcontext.arm_pc); +#else +#error "This code has not been ported to your platform yet." +#endif + return GenerateDump(&context); } @@ -586,4 +602,21 @@ void ExceptionHandler::UnregisterAppMemory(void* ptr) { } } +// static +bool ExceptionHandler::WriteMinidumpForChild(pid_t child, + pid_t child_blamed_thread, + const string& dump_path, + MinidumpCallback callback, + void* callback_context) { + // This function is not run in a compromised context. + MinidumpDescriptor descriptor(dump_path); + descriptor.UpdatePath(); + if (!google_breakpad::WriteMinidump(descriptor.path(), + child, + child_blamed_thread)) + return false; + + return callback ? callback(descriptor, callback_context, true) : true; +} + } // namespace google_breakpad diff --git a/src/client/linux/handler/exception_handler.h b/src/client/linux/handler/exception_handler.h index 68953adf..7e6f8649 100644 --- a/src/client/linux/handler/exception_handler.h +++ b/src/client/linux/handler/exception_handler.h @@ -167,6 +167,23 @@ class ExceptionHandler { MinidumpCallback callback, void* callback_context); + // Write a minidump of |child| immediately. This can be used to + // capture the execution state of |child| independently of a crash. + // Pass a meaningful |child_blamed_thread| to make that thread in + // the child process the one from which a crash signature is + // extracted. + // + // WARNING: the return of this function *must* happen before + // the code that will eventually reap |child| executes. + // Otherwise there's a pernicious race condition in which |child| + // exits, is reaped, another process created with its pid, then that + // new process dumped. + static bool WriteMinidumpForChild(pid_t child, + pid_t child_blamed_thread, + const string& dump_path, + MinidumpCallback callback, + void* callback_context); + // This structure is passed to minidump_writer.h:WriteMinidump via an opaque // blob. It shouldn't be needed in any user code. struct CrashContext { diff --git a/src/client/linux/handler/exception_handler_unittest.cc b/src/client/linux/handler/exception_handler_unittest.cc index 341d2fb0..80839730 100644 --- a/src/client/linux/handler/exception_handler_unittest.cc +++ b/src/client/linux/handler/exception_handler_unittest.cc @@ -895,6 +895,25 @@ TEST(ExceptionHandlerTest, ExternalDumper) { unlink(templ.c_str()); } +TEST(ExceptionHandlerTest, WriteMinidumpExceptionStream) { + AutoTempDir temp_dir; + ExceptionHandler handler(MinidumpDescriptor(temp_dir.path()), NULL, NULL, + NULL, false, -1); + ASSERT_TRUE(handler.WriteMinidump()); + + string minidump_path = handler.minidump_descriptor().path(); + + // Read the minidump and check the exception stream. + Minidump minidump(minidump_path); + ASSERT_TRUE(minidump.Read()); + MinidumpException* exception = minidump.GetException(); + ASSERT_TRUE(exception); + const MDRawExceptionStream* raw = exception->exception(); + ASSERT_TRUE(raw); + EXPECT_EQ(MD_EXCEPTION_CODE_LIN_DUMP_REQUESTED, + raw->exception_record.exception_code); +} + TEST(ExceptionHandlerTest, GenerateMultipleDumpsWithFD) { AutoTempDir temp_dir; std::string path; @@ -1021,3 +1040,50 @@ TEST(ExceptionHandlerTest, AdditionalMemoryRemove) { delete[] memory; } + +static bool SimpleCallback(const MinidumpDescriptor& descriptor, + void* context, + bool succeeded) { + string* filename = reinterpret_cast<string*>(context); + *filename = descriptor.path(); + return true; +} + +TEST(ExceptionHandlerTest, WriteMinidumpForChild) { + int fds[2]; + ASSERT_NE(-1, pipe(fds)); + + const pid_t child = fork(); + if (child == 0) { + close(fds[1]); + char b; + HANDLE_EINTR(read(fds[0], &b, sizeof(b))); + close(fds[0]); + syscall(__NR_exit); + } + close(fds[0]); + + AutoTempDir temp_dir; + string minidump_filename; + ASSERT_TRUE( + ExceptionHandler::WriteMinidumpForChild(child, child, + temp_dir.path(), SimpleCallback, + (void*)&minidump_filename)); + + Minidump minidump(minidump_filename); + ASSERT_TRUE(minidump.Read()); + // Check that the crashing thread is the main thread of |child| + MinidumpException* exception = minidump.GetException(); + ASSERT_TRUE(exception); + u_int32_t thread_id; + ASSERT_TRUE(exception->GetThreadID(&thread_id)); + EXPECT_EQ(child, thread_id); + + const MDRawExceptionStream* raw = exception->exception(); + ASSERT_TRUE(raw); + EXPECT_EQ(MD_EXCEPTION_CODE_LIN_DUMP_REQUESTED, + raw->exception_record.exception_code); + + close(fds[1]); + unlink(minidump_filename.c_str()); +} |