mirror of
https://github.com/yuzu-emu/breakpad.git
synced 2025-01-10 22:55:33 +00:00
- Fixes the bug with a fork()'d child's exception being caught in the parent process
- Only looks for EXC_MASK_BAD_ACCESS, EXC_MASK_BAD_INSTRUCTION, and EXC_MASK_ARITHMETIC exceptions - Adds try/catch blocks around "new" for bad_alloc - Uses map.find() rather than map[] notation so as not to create extraneous objects - Creates the exception watching thread as detached r=mmentovai git-svn-id: http://google-breakpad.googlecode.com/svn/trunk@117 4c0a9323-5329-0410-9bdc-e9ce6186880e
This commit is contained in:
parent
3a9a38a29e
commit
600e56bc39
|
@ -224,12 +224,15 @@ kern_return_t ForwardException(mach_port_t task, mach_port_t failed_thread,
|
||||||
exception_type_t exception,
|
exception_type_t exception,
|
||||||
exception_data_t code,
|
exception_data_t code,
|
||||||
mach_msg_type_number_t code_count) {
|
mach_msg_type_number_t code_count) {
|
||||||
ExceptionParameters *previous = (*s_exception_parameter_map)[pthread_self()];
|
map<pthread_t, ExceptionParameters *>::iterator previous_location =
|
||||||
|
s_exception_parameter_map->find(pthread_self());
|
||||||
|
|
||||||
// If we don't have the previous data, we need to just exit
|
// If we don't have the previous data, we need to just exit
|
||||||
if (!previous)
|
if (previous_location == (*s_exception_parameter_map).end())
|
||||||
exit(exception);
|
exit(exception);
|
||||||
|
|
||||||
|
ExceptionParameters *previous = (*previous_location).second;
|
||||||
|
|
||||||
// Find the first exception handler that matches the exception
|
// Find the first exception handler that matches the exception
|
||||||
unsigned int found;
|
unsigned int found;
|
||||||
for (found = 0; found < previous->count; ++found) {
|
for (found = 0; found < previous->count; ++found) {
|
||||||
|
@ -312,8 +315,14 @@ void *ExceptionHandler::WaitForMessage(void *exception_handler_class) {
|
||||||
|
|
||||||
// Save a pointer to our instance so that it will be available in the
|
// Save a pointer to our instance so that it will be available in the
|
||||||
// routines that are called from exc_server();
|
// routines that are called from exc_server();
|
||||||
if (!s_exception_parameter_map)
|
if (!s_exception_parameter_map) {
|
||||||
s_exception_parameter_map = new map<pthread_t, ExceptionParameters *>;
|
try {
|
||||||
|
s_exception_parameter_map = new map<pthread_t, ExceptionParameters *>;
|
||||||
|
}
|
||||||
|
catch (std::bad_alloc) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
(*s_exception_parameter_map)[pthread_self()] = self->previous_;
|
(*s_exception_parameter_map)[pthread_self()] = self->previous_;
|
||||||
|
|
||||||
|
@ -328,8 +337,10 @@ void *ExceptionHandler::WaitForMessage(void *exception_handler_class) {
|
||||||
|
|
||||||
if (result == KERN_SUCCESS) {
|
if (result == KERN_SUCCESS) {
|
||||||
// Uninstall our handler so that we don't get in a loop if the process of
|
// Uninstall our handler so that we don't get in a loop if the process of
|
||||||
// writing out a minidump causes an exception.
|
// writing out a minidump causes an exception. However, if the exception
|
||||||
self->UninstallHandler();
|
// was caused by a fork'd process, don't uninstall things
|
||||||
|
if (receive.task.name == mach_task_self())
|
||||||
|
self->UninstallHandler();
|
||||||
|
|
||||||
// If the actual exception code is zero, then we're calling this handler
|
// 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
|
// in a way that indicates that we want to either exit this thread or
|
||||||
|
@ -344,21 +355,31 @@ void *ExceptionHandler::WaitForMessage(void *exception_handler_class) {
|
||||||
if (self->use_minidump_write_mutex_)
|
if (self->use_minidump_write_mutex_)
|
||||||
pthread_mutex_unlock(&self->minidump_write_mutex_);
|
pthread_mutex_unlock(&self->minidump_write_mutex_);
|
||||||
} else {
|
} else {
|
||||||
// Generate the minidump with the exception data.
|
// When forking a child process with the exception handler installed,
|
||||||
self->WriteMinidumpWithException(receive.exception, receive.code[0],
|
// if the child crashes, it will send the exception back to the parent
|
||||||
receive.thread.name);
|
// process. The check for task == self_task() ensures that only
|
||||||
|
// exceptions that occur in the parent process are caught and
|
||||||
// Pass along the exception to the server, which will setup the message
|
// processed.
|
||||||
// and call catch_exception_raise() and put the KERN_SUCCESS into the
|
if (receive.task.name == mach_task_self()) {
|
||||||
// reply.
|
|
||||||
ExceptionReplyMessage reply;
|
// Generate the minidump with the exception data.
|
||||||
if (!exc_server(&receive.header, &reply.header))
|
self->WriteMinidumpWithException(receive.exception, receive.code[0],
|
||||||
exit(1);
|
receive.thread.name);
|
||||||
|
|
||||||
|
// Pass along the exception to the server, which will setup the
|
||||||
|
// message and call catch_exception_raise() and put the KERN_SUCCESS
|
||||||
|
// into the reply.
|
||||||
|
ExceptionReplyMessage reply;
|
||||||
|
if (!exc_server(&receive.header, &reply.header))
|
||||||
|
exit(1);
|
||||||
|
|
||||||
// Send a reply and exit
|
// Send a reply and exit
|
||||||
result = mach_msg(&(reply.header), MACH_SEND_MSG,
|
result = mach_msg(&(reply.header), MACH_SEND_MSG,
|
||||||
reply.header.msgh_size, 0, MACH_PORT_NULL,
|
reply.header.msgh_size, 0, MACH_PORT_NULL,
|
||||||
MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
|
MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
|
||||||
|
} else {
|
||||||
|
// An exception occurred in a child process
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -367,12 +388,19 @@ void *ExceptionHandler::WaitForMessage(void *exception_handler_class) {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ExceptionHandler::InstallHandler() {
|
bool ExceptionHandler::InstallHandler() {
|
||||||
// Get the actual previous data
|
// Only catch these three exceptions. The other ones are nebulously defined
|
||||||
exception_mask_t exception_mask = EXC_MASK_ALL &
|
// and may result in treating a non-fatal exception as fatal.
|
||||||
~(EXC_MASK_BREAKPOINT | EXC_MASK_MACH_SYSCALL |
|
exception_mask_t exception_mask = EXC_MASK_BAD_ACCESS |
|
||||||
EXC_MASK_SYSCALL | EXC_MASK_RPC_ALERT);
|
EXC_MASK_BAD_INSTRUCTION | EXC_MASK_ARITHMETIC;
|
||||||
|
|
||||||
previous_ = new ExceptionParameters();
|
try {
|
||||||
|
previous_ = new ExceptionParameters();
|
||||||
|
}
|
||||||
|
catch (std::bad_alloc) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Save the current exception ports so that we can forward to them
|
||||||
previous_->count = EXC_TYPES_COUNT;
|
previous_->count = EXC_TYPES_COUNT;
|
||||||
mach_port_t current_task = mach_task_self();
|
mach_port_t current_task = mach_task_self();
|
||||||
kern_return_t result = task_get_exception_ports(current_task, exception_mask,
|
kern_return_t result = task_get_exception_ports(current_task, exception_mask,
|
||||||
|
@ -436,10 +464,14 @@ bool ExceptionHandler::Setup(bool install_handler) {
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (result == KERN_SUCCESS) {
|
if (result == KERN_SUCCESS) {
|
||||||
// Install the handler in its own thread
|
// Install the handler in its own thread, detached as we won't be joining.
|
||||||
if (pthread_create(&handler_thread_, NULL, &WaitForMessage, this) == 0) {
|
pthread_attr_t attr;
|
||||||
pthread_detach(handler_thread_);
|
pthread_attr_init(&attr);
|
||||||
}
|
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
|
||||||
|
int thread_create_result = pthread_create(&handler_thread_, &attr,
|
||||||
|
&WaitForMessage, this);
|
||||||
|
pthread_attr_destroy(&attr);
|
||||||
|
result = thread_create_result ? KERN_FAILURE : KERN_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
return result == KERN_SUCCESS ? true : false;
|
return result == KERN_SUCCESS ? true : false;
|
||||||
|
|
Loading…
Reference in a new issue