aboutsummaryrefslogtreecommitdiff
path: root/src/client/mac
diff options
context:
space:
mode:
authorted.mielczarek@gmail.com <ted.mielczarek@gmail.com@4c0a9323-5329-0410-9bdc-e9ce6186880e>2010-08-13 20:19:32 +0000
committerted.mielczarek@gmail.com <ted.mielczarek@gmail.com@4c0a9323-5329-0410-9bdc-e9ce6186880e>2010-08-13 20:19:32 +0000
commit144938cf22243407a56601bd5b75147f796a8424 (patch)
treea8260c496ba9dafe3c01bbe810a3880e5f673b1b /src/client/mac
parentImplement CrashGeneration{Client,Server} for OOP dump generation on OS X, ena... (diff)
downloadbreakpad-144938cf22243407a56601bd5b75147f796a8424.tar.xz
Allow dumping live processes on OS X
R=mark at http://breakpad.appspot.com/148001/show git-svn-id: http://google-breakpad.googlecode.com/svn/trunk@647 4c0a9323-5329-0410-9bdc-e9ce6186880e
Diffstat (limited to 'src/client/mac')
-rw-r--r--src/client/mac/handler/exception_handler.cc32
-rw-r--r--src/client/mac/handler/exception_handler.h8
-rw-r--r--src/client/mac/handler/minidump_generator.cc5
-rw-r--r--src/client/mac/tests/exception_handler_test.cc92
4 files changed, 135 insertions, 2 deletions
diff --git a/src/client/mac/handler/exception_handler.cc b/src/client/mac/handler/exception_handler.cc
index ccf1ff99..89cc4e90 100644
--- a/src/client/mac/handler/exception_handler.cc
+++ b/src/client/mac/handler/exception_handler.cc
@@ -33,6 +33,7 @@
#include "client/mac/handler/exception_handler.h"
#include "client/mac/handler/minidump_generator.h"
#include "common/mac/macho_utilities.h"
+#include "common/mac/scoped_task_suspend-inl.h"
#ifndef USE_PROTECTED_ALLOCATIONS
#define USE_PROTECTED_ALLOCATIONS 0
@@ -301,6 +302,37 @@ bool ExceptionHandler::WriteMinidump(const string &dump_path,
return handler.WriteMinidump();
}
+// static
+bool ExceptionHandler::WriteMinidumpForChild(mach_port_t child,
+ mach_port_t child_blamed_thread,
+ const string &dump_path,
+ MinidumpCallback callback,
+ void *callback_context) {
+ ScopedTaskSuspend suspend(child);
+
+ MinidumpGenerator generator(child, MACH_PORT_NULL);
+ string dump_id;
+ string dump_filename = generator.UniqueNameInDirectory(dump_path, &dump_id);
+
+ generator.SetExceptionInformation(EXC_BREAKPOINT,
+#if defined (__i386__) || defined(__x86_64__)
+ EXC_I386_BPT,
+#elif defined (__ppc__) || defined (__ppc64__)
+ EXC_PPC_BREAKPOINT,
+#else
+ #error architecture not supported
+#endif
+ 0,
+ child_blamed_thread);
+ bool result = generator.Write(dump_filename.c_str());
+
+ if (callback) {
+ return callback(dump_path.c_str(), dump_id.c_str(),
+ callback_context, result);
+ }
+ return result;
+}
+
bool ExceptionHandler::WriteMinidumpWithException(int exception_type,
int exception_code,
int exception_subcode,
diff --git a/src/client/mac/handler/exception_handler.h b/src/client/mac/handler/exception_handler.h
index ee3e2e1d..76353255 100644
--- a/src/client/mac/handler/exception_handler.h
+++ b/src/client/mac/handler/exception_handler.h
@@ -121,6 +121,14 @@ class ExceptionHandler {
static bool WriteMinidump(const string &dump_path, MinidumpCallback callback,
void *callback_context);
+ // Write a minidump of child immediately. This can be used to capture
+ // the execution state of a child process independently of a crash.
+ static bool WriteMinidumpForChild(mach_port_t child,
+ mach_port_t child_blamed_thread,
+ const std::string &dump_path,
+ MinidumpCallback callback,
+ void *callback_context);
+
// Returns whether out-of-process dump generation is used or not.
bool IsOutOfProcess() const {
return crash_generation_client_.get() != NULL;
diff --git a/src/client/mac/handler/minidump_generator.cc b/src/client/mac/handler/minidump_generator.cc
index c5886be0..0c54cf42 100644
--- a/src/client/mac/handler/minidump_generator.cc
+++ b/src/client/mac/handler/minidump_generator.cc
@@ -536,7 +536,10 @@ bool MinidumpGenerator::WriteThreadListStream(
return false;
// Don't include the generator thread
- non_generator_thread_count = thread_count - 1;
+ if (handler_thread_ != MACH_PORT_NULL)
+ non_generator_thread_count = thread_count - 1;
+ else
+ non_generator_thread_count = thread_count;
if (!list.AllocateObjectAndArray(non_generator_thread_count,
sizeof(MDRawThread)))
return false;
diff --git a/src/client/mac/tests/exception_handler_test.cc b/src/client/mac/tests/exception_handler_test.cc
index 6ba244dd..f949a8b1 100644
--- a/src/client/mac/tests/exception_handler_test.cc
+++ b/src/client/mac/tests/exception_handler_test.cc
@@ -35,6 +35,7 @@
#include "breakpad_googletest_includes.h"
#include "client/mac/handler/exception_handler.h"
#include "client/mac/tests/auto_tempdir.h"
+#include "common/mac/MachIPC.h"
namespace {
using std::string;
@@ -42,6 +43,12 @@ using google_breakpad::AutoTempDir;
using google_breakpad::ExceptionHandler;
using testing::Test;
+class ExceptionHandlerTest : public Test {
+ public:
+ AutoTempDir tempDir;
+ string lastDumpName;
+};
+
static void Crasher() {
int *a = (int*)0x42;
@@ -68,7 +75,7 @@ static bool MDCallback(const char *dump_dir, const char *file_name,
return true;
}
-TEST(ExceptionHandler, InProcess) {
+TEST_F(ExceptionHandlerTest, InProcess) {
AutoTempDir tempDir;
// Give the child process a pipe to report back on.
int fds[2];
@@ -76,6 +83,7 @@ TEST(ExceptionHandler, InProcess) {
// Fork off a child process so it can crash.
pid_t pid = fork();
if (pid == 0) {
+ // In the child process.
close(fds[0]);
ExceptionHandler eh(tempDir.path, NULL, MDCallback, &fds[1], true, NULL);
// crash
@@ -83,6 +91,7 @@ TEST(ExceptionHandler, InProcess) {
// not reached
exit(1);
}
+ // In the parent process.
ASSERT_NE(-1, pid);
// Wait for the background process to return the minidump file.
close(fds[1]);
@@ -101,4 +110,85 @@ TEST(ExceptionHandler, InProcess) {
EXPECT_EQ(0, WEXITSTATUS(ret));
}
+static bool ChildMDCallback(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;
+ self->lastDumpName += ".dmp";
+ }
+ return true;
+}
+
+TEST_F(ExceptionHandlerTest, DumpChildProcess) {
+ const int kTimeoutMs = 2000;
+ // Create a mach port to receive the child task on.
+ char machPortName[128];
+ sprintf(machPortName, "ExceptionHandlerTest.%d", getpid());
+ ReceivePort parent_recv_port(machPortName);
+
+ // Give the child process a pipe to block on.
+ int fds[2];
+ ASSERT_EQ(0, pipe(fds));
+
+ // Fork off a child process to dump.
+ pid_t pid = fork();
+ if (pid == 0) {
+ // In the child process
+ close(fds[0]);
+
+ // Send parent process the task and thread ports.
+ MachSendMessage child_message(0);
+ child_message.AddDescriptor(mach_task_self());
+ child_message.AddDescriptor(mach_thread_self());
+
+ MachPortSender child_sender(machPortName);
+ if (child_sender.SendMessage(child_message, kTimeoutMs) != KERN_SUCCESS)
+ exit(1);
+
+ // Wait for the parent process.
+ uint8_t data;
+ read(fds[1], &data, 1);
+ exit(0);
+ }
+ // In the parent process.
+ ASSERT_NE(-1, pid);
+ close(fds[1]);
+
+ // Read the child's task and thread ports.
+ MachReceiveMessage child_message;
+ ASSERT_EQ(KERN_SUCCESS,
+ parent_recv_port.WaitForMessage(&child_message, kTimeoutMs));
+ mach_port_t child_task = child_message.GetTranslatedPort(0);
+ mach_port_t child_thread = child_message.GetTranslatedPort(1);
+ ASSERT_NE(MACH_PORT_NULL, child_task);
+ ASSERT_NE(MACH_PORT_NULL, child_thread);
+
+ // Write a minidump of the child process.
+ bool result = ExceptionHandler::WriteMinidumpForChild(child_task,
+ child_thread,
+ tempDir.path,
+ ChildMDCallback,
+ this);
+ ASSERT_EQ(true, result);
+
+ // Ensure that minidump file exists and is > 0 bytes.
+ ASSERT_FALSE(lastDumpName.empty());
+ struct stat st;
+ ASSERT_EQ(0, stat(lastDumpName.c_str(), &st));
+ ASSERT_LT(0, st.st_size);
+
+ // Unblock child process
+ uint8_t data = 1;
+ (void)write(fds[0], &data, 1);
+
+ // 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));
+}
+
}