зеркало из https://github.com/mozilla/gecko-dev.git
b=851964; Odin/OSX, part 1. add breakpad TARGET_OSX_USE_64BIT_EXCEPTIONS, and use it; r=ted
This commit is contained in:
Родитель
c78bce9689
Коммит
cfe5d50335
|
@ -0,0 +1,479 @@
|
|||
commit 4b67f801ba77ce1c90568347650a7c2792eb30cf
|
||||
Author: Vladimir Vukicevic <vladimir@pobox.com>
|
||||
Date: Mon Mar 18 01:01:53 2013 -0400
|
||||
|
||||
Add breakpad TARGET_OSX_USE_64BIT_EXCEPTIONS support
|
||||
|
||||
diff --git a/toolkit/crashreporter/google-breakpad/src/client/mac/handler/exception_handler.cc b/toolkit/crashreporter/google-breakpad/src/client/mac/handler/exception_handler.cc
|
||||
index 6862322..db605dc 100644
|
||||
--- a/toolkit/crashreporter/google-breakpad/src/client/mac/handler/exception_handler.cc
|
||||
+++ b/toolkit/crashreporter/google-breakpad/src/client/mac/handler/exception_handler.cc
|
||||
@@ -31,10 +31,17 @@
|
||||
#include <mach/mig.h>
|
||||
#include <pthread.h>
|
||||
#include <signal.h>
|
||||
+#include <dlfcn.h>
|
||||
#include <TargetConditionals.h>
|
||||
|
||||
#include <map>
|
||||
|
||||
+#ifdef __x86_64__
|
||||
+// On x86-64, use the 64-bit exception catching mechanism so that exception
|
||||
+// codes don't get truncated
|
||||
+#define TARGET_OSX_USE_64BIT_EXCEPTIONS 1
|
||||
+#endif
|
||||
+
|
||||
#include "client/mac/handler/exception_handler.h"
|
||||
#include "client/mac/handler/minidump_generator.h"
|
||||
#include "common/mac/macho_utilities.h"
|
||||
@@ -83,6 +90,9 @@ using std::map;
|
||||
|
||||
// These structures and techniques are illustrated in
|
||||
// Mac OS X Internals, Amit Singh, ch 9.7
|
||||
+#ifdef __MigPackStructs
|
||||
+#pragma pack(4)
|
||||
+#endif
|
||||
struct ExceptionMessage {
|
||||
mach_msg_header_t header;
|
||||
mach_msg_body_t body;
|
||||
@@ -91,9 +101,16 @@ struct ExceptionMessage {
|
||||
NDR_record_t ndr;
|
||||
exception_type_t exception;
|
||||
mach_msg_type_number_t code_count;
|
||||
+#if TARGET_OSX_USE_64BIT_EXCEPTIONS
|
||||
+ int64_t code[EXCEPTION_CODE_MAX];
|
||||
+#else
|
||||
integer_t code[EXCEPTION_CODE_MAX];
|
||||
+#endif
|
||||
char padding[512];
|
||||
};
|
||||
+#ifdef __MigPackStructs
|
||||
+#pragma pack()
|
||||
+#endif
|
||||
|
||||
struct ExceptionParameters {
|
||||
ExceptionParameters() : count(0) {}
|
||||
@@ -104,18 +121,24 @@ struct ExceptionParameters {
|
||||
thread_state_flavor_t flavors[EXC_TYPES_COUNT];
|
||||
};
|
||||
|
||||
+#ifdef __MigPackStructs
|
||||
+#pragma pack(4)
|
||||
+#endif
|
||||
struct ExceptionReplyMessage {
|
||||
mach_msg_header_t header;
|
||||
NDR_record_t ndr;
|
||||
kern_return_t return_code;
|
||||
};
|
||||
+#ifdef __MigPackStructs
|
||||
+#pragma pack()
|
||||
+#endif
|
||||
|
||||
// Only catch these three exceptions. The other ones are nebulously defined
|
||||
// and may result in treating a non-fatal exception as fatal.
|
||||
exception_mask_t s_exception_mask = EXC_MASK_BAD_ACCESS |
|
||||
EXC_MASK_BAD_INSTRUCTION | EXC_MASK_ARITHMETIC | EXC_MASK_BREAKPOINT;
|
||||
|
||||
-#if !TARGET_OS_IPHONE
|
||||
+#if !TARGET_OS_IPHONE && !TARGET_OSX_USE_64BIT_EXCEPTIONS
|
||||
extern "C" {
|
||||
// Forward declarations for functions that need "C" style compilation
|
||||
boolean_t exc_server(mach_msg_header_t* request,
|
||||
@@ -136,13 +159,28 @@ extern "C" {
|
||||
kern_return_t ForwardException(mach_port_t task,
|
||||
mach_port_t failed_thread,
|
||||
exception_type_t exception,
|
||||
+#if TARGET_OSX_USE_64BIT_EXCEPTIONS
|
||||
+ mach_exception_data_t code,
|
||||
+#else
|
||||
exception_data_t code,
|
||||
+#endif
|
||||
mach_msg_type_number_t code_count);
|
||||
|
||||
+#if TARGET_OS_IPHONE || TARGET_OSX_USE_64BIT_EXCEPTIONS
|
||||
+
|
||||
#if TARGET_OS_IPHONE
|
||||
+// no idea if this is still correct; this is the value the old code had
|
||||
+#define MACH_EXCEPTION_RAISE_RPC 2401
|
||||
+#elif TARGET_OSX_USE_64BIT_EXCEPTIONS
|
||||
+#define MACH_EXCEPTION_RAISE_RPC 2405
|
||||
+#else
|
||||
+#error Need a value for MACH_EXCEPTION_RAISE_RPC
|
||||
+#endif
|
||||
+
|
||||
// Implementation is based on the implementation generated by mig.
|
||||
boolean_t breakpad_exc_server(mach_msg_header_t* InHeadP,
|
||||
- mach_msg_header_t* OutHeadP) {
|
||||
+ mach_msg_header_t* OutHeadP)
|
||||
+{
|
||||
OutHeadP->msgh_bits =
|
||||
MACH_MSGH_BITS(MACH_MSGH_BITS_REMOTE(InHeadP->msgh_bits), 0);
|
||||
OutHeadP->msgh_remote_port = InHeadP->msgh_remote_port;
|
||||
@@ -151,7 +189,7 @@ boolean_t breakpad_exc_server(mach_msg_header_t* InHeadP,
|
||||
OutHeadP->msgh_local_port = MACH_PORT_NULL;
|
||||
OutHeadP->msgh_id = InHeadP->msgh_id + 100;
|
||||
|
||||
- if (InHeadP->msgh_id != 2401) {
|
||||
+ if (InHeadP->msgh_id != MACH_EXCEPTION_RAISE_RPC) {
|
||||
((mig_reply_error_t*)OutHeadP)->NDR = NDR_record;
|
||||
((mig_reply_error_t*)OutHeadP)->RetCode = MIG_BAD_ID;
|
||||
return FALSE;
|
||||
@@ -170,7 +208,11 @@ boolean_t breakpad_exc_server(mach_msg_header_t* InHeadP,
|
||||
NDR_record_t NDR;
|
||||
exception_type_t exception;
|
||||
mach_msg_type_number_t codeCnt;
|
||||
+#if TARGET_OSX_USE_64BIT_EXCEPTIONS
|
||||
+ int64_t code[2];
|
||||
+#else
|
||||
integer_t code[2];
|
||||
+#endif
|
||||
mach_msg_trailer_t trailer;
|
||||
} Request;
|
||||
|
||||
@@ -197,7 +239,93 @@ boolean_t breakpad_exc_server(mach_msg_header_t* InHeadP,
|
||||
OutP->NDR = NDR_record;
|
||||
return TRUE;
|
||||
}
|
||||
-#else
|
||||
+
|
||||
+#if TARGET_OSX_USE_64BIT_EXCEPTIONS
|
||||
+// Implementation is based on the implementation generated by mig.
|
||||
+kern_return_t breakpad_exception_raise (mach_port_t exception_port,
|
||||
+ mach_port_t thread,
|
||||
+ mach_port_t task,
|
||||
+ exception_type_t exception,
|
||||
+ mach_exception_data_t code,
|
||||
+ mach_msg_type_number_t codeCnt)
|
||||
+{
|
||||
+#ifdef __MigPackStructs
|
||||
+#pragma pack(4)
|
||||
+#endif
|
||||
+ typedef struct {
|
||||
+ mach_msg_header_t Head;
|
||||
+ /* start of the kernel processed data */
|
||||
+ mach_msg_body_t msgh_body;
|
||||
+ mach_msg_port_descriptor_t thread;
|
||||
+ mach_msg_port_descriptor_t task;
|
||||
+ /* end of the kernel processed data */
|
||||
+ NDR_record_t NDR;
|
||||
+ exception_type_t exception;
|
||||
+ mach_msg_type_number_t codeCnt;
|
||||
+ int64_t code[2];
|
||||
+ } Request;
|
||||
+#ifdef __MigPackStructs
|
||||
+#pragma pack()
|
||||
+#endif
|
||||
+
|
||||
+#ifdef __MigPackStructs
|
||||
+#pragma pack(4)
|
||||
+#endif
|
||||
+ typedef struct {
|
||||
+ mach_msg_header_t Head;
|
||||
+ NDR_record_t NDR;
|
||||
+ kern_return_t RetCode;
|
||||
+ mach_msg_trailer_t trailer;
|
||||
+ } Reply;
|
||||
+#ifdef __MigPackStructs
|
||||
+#pragma pack()
|
||||
+#endif
|
||||
+
|
||||
+ Request In;
|
||||
+ Request *InP = &In;
|
||||
+
|
||||
+ mach_msg_return_t msg_result;
|
||||
+ unsigned int msgh_size;
|
||||
+
|
||||
+ InP->msgh_body.msgh_descriptor_count = 2;
|
||||
+ InP->thread.name = thread;
|
||||
+ InP->thread.disposition = 19;
|
||||
+ InP->thread.type = MACH_MSG_PORT_DESCRIPTOR;
|
||||
+ InP->task.name = task;
|
||||
+ InP->task.disposition = 19;
|
||||
+ InP->task.type = MACH_MSG_PORT_DESCRIPTOR;
|
||||
+ InP->NDR = NDR_record;
|
||||
+
|
||||
+ InP->exception = exception;
|
||||
+
|
||||
+ if (codeCnt > 2) {
|
||||
+ { return MIG_ARRAY_TOO_LARGE; }
|
||||
+ }
|
||||
+ (void)memcpy((char *) InP->code, (const char *) code, 8 * codeCnt);
|
||||
+
|
||||
+ msgh_size = (mach_msg_size_t)(sizeof(Request) - 16) + ((8 * codeCnt));
|
||||
+ InP->Head.msgh_bits = MACH_MSGH_BITS_COMPLEX|
|
||||
+ MACH_MSGH_BITS(19, MACH_MSG_TYPE_MAKE_SEND_ONCE);
|
||||
+ /* msgh_size passed as argument */
|
||||
+ InP->Head.msgh_remote_port = exception_port;
|
||||
+ InP->Head.msgh_local_port = mig_get_reply_port();
|
||||
+ InP->Head.msgh_id = 2405;
|
||||
+
|
||||
+ msg_result = mach_msg(&InP->Head, MACH_SEND_MSG|MACH_RCV_MSG|MACH_MSG_OPTION_NONE,
|
||||
+ msgh_size, (mach_msg_size_t)sizeof(Reply), InP->Head.msgh_local_port,
|
||||
+ MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
|
||||
+ if (msg_result != MACH_MSG_SUCCESS) {
|
||||
+ return msg_result;
|
||||
+ }
|
||||
+
|
||||
+ return KERN_SUCCESS;
|
||||
+}
|
||||
+#endif
|
||||
+
|
||||
+#else /* not TARGET_OS_IPHONE || TARGET_OSX_USE_64BIT_EXCEPTIONS */
|
||||
+
|
||||
+#define breakpad_exception_raise exception_raise
|
||||
+
|
||||
boolean_t breakpad_exc_server(mach_msg_header_t* request,
|
||||
mach_msg_header_t* reply) {
|
||||
return exc_server(request, reply);
|
||||
@@ -414,7 +542,11 @@ bool ExceptionHandler::WriteMinidumpWithException(int exception_type,
|
||||
|
||||
kern_return_t ForwardException(mach_port_t task, mach_port_t failed_thread,
|
||||
exception_type_t exception,
|
||||
+#if TARGET_OSX_USE_64BIT_EXCEPTIONS
|
||||
+ mach_exception_data_t code,
|
||||
+#else
|
||||
exception_data_t code,
|
||||
+#endif
|
||||
mach_msg_type_number_t code_count) {
|
||||
// At this time, we should have called Uninstall() on the exception handler
|
||||
// so that the current exception ports are the ones that we should be
|
||||
@@ -448,11 +580,25 @@ kern_return_t ForwardException(mach_port_t task, mach_port_t failed_thread,
|
||||
mach_port_t target_port = current.ports[found];
|
||||
exception_behavior_t target_behavior = current.behaviors[found];
|
||||
|
||||
+#if TARGET_OSX_USE_64BIT_EXCEPTIONS
|
||||
+ boolean_t code64 = (target_behavior & MACH_EXCEPTION_CODES) != 0;
|
||||
+ target_behavior = target_behavior & ~MACH_EXCEPTION_CODES;
|
||||
+
|
||||
+ if (target_behavior == EXCEPTION_DEFAULT && !code64) {
|
||||
+ // we could in theory handle this by downcasting codes to int_t and calling
|
||||
+ // exception_raise instead of breakpad_exception_raise with the int_t array
|
||||
+ // below; but, really, we end up aborting the process before we ever
|
||||
+ // get here, so why bother?
|
||||
+ fprintf(stderr, "*** EXCEPTION_DEFAULT but without MACH_EXCEPTION_CODES bit, we don't have code to forward this");
|
||||
+ return KERN_FAILURE;
|
||||
+ }
|
||||
+#endif
|
||||
+
|
||||
kern_return_t result;
|
||||
switch (target_behavior) {
|
||||
case EXCEPTION_DEFAULT:
|
||||
- result = exception_raise(target_port, failed_thread, task, exception,
|
||||
- code, code_count);
|
||||
+ result = breakpad_exception_raise(target_port, failed_thread, task, exception,
|
||||
+ code, code_count);
|
||||
break;
|
||||
|
||||
default:
|
||||
@@ -480,90 +626,99 @@ void* ExceptionHandler::WaitForMessage(void* exception_handler_class) {
|
||||
self->handler_port_,
|
||||
MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
|
||||
|
||||
-
|
||||
- if (result == KERN_SUCCESS) {
|
||||
- // Uninstall our handler so that we don't get in a loop if the process of
|
||||
- // writing out a minidump causes an exception. However, if the exception
|
||||
- // was caused by a fork'd process, don't uninstall things
|
||||
-
|
||||
- // If the actual exception code is zero, then we're calling this handler
|
||||
- // in a way that indicates that we want to either exit this thread or
|
||||
- // generate a minidump
|
||||
- //
|
||||
- // While reporting, all threads (except this one) must be suspended
|
||||
- // to avoid misleading stacks. If appropriate they will be resumed
|
||||
- // afterwards.
|
||||
- if (!receive.exception) {
|
||||
- // Don't touch self, since this message could have been sent
|
||||
- // from its destructor.
|
||||
- if (receive.header.msgh_id == kShutdownMessage)
|
||||
- return NULL;
|
||||
-
|
||||
- self->SuspendThreads();
|
||||
+ if (result != KERN_SUCCESS)
|
||||
+ continue;
|
||||
+
|
||||
+ // Uninstall our handler so that we don't get in a loop if the process of
|
||||
+ // writing out a minidump causes an exception. However, if the exception
|
||||
+ // was caused by a fork'd process, don't uninstall things
|
||||
+
|
||||
+ // If the actual exception code is zero, then we're calling this handler
|
||||
+ // in a way that indicates that we want to either exit this thread or
|
||||
+ // generate a minidump
|
||||
+ //
|
||||
+ // While reporting, all threads (except this one) must be suspended
|
||||
+ // to avoid misleading stacks. If appropriate they will be resumed
|
||||
+ // afterwards.
|
||||
+ if (!receive.exception) {
|
||||
+ // Don't touch self, since this message could have been sent
|
||||
+ // from its destructor.
|
||||
+ if (receive.header.msgh_id == kShutdownMessage)
|
||||
+ return NULL;
|
||||
+
|
||||
+ self->SuspendThreads();
|
||||
|
||||
#if USE_PROTECTED_ALLOCATIONS
|
||||
- if (gBreakpadAllocator)
|
||||
- gBreakpadAllocator->Unprotect();
|
||||
+ 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;
|
||||
+ 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;
|
||||
+ exception_code = EXC_I386_BPT;
|
||||
#elif defined(__ppc__) || defined(__ppc64__)
|
||||
- exception_code = EXC_PPC_BREAKPOINT;
|
||||
+ exception_code = EXC_PPC_BREAKPOINT;
|
||||
#elif defined(__arm__)
|
||||
- exception_code = EXC_ARM_BREAKPOINT;
|
||||
+ exception_code = EXC_ARM_BREAKPOINT;
|
||||
#else
|
||||
#error architecture not supported
|
||||
#endif
|
||||
- }
|
||||
+ }
|
||||
|
||||
- // Write out the dump and save the result for later retrieval
|
||||
- self->last_minidump_write_result_ =
|
||||
- self->WriteMinidumpWithException(exception_type, exception_code,
|
||||
- 0, NULL, thread,
|
||||
- false, false);
|
||||
+ // Write out the dump and save the result for later retrieval
|
||||
+ self->last_minidump_write_result_ =
|
||||
+ self->WriteMinidumpWithException(exception_type, exception_code,
|
||||
+ 0, NULL, thread,
|
||||
+ false, false);
|
||||
|
||||
#if USE_PROTECTED_ALLOCATIONS
|
||||
- if (gBreakpadAllocator)
|
||||
- gBreakpadAllocator->Protect();
|
||||
+ if (gBreakpadAllocator)
|
||||
+ gBreakpadAllocator->Protect();
|
||||
+#endif
|
||||
+
|
||||
+ self->ResumeThreads();
|
||||
+
|
||||
+ if (self->use_minidump_write_mutex_)
|
||||
+ pthread_mutex_unlock(&self->minidump_write_mutex_);
|
||||
+ } else {
|
||||
+ // When forking a child process with the exception handler installed,
|
||||
+ // if the child crashes, it will send the exception back to the parent
|
||||
+ // process. The check for task == self_task() ensures that only
|
||||
+ // exceptions that occur in the parent process are caught and
|
||||
+ // processed. If the exception was not caused by this task, we
|
||||
+ // still need to call into the exception server and have it return
|
||||
+ // KERN_FAILURE (see catch_exception_raise) in order for the kernel
|
||||
+ // to move onto the host exception handler for the child task
|
||||
+ if (receive.task.name == mach_task_self()) {
|
||||
+#if TARGET_OSX_USE_64BIT_EXCEPTIONS
|
||||
+ int64_t subcode = 0;
|
||||
+#else
|
||||
+ int subcode = 0;
|
||||
#endif
|
||||
|
||||
- self->ResumeThreads();
|
||||
+ if (receive.exception == EXC_BAD_ACCESS && receive.code_count > 1)
|
||||
+ subcode = receive.code[1];
|
||||
|
||||
- if (self->use_minidump_write_mutex_)
|
||||
- pthread_mutex_unlock(&self->minidump_write_mutex_);
|
||||
- } else {
|
||||
- // When forking a child process with the exception handler installed,
|
||||
- // if the child crashes, it will send the exception back to the parent
|
||||
- // process. The check for task == self_task() ensures that only
|
||||
- // exceptions that occur in the parent process are caught and
|
||||
- // processed. If the exception was not caused by this task, we
|
||||
- // still need to call into the exception server and have it return
|
||||
- // KERN_FAILURE (see catch_exception_raise) in order for the kernel
|
||||
- // to move onto the host exception handler for the child task
|
||||
- if (receive.task.name == mach_task_self()) {
|
||||
- self->SuspendThreads();
|
||||
+ self->SuspendThreads();
|
||||
|
||||
#if USE_PROTECTED_ALLOCATIONS
|
||||
if (gBreakpadAllocator)
|
||||
gBreakpadAllocator->Unprotect();
|
||||
#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, NULL, receive.thread.name,
|
||||
true, false);
|
||||
|
||||
+ // Note! We passed "true" to the exit_after_write param above;
|
||||
+ // this code will never execute, because the process will have exited.
|
||||
+
|
||||
#if USE_PROTECTED_ALLOCATIONS
|
||||
// This may have become protected again within
|
||||
// WriteMinidumpWithException, but it needs to be unprotected for
|
||||
@@ -578,19 +733,20 @@ void* ExceptionHandler::WaitForMessage(void* exception_handler_class) {
|
||||
if (gBreakpadAllocator)
|
||||
gBreakpadAllocator->Protect();
|
||||
#endif
|
||||
- }
|
||||
- // Pass along the exception to the server, which will setup the
|
||||
- // message and call catch_exception_raise() and put the return
|
||||
- // code into the reply.
|
||||
- ExceptionReplyMessage reply;
|
||||
- if (!breakpad_exc_server(&receive.header, &reply.header))
|
||||
- exit(1);
|
||||
-
|
||||
- // Send a reply and exit
|
||||
- mach_msg(&(reply.header), MACH_SEND_MSG,
|
||||
- reply.header.msgh_size, 0, MACH_PORT_NULL,
|
||||
- MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
|
||||
+ } // if (receive.task.name == mach_task_self())
|
||||
+
|
||||
+ // Pass along the exception to the server, which will setup the
|
||||
+ // message and call catch_exception_raise() and put the return
|
||||
+ // code into the reply.
|
||||
+ ExceptionReplyMessage reply;
|
||||
+ if (!breakpad_exc_server(&receive.header, &reply.header)) {
|
||||
+ exit(1);
|
||||
}
|
||||
+
|
||||
+ // Send a reply
|
||||
+ mach_msg(&(reply.header), MACH_SEND_MSG,
|
||||
+ reply.header.msgh_size, 0, MACH_PORT_NULL,
|
||||
+ MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -622,6 +778,7 @@ bool ExceptionHandler::InstallHandler() {
|
||||
if (gProtectedData.handler != NULL) {
|
||||
return false;
|
||||
}
|
||||
+
|
||||
#if TARGET_OS_IPHONE
|
||||
if (!IsOutOfProcess()) {
|
||||
struct sigaction sa;
|
||||
@@ -670,7 +827,12 @@ bool ExceptionHandler::InstallHandler() {
|
||||
// Setup the exception ports on this task
|
||||
if (result == KERN_SUCCESS)
|
||||
result = task_set_exception_ports(current_task, s_exception_mask,
|
||||
- handler_port_, EXCEPTION_DEFAULT,
|
||||
+ handler_port_,
|
||||
+#if TARGET_OSX_USE_64BIT_EXCEPTIONS
|
||||
+ EXCEPTION_DEFAULT | MACH_EXCEPTION_CODES,
|
||||
+#else
|
||||
+ EXCEPTION_DEFAULT,
|
||||
+#endif
|
||||
THREAD_STATE_NONE);
|
||||
|
||||
installed_exception_handler_ = (result == KERN_SUCCESS);
|
|
@ -31,10 +31,17 @@
|
|||
#include <mach/mig.h>
|
||||
#include <pthread.h>
|
||||
#include <signal.h>
|
||||
#include <dlfcn.h>
|
||||
#include <TargetConditionals.h>
|
||||
|
||||
#include <map>
|
||||
|
||||
#ifdef __x86_64__
|
||||
// On x86-64, use the 64-bit exception catching mechanism so that exception
|
||||
// codes don't get truncated
|
||||
#define TARGET_OSX_USE_64BIT_EXCEPTIONS 1
|
||||
#endif
|
||||
|
||||
#include "client/mac/handler/exception_handler.h"
|
||||
#include "client/mac/handler/minidump_generator.h"
|
||||
#include "common/mac/macho_utilities.h"
|
||||
|
@ -83,6 +90,9 @@ using std::map;
|
|||
|
||||
// These structures and techniques are illustrated in
|
||||
// Mac OS X Internals, Amit Singh, ch 9.7
|
||||
#ifdef __MigPackStructs
|
||||
#pragma pack(4)
|
||||
#endif
|
||||
struct ExceptionMessage {
|
||||
mach_msg_header_t header;
|
||||
mach_msg_body_t body;
|
||||
|
@ -91,9 +101,16 @@ struct ExceptionMessage {
|
|||
NDR_record_t ndr;
|
||||
exception_type_t exception;
|
||||
mach_msg_type_number_t code_count;
|
||||
#if TARGET_OSX_USE_64BIT_EXCEPTIONS
|
||||
int64_t code[EXCEPTION_CODE_MAX];
|
||||
#else
|
||||
integer_t code[EXCEPTION_CODE_MAX];
|
||||
#endif
|
||||
char padding[512];
|
||||
};
|
||||
#ifdef __MigPackStructs
|
||||
#pragma pack()
|
||||
#endif
|
||||
|
||||
struct ExceptionParameters {
|
||||
ExceptionParameters() : count(0) {}
|
||||
|
@ -104,18 +121,24 @@ struct ExceptionParameters {
|
|||
thread_state_flavor_t flavors[EXC_TYPES_COUNT];
|
||||
};
|
||||
|
||||
#ifdef __MigPackStructs
|
||||
#pragma pack(4)
|
||||
#endif
|
||||
struct ExceptionReplyMessage {
|
||||
mach_msg_header_t header;
|
||||
NDR_record_t ndr;
|
||||
kern_return_t return_code;
|
||||
};
|
||||
#ifdef __MigPackStructs
|
||||
#pragma pack()
|
||||
#endif
|
||||
|
||||
// Only catch these three exceptions. The other ones are nebulously defined
|
||||
// and may result in treating a non-fatal exception as fatal.
|
||||
exception_mask_t s_exception_mask = EXC_MASK_BAD_ACCESS |
|
||||
EXC_MASK_BAD_INSTRUCTION | EXC_MASK_ARITHMETIC | EXC_MASK_BREAKPOINT;
|
||||
|
||||
#if !TARGET_OS_IPHONE
|
||||
#if !TARGET_OS_IPHONE && !TARGET_OSX_USE_64BIT_EXCEPTIONS
|
||||
extern "C" {
|
||||
// Forward declarations for functions that need "C" style compilation
|
||||
boolean_t exc_server(mach_msg_header_t* request,
|
||||
|
@ -136,13 +159,28 @@ extern "C" {
|
|||
kern_return_t ForwardException(mach_port_t task,
|
||||
mach_port_t failed_thread,
|
||||
exception_type_t exception,
|
||||
#if TARGET_OSX_USE_64BIT_EXCEPTIONS
|
||||
mach_exception_data_t code,
|
||||
#else
|
||||
exception_data_t code,
|
||||
#endif
|
||||
mach_msg_type_number_t code_count);
|
||||
|
||||
#if TARGET_OS_IPHONE || TARGET_OSX_USE_64BIT_EXCEPTIONS
|
||||
|
||||
#if TARGET_OS_IPHONE
|
||||
// no idea if this is still correct; this is the value the old code had
|
||||
#define MACH_EXCEPTION_RAISE_RPC 2401
|
||||
#elif TARGET_OSX_USE_64BIT_EXCEPTIONS
|
||||
#define MACH_EXCEPTION_RAISE_RPC 2405
|
||||
#else
|
||||
#error Need a value for MACH_EXCEPTION_RAISE_RPC
|
||||
#endif
|
||||
|
||||
// Implementation is based on the implementation generated by mig.
|
||||
boolean_t breakpad_exc_server(mach_msg_header_t* InHeadP,
|
||||
mach_msg_header_t* OutHeadP) {
|
||||
mach_msg_header_t* OutHeadP)
|
||||
{
|
||||
OutHeadP->msgh_bits =
|
||||
MACH_MSGH_BITS(MACH_MSGH_BITS_REMOTE(InHeadP->msgh_bits), 0);
|
||||
OutHeadP->msgh_remote_port = InHeadP->msgh_remote_port;
|
||||
|
@ -151,7 +189,7 @@ boolean_t breakpad_exc_server(mach_msg_header_t* InHeadP,
|
|||
OutHeadP->msgh_local_port = MACH_PORT_NULL;
|
||||
OutHeadP->msgh_id = InHeadP->msgh_id + 100;
|
||||
|
||||
if (InHeadP->msgh_id != 2401) {
|
||||
if (InHeadP->msgh_id != MACH_EXCEPTION_RAISE_RPC) {
|
||||
((mig_reply_error_t*)OutHeadP)->NDR = NDR_record;
|
||||
((mig_reply_error_t*)OutHeadP)->RetCode = MIG_BAD_ID;
|
||||
return FALSE;
|
||||
|
@ -170,7 +208,11 @@ boolean_t breakpad_exc_server(mach_msg_header_t* InHeadP,
|
|||
NDR_record_t NDR;
|
||||
exception_type_t exception;
|
||||
mach_msg_type_number_t codeCnt;
|
||||
#if TARGET_OSX_USE_64BIT_EXCEPTIONS
|
||||
int64_t code[2];
|
||||
#else
|
||||
integer_t code[2];
|
||||
#endif
|
||||
mach_msg_trailer_t trailer;
|
||||
} Request;
|
||||
|
||||
|
@ -197,7 +239,93 @@ boolean_t breakpad_exc_server(mach_msg_header_t* InHeadP,
|
|||
OutP->NDR = NDR_record;
|
||||
return TRUE;
|
||||
}
|
||||
#else
|
||||
|
||||
#if TARGET_OSX_USE_64BIT_EXCEPTIONS
|
||||
// Implementation is based on the implementation generated by mig.
|
||||
kern_return_t breakpad_exception_raise (mach_port_t exception_port,
|
||||
mach_port_t thread,
|
||||
mach_port_t task,
|
||||
exception_type_t exception,
|
||||
mach_exception_data_t code,
|
||||
mach_msg_type_number_t codeCnt)
|
||||
{
|
||||
#ifdef __MigPackStructs
|
||||
#pragma pack(4)
|
||||
#endif
|
||||
typedef struct {
|
||||
mach_msg_header_t Head;
|
||||
/* start of the kernel processed data */
|
||||
mach_msg_body_t msgh_body;
|
||||
mach_msg_port_descriptor_t thread;
|
||||
mach_msg_port_descriptor_t task;
|
||||
/* end of the kernel processed data */
|
||||
NDR_record_t NDR;
|
||||
exception_type_t exception;
|
||||
mach_msg_type_number_t codeCnt;
|
||||
int64_t code[2];
|
||||
} Request;
|
||||
#ifdef __MigPackStructs
|
||||
#pragma pack()
|
||||
#endif
|
||||
|
||||
#ifdef __MigPackStructs
|
||||
#pragma pack(4)
|
||||
#endif
|
||||
typedef struct {
|
||||
mach_msg_header_t Head;
|
||||
NDR_record_t NDR;
|
||||
kern_return_t RetCode;
|
||||
mach_msg_trailer_t trailer;
|
||||
} Reply;
|
||||
#ifdef __MigPackStructs
|
||||
#pragma pack()
|
||||
#endif
|
||||
|
||||
Request In;
|
||||
Request *InP = &In;
|
||||
|
||||
mach_msg_return_t msg_result;
|
||||
unsigned int msgh_size;
|
||||
|
||||
InP->msgh_body.msgh_descriptor_count = 2;
|
||||
InP->thread.name = thread;
|
||||
InP->thread.disposition = 19;
|
||||
InP->thread.type = MACH_MSG_PORT_DESCRIPTOR;
|
||||
InP->task.name = task;
|
||||
InP->task.disposition = 19;
|
||||
InP->task.type = MACH_MSG_PORT_DESCRIPTOR;
|
||||
InP->NDR = NDR_record;
|
||||
|
||||
InP->exception = exception;
|
||||
|
||||
if (codeCnt > 2) {
|
||||
{ return MIG_ARRAY_TOO_LARGE; }
|
||||
}
|
||||
(void)memcpy((char *) InP->code, (const char *) code, 8 * codeCnt);
|
||||
|
||||
msgh_size = (mach_msg_size_t)(sizeof(Request) - 16) + ((8 * codeCnt));
|
||||
InP->Head.msgh_bits = MACH_MSGH_BITS_COMPLEX|
|
||||
MACH_MSGH_BITS(19, MACH_MSG_TYPE_MAKE_SEND_ONCE);
|
||||
/* msgh_size passed as argument */
|
||||
InP->Head.msgh_remote_port = exception_port;
|
||||
InP->Head.msgh_local_port = mig_get_reply_port();
|
||||
InP->Head.msgh_id = 2405;
|
||||
|
||||
msg_result = mach_msg(&InP->Head, MACH_SEND_MSG|MACH_RCV_MSG|MACH_MSG_OPTION_NONE,
|
||||
msgh_size, (mach_msg_size_t)sizeof(Reply), InP->Head.msgh_local_port,
|
||||
MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
|
||||
if (msg_result != MACH_MSG_SUCCESS) {
|
||||
return msg_result;
|
||||
}
|
||||
|
||||
return KERN_SUCCESS;
|
||||
}
|
||||
#endif
|
||||
|
||||
#else /* not TARGET_OS_IPHONE || TARGET_OSX_USE_64BIT_EXCEPTIONS */
|
||||
|
||||
#define breakpad_exception_raise exception_raise
|
||||
|
||||
boolean_t breakpad_exc_server(mach_msg_header_t* request,
|
||||
mach_msg_header_t* reply) {
|
||||
return exc_server(request, reply);
|
||||
|
@ -414,7 +542,11 @@ bool ExceptionHandler::WriteMinidumpWithException(int exception_type,
|
|||
|
||||
kern_return_t ForwardException(mach_port_t task, mach_port_t failed_thread,
|
||||
exception_type_t exception,
|
||||
#if TARGET_OSX_USE_64BIT_EXCEPTIONS
|
||||
mach_exception_data_t code,
|
||||
#else
|
||||
exception_data_t code,
|
||||
#endif
|
||||
mach_msg_type_number_t code_count) {
|
||||
// At this time, we should have called Uninstall() on the exception handler
|
||||
// so that the current exception ports are the ones that we should be
|
||||
|
@ -448,11 +580,25 @@ kern_return_t ForwardException(mach_port_t task, mach_port_t failed_thread,
|
|||
mach_port_t target_port = current.ports[found];
|
||||
exception_behavior_t target_behavior = current.behaviors[found];
|
||||
|
||||
#if TARGET_OSX_USE_64BIT_EXCEPTIONS
|
||||
boolean_t code64 = (target_behavior & MACH_EXCEPTION_CODES) != 0;
|
||||
target_behavior = target_behavior & ~MACH_EXCEPTION_CODES;
|
||||
|
||||
if (target_behavior == EXCEPTION_DEFAULT && !code64) {
|
||||
// we could in theory handle this by downcasting codes to int_t and calling
|
||||
// exception_raise instead of breakpad_exception_raise with the int_t array
|
||||
// below; but, really, we end up aborting the process before we ever
|
||||
// get here, so why bother?
|
||||
fprintf(stderr, "*** EXCEPTION_DEFAULT but without MACH_EXCEPTION_CODES bit, we don't have code to forward this");
|
||||
return KERN_FAILURE;
|
||||
}
|
||||
#endif
|
||||
|
||||
kern_return_t result;
|
||||
switch (target_behavior) {
|
||||
case EXCEPTION_DEFAULT:
|
||||
result = exception_raise(target_port, failed_thread, task, exception,
|
||||
code, code_count);
|
||||
result = breakpad_exception_raise(target_port, failed_thread, task, exception,
|
||||
code, code_count);
|
||||
break;
|
||||
|
||||
default:
|
||||
|
@ -480,24 +626,83 @@ void* ExceptionHandler::WaitForMessage(void* exception_handler_class) {
|
|||
self->handler_port_,
|
||||
MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
|
||||
|
||||
if (result != KERN_SUCCESS)
|
||||
continue;
|
||||
|
||||
if (result == KERN_SUCCESS) {
|
||||
// Uninstall our handler so that we don't get in a loop if the process of
|
||||
// writing out a minidump causes an exception. However, if the exception
|
||||
// was caused by a fork'd process, don't uninstall things
|
||||
// Uninstall our handler so that we don't get in a loop if the process of
|
||||
// writing out a minidump causes an exception. However, if the exception
|
||||
// was caused by a fork'd process, don't uninstall things
|
||||
|
||||
// If the actual exception code is zero, then we're calling this handler
|
||||
// in a way that indicates that we want to either exit this thread or
|
||||
// generate a minidump
|
||||
//
|
||||
// While reporting, all threads (except this one) must be suspended
|
||||
// to avoid misleading stacks. If appropriate they will be resumed
|
||||
// afterwards.
|
||||
if (!receive.exception) {
|
||||
// Don't touch self, since this message could have been sent
|
||||
// from its destructor.
|
||||
if (receive.header.msgh_id == kShutdownMessage)
|
||||
return NULL;
|
||||
// If the actual exception code is zero, then we're calling this handler
|
||||
// in a way that indicates that we want to either exit this thread or
|
||||
// generate a minidump
|
||||
//
|
||||
// While reporting, all threads (except this one) must be suspended
|
||||
// to avoid misleading stacks. If appropriate they will be resumed
|
||||
// afterwards.
|
||||
if (!receive.exception) {
|
||||
// Don't touch self, since this message could have been sent
|
||||
// from its destructor.
|
||||
if (receive.header.msgh_id == kShutdownMessage)
|
||||
return NULL;
|
||||
|
||||
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;
|
||||
#elif defined(__arm__)
|
||||
exception_code = EXC_ARM_BREAKPOINT;
|
||||
#else
|
||||
#error architecture not supported
|
||||
#endif
|
||||
}
|
||||
|
||||
// Write out the dump and save the result for later retrieval
|
||||
self->last_minidump_write_result_ =
|
||||
self->WriteMinidumpWithException(exception_type, exception_code,
|
||||
0, NULL, thread,
|
||||
false, false);
|
||||
|
||||
#if USE_PROTECTED_ALLOCATIONS
|
||||
if (gBreakpadAllocator)
|
||||
gBreakpadAllocator->Protect();
|
||||
#endif
|
||||
|
||||
self->ResumeThreads();
|
||||
|
||||
if (self->use_minidump_write_mutex_)
|
||||
pthread_mutex_unlock(&self->minidump_write_mutex_);
|
||||
} else {
|
||||
// When forking a child process with the exception handler installed,
|
||||
// if the child crashes, it will send the exception back to the parent
|
||||
// process. The check for task == self_task() ensures that only
|
||||
// exceptions that occur in the parent process are caught and
|
||||
// processed. If the exception was not caused by this task, we
|
||||
// still need to call into the exception server and have it return
|
||||
// KERN_FAILURE (see catch_exception_raise) in order for the kernel
|
||||
// to move onto the host exception handler for the child task
|
||||
if (receive.task.name == mach_task_self()) {
|
||||
#if TARGET_OSX_USE_64BIT_EXCEPTIONS
|
||||
int64_t subcode = 0;
|
||||
#else
|
||||
int subcode = 0;
|
||||
#endif
|
||||
|
||||
if (receive.exception == EXC_BAD_ACCESS && receive.code_count > 1)
|
||||
subcode = receive.code[1];
|
||||
|
||||
self->SuspendThreads();
|
||||
|
||||
|
@ -506,64 +711,14 @@ void* ExceptionHandler::WaitForMessage(void* exception_handler_class) {
|
|||
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;
|
||||
#elif defined(__arm__)
|
||||
exception_code = EXC_ARM_BREAKPOINT;
|
||||
#else
|
||||
#error architecture not supported
|
||||
#endif
|
||||
}
|
||||
|
||||
// Write out the dump and save the result for later retrieval
|
||||
self->last_minidump_write_result_ =
|
||||
self->WriteMinidumpWithException(exception_type, exception_code,
|
||||
0, NULL, thread,
|
||||
false, false);
|
||||
|
||||
#if USE_PROTECTED_ALLOCATIONS
|
||||
if (gBreakpadAllocator)
|
||||
gBreakpadAllocator->Protect();
|
||||
#endif
|
||||
|
||||
self->ResumeThreads();
|
||||
|
||||
if (self->use_minidump_write_mutex_)
|
||||
pthread_mutex_unlock(&self->minidump_write_mutex_);
|
||||
} else {
|
||||
// When forking a child process with the exception handler installed,
|
||||
// if the child crashes, it will send the exception back to the parent
|
||||
// process. The check for task == self_task() ensures that only
|
||||
// exceptions that occur in the parent process are caught and
|
||||
// processed. If the exception was not caused by this task, we
|
||||
// still need to call into the exception server and have it return
|
||||
// KERN_FAILURE (see catch_exception_raise) in order for the kernel
|
||||
// to move onto the host exception handler for the child task
|
||||
if (receive.task.name == mach_task_self()) {
|
||||
self->SuspendThreads();
|
||||
|
||||
#if USE_PROTECTED_ALLOCATIONS
|
||||
if (gBreakpadAllocator)
|
||||
gBreakpadAllocator->Unprotect();
|
||||
#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, NULL, receive.thread.name,
|
||||
true, false);
|
||||
|
||||
// Note! We passed "true" to the exit_after_write param above;
|
||||
// this code will never execute, because the process will have exited.
|
||||
|
||||
#if USE_PROTECTED_ALLOCATIONS
|
||||
// This may have become protected again within
|
||||
// WriteMinidumpWithException, but it needs to be unprotected for
|
||||
|
@ -578,19 +733,20 @@ void* ExceptionHandler::WaitForMessage(void* exception_handler_class) {
|
|||
if (gBreakpadAllocator)
|
||||
gBreakpadAllocator->Protect();
|
||||
#endif
|
||||
}
|
||||
// Pass along the exception to the server, which will setup the
|
||||
// message and call catch_exception_raise() and put the return
|
||||
// code into the reply.
|
||||
ExceptionReplyMessage reply;
|
||||
if (!breakpad_exc_server(&receive.header, &reply.header))
|
||||
exit(1);
|
||||
} // if (receive.task.name == mach_task_self())
|
||||
|
||||
// Send a reply and exit
|
||||
mach_msg(&(reply.header), MACH_SEND_MSG,
|
||||
reply.header.msgh_size, 0, MACH_PORT_NULL,
|
||||
MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
|
||||
// Pass along the exception to the server, which will setup the
|
||||
// message and call catch_exception_raise() and put the return
|
||||
// code into the reply.
|
||||
ExceptionReplyMessage reply;
|
||||
if (!breakpad_exc_server(&receive.header, &reply.header)) {
|
||||
exit(1);
|
||||
}
|
||||
|
||||
// Send a reply
|
||||
mach_msg(&(reply.header), MACH_SEND_MSG,
|
||||
reply.header.msgh_size, 0, MACH_PORT_NULL,
|
||||
MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -622,6 +778,7 @@ bool ExceptionHandler::InstallHandler() {
|
|||
if (gProtectedData.handler != NULL) {
|
||||
return false;
|
||||
}
|
||||
|
||||
#if TARGET_OS_IPHONE
|
||||
if (!IsOutOfProcess()) {
|
||||
struct sigaction sa;
|
||||
|
@ -670,7 +827,12 @@ bool ExceptionHandler::InstallHandler() {
|
|||
// Setup the exception ports on this task
|
||||
if (result == KERN_SUCCESS)
|
||||
result = task_set_exception_ports(current_task, s_exception_mask,
|
||||
handler_port_, EXCEPTION_DEFAULT,
|
||||
handler_port_,
|
||||
#if TARGET_OSX_USE_64BIT_EXCEPTIONS
|
||||
EXCEPTION_DEFAULT | MACH_EXCEPTION_CODES,
|
||||
#else
|
||||
EXCEPTION_DEFAULT,
|
||||
#endif
|
||||
THREAD_STATE_NONE);
|
||||
|
||||
installed_exception_handler_ = (result == KERN_SUCCESS);
|
||||
|
|
Загрузка…
Ссылка в новой задаче