aboutsummaryrefslogtreecommitdiff
path: root/src/client/linux/handler
diff options
context:
space:
mode:
Diffstat (limited to 'src/client/linux/handler')
-rw-r--r--src/client/linux/handler/exception_handler.cc35
-rw-r--r--src/client/linux/handler/exception_handler.h17
-rw-r--r--src/client/linux/handler/exception_handler_unittest.cc66
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());
+}