Index: src/client/mac/handler/exception_handler.cc |
=================================================================== |
--- a/src/client/mac/handler/exception_handler.cc |
+++ b/src/client/mac/handler/exception_handler.cc |
@@ -48,18 +48,23 @@ |
extern ProtectedMemoryAllocator *gBreakpadAllocator; |
#endif |
namespace google_breakpad { |
using std::map; |
+// Message ID telling the handler thread to write a dump. |
Mark Mentovai
2010/12/08 20:57:19
Why did you pick such a weird order to put these m
Ted Mielczarek
2010/12/08 21:01:27
I think in the previous patch it was an artifact o
|
+static const mach_msg_id_t kWriteDumpMessage = 0; |
// Message ID telling the handler thread to quit. |
static const mach_msg_id_t kQuitMessage = 1; |
+// Message ID telling the handler thread to write a dump and include |
+// an exception stream. |
+static const mach_msg_id_t kWriteDumpWithExceptionMessage = 2; |
// These structures and techniques are illustrated in |
// Mac OS X Internals, Amit Singh, ch 9.7 |
struct ExceptionMessage { |
mach_msg_header_t header; |
mach_msg_body_t body; |
mach_msg_port_descriptor_t thread; |
mach_msg_port_descriptor_t task; |
@@ -267,47 +272,50 @@ ExceptionHandler::ExceptionHandler(Direc |
MinidumpGenerator::GatherSystemInformation(); |
Setup(install_handler); |
} |
ExceptionHandler::~ExceptionHandler() { |
Teardown(); |
} |
-bool ExceptionHandler::WriteMinidump() { |
+bool ExceptionHandler::WriteMinidump(bool write_exception_stream) { |
// If we're currently writing, just return |
if (use_minidump_write_mutex_) |
return false; |
use_minidump_write_mutex_ = true; |
last_minidump_write_result_ = false; |
// Lock the mutex. Since we just created it, this will return immediately. |
if (pthread_mutex_lock(&minidump_write_mutex_) == 0) { |
// Send an empty message to the handle port so that a minidump will |
// be written |
- SendEmptyMachMessage(false); |
+ SendMessageToHandlerThread(write_exception_stream ? |
+ kWriteDumpWithExceptionMessage |
Mark Mentovai
2010/12/08 20:57:19
Style nit: indent this line by 4 more spaces, put
|
+ : kWriteDumpMessage); |
// Wait for the minidump writer to complete its writing. It will unlock |
// the mutex when completed |
pthread_mutex_lock(&minidump_write_mutex_); |
} |
use_minidump_write_mutex_ = false; |
UpdateNextID(); |
return last_minidump_write_result_; |
} |
// static |
bool ExceptionHandler::WriteMinidump(const string &dump_path, |
+ bool write_exception_stream, |
MinidumpCallback callback, |
void *callback_context) { |
ExceptionHandler handler(dump_path, NULL, callback, callback_context, false, |
NULL); |
- return handler.WriteMinidump(); |
+ return handler.WriteMinidump(write_exception_stream); |
} |
// static |
bool ExceptionHandler::WriteMinidumpForChild(mach_port_t child, |
mach_port_t child_blamed_thread, |
const string &dump_path, |
MinidumpCallback callback, |
void *callback_context) { |
@@ -334,26 +342,27 @@ bool ExceptionHandler::WriteMinidumpForC |
callback_context, result); |
} |
return result; |
} |
bool ExceptionHandler::WriteMinidumpWithException(int exception_type, |
int exception_code, |
int exception_subcode, |
- mach_port_t thread_name) { |
+ mach_port_t thread_name, |
+ bool exit_after_write) { |
bool result = false; |
if (directCallback_) { |
if (directCallback_(callback_context_, |
exception_type, |
exception_code, |
exception_subcode, |
thread_name) ) { |
- if (exception_type && exception_code) |
+ if (exit_after_write) |
_exit(exception_type); |
} |
} else if (IsOutOfProcess()) { |
if (exception_type && exception_code) { |
// If this is a real exception, give the filter (if any) a chance to |
// decide if this should be sent. |
if (filter_ && !filter_(callback_context_)) |
return false; |
@@ -385,17 +394,17 @@ bool ExceptionHandler::WriteMinidumpWith |
// Call user specified callback (if any) |
if (callback_) { |
// If the user callback returned true and we're handling an exception |
// (rather than just writing out the file), then we should exit without |
// forwarding the exception to the next handler. |
if (callback_(dump_path_c_, next_minidump_id_c_, callback_context_, |
result)) { |
- if (exception_type && exception_code) |
+ if (exit_after_write) |
_exit(exception_type); |
} |
} |
} |
return result; |
} |
@@ -532,19 +541,36 @@ void *ExceptionHandler::WaitForMessage(v |
self->SuspendThreads(); |
#if USE_PROTECTED_ALLOCATIONS |
if(gBreakpadAllocator) |
gBreakpadAllocator->Unprotect(); |
#endif |
+ mach_port_t thread = MACH_PORT_NULL; |
+ int exception_type = 0; |
+ int exception_code = 0; |
+ if (receive.header.msgh_id == kWriteDumpWithExceptionMessage) { |
+ thread = receive.thread.name; |
+ exception_type = EXC_BREAKPOINT; |
+#if defined (__i386__) || defined(__x86_64__) |
+ exception_code = EXC_I386_BPT; |
+#elif defined (__ppc__) || defined (__ppc64__) |
+ exception_code = EXC_PPC_BREAKPOINT; |
+#else |
+ #error architecture not supported |
Mark Mentovai
2010/12/08 20:57:19
Don’t indent preprocessor things.
|
+#endif |
+ } |
+ |
// Write out the dump and save the result for later retrieval |
self->last_minidump_write_result_ = |
- self->WriteMinidumpWithException(0, 0, 0, 0); |
+ self->WriteMinidumpWithException(exception_type, exception_code, |
+ 0, thread, |
+ exception_type != EXC_BREAKPOINT); |
self->UninstallHandler(false); |
#if USE_PROTECTED_ALLOCATIONS |
if(gBreakpadAllocator) |
gBreakpadAllocator->Protect(); |
#endif |
@@ -570,17 +596,17 @@ void *ExceptionHandler::WaitForMessage(v |
#endif |
int subcode = 0; |
if (receive.exception == EXC_BAD_ACCESS && receive.code_count > 1) |
subcode = receive.code[1]; |
// Generate the minidump with the exception data. |
self->WriteMinidumpWithException(receive.exception, receive.code[0], |
- subcode, receive.thread.name); |
+ subcode, receive.thread.name, true); |
self->UninstallHandler(true); |
#if USE_PROTECTED_ALLOCATIONS |
if(gBreakpadAllocator) |
gBreakpadAllocator->Protect(); |
#endif |
} |
@@ -705,44 +731,50 @@ bool ExceptionHandler::Setup(bool instal |
bool ExceptionHandler::Teardown() { |
kern_return_t result = KERN_SUCCESS; |
is_in_teardown_ = true; |
if (!UninstallHandler(false)) |
return false; |
// Send an empty message so that the handler_thread exits |
- if (SendEmptyMachMessage(true)) { |
+ if (SendMessageToHandlerThread(kQuitMessage)) { |
mach_port_t current_task = mach_task_self(); |
result = mach_port_deallocate(current_task, handler_port_); |
if (result != KERN_SUCCESS) |
return false; |
} else { |
return false; |
} |
handler_thread_ = NULL; |
handler_port_ = NULL; |
pthread_mutex_destroy(&minidump_write_mutex_); |
return result == KERN_SUCCESS; |
} |
-bool ExceptionHandler::SendEmptyMachMessage(bool quit) { |
- ExceptionMessage empty; |
- memset(&empty, 0, sizeof(empty)); |
- if (quit) |
- empty.header.msgh_id = kQuitMessage; |
- empty.header.msgh_size = sizeof(empty) - sizeof(empty.padding); |
- empty.header.msgh_remote_port = handler_port_; |
- empty.header.msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND, |
+bool ExceptionHandler::SendMessageToHandlerThread(mach_msg_id_t message_id) { |
+ ExceptionMessage msg; |
+ memset(&msg, 0, sizeof(msg)); |
+ msg.header.msgh_id = message_id; |
+ if (message_id == kWriteDumpMessage || |
+ message_id == kWriteDumpWithExceptionMessage) { |
+ // Include this thread's port. |
+ msg.thread.name = mach_thread_self(); |
+ msg.thread.disposition = MACH_MSG_TYPE_PORT_SEND; |
+ msg.thread.type = MACH_MSG_PORT_DESCRIPTOR; |
+ } |
+ msg.header.msgh_size = sizeof(msg) - sizeof(msg.padding); |
+ msg.header.msgh_remote_port = handler_port_; |
+ msg.header.msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND, |
MACH_MSG_TYPE_MAKE_SEND_ONCE); |
- kern_return_t result = mach_msg(&(empty.header), |
+ kern_return_t result = mach_msg(&(msg.header), |
MACH_SEND_MSG | MACH_SEND_TIMEOUT, |
- empty.header.msgh_size, 0, 0, |
+ msg.header.msgh_size, 0, 0, |
MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL); |
return result == KERN_SUCCESS; |
} |
void ExceptionHandler::UpdateNextID() { |
next_minidump_path_ = |
(MinidumpGenerator::UniqueNameInDirectory(dump_path_, &next_minidump_id_)); |