Add first chance exception handler API

This change adds the option for Breakpad hosts to register a callback
that gets the first chance to handle an exception. The handler will 
return true if it handled the exception and false otherwise.

The primary use case is V8's trap-based bounds checking support for
WebAssembly.

Bug:
Change-Id: I5aa5b87d1229f1cef905a00404fa2027ee86be56
Reviewed-on: https://chromium-review.googlesource.com/509994
Reviewed-by: Mark Mentovai <mark@chromium.org>
This commit is contained in:
Eric Holk 2017-06-19 10:06:28 -07:00 committed by Mark Mentovai
parent c142362a6c
commit 1628d99f7b
3 changed files with 44 additions and 0 deletions

View file

@ -218,6 +218,7 @@ pthread_mutex_t g_handler_stack_mutex_ = PTHREAD_MUTEX_INITIALIZER;
// time can use |g_crash_context_|.
ExceptionHandler::CrashContext g_crash_context_;
FirstChanceHandler g_first_chance_handler_ = nullptr;
} // namespace
// Runs before crashing: normal context.
@ -331,6 +332,18 @@ void ExceptionHandler::RestoreHandlersLocked() {
// Runs on the crashing thread.
// static
void ExceptionHandler::SignalHandler(int sig, siginfo_t* info, void* uc) {
// Give the first chance handler a chance to recover from this signal
//
// This is primarily used by V8. V8 uses guard regions to guarantee memory
// safety in WebAssembly. This means some signals might be expected if they
// originate from Wasm code while accessing the guard region. We give V8 the
// chance to handle and recover from these signals first.
if (g_first_chance_handler_ != nullptr &&
g_first_chance_handler_(sig, info, uc)) {
return;
}
// All the exception signals are blocked at this point.
pthread_mutex_lock(&g_handler_stack_mutex_);
@ -782,4 +795,8 @@ bool ExceptionHandler::WriteMinidumpForChild(pid_t child,
return callback ? callback(descriptor, callback_context, true) : true;
}
void SetFirstChanceExceptionHandler(FirstChanceHandler callback) {
g_first_chance_handler_ = callback;
}
} // namespace google_breakpad

View file

@ -273,6 +273,10 @@ class ExceptionHandler {
AppMemoryList app_memory_list_;
};
typedef bool (*FirstChanceHandler)(int, void*, void*);
void SetFirstChanceExceptionHandler(FirstChanceHandler callback);
} // namespace google_breakpad
#endif // CLIENT_LINUX_HANDLER_EXCEPTION_HANDLER_H_

View file

@ -1177,3 +1177,26 @@ TEST(ExceptionHandlerTest, WriteMinidumpForChild) {
close(fds[1]);
unlink(minidump_filename.c_str());
}
namespace {
const int kSimpleFirstChanceReturnStatus = 42;
bool SimpleFirstChanceHandler(int, void*, void*) {
exit(kSimpleFirstChanceReturnStatus);
}
}
TEST(ExceptionHandlerTest, FirstChanceHandlerRuns) {
AutoTempDir temp_dir;
const pid_t child = fork();
if (child == 0) {
ExceptionHandler handler(
MinidumpDescriptor(temp_dir.path()), NULL, NULL, NULL, true, -1);
google_breakpad::SetFirstChanceExceptionHandler(SimpleFirstChanceHandler);
DoNullPointerDereference();
}
int status;
ASSERT_NE(HANDLE_EINTR(waitpid(child, &status, 0)), -1);
ASSERT_TRUE(WIFEXITED(status));
ASSERT_EQ(kSimpleFirstChanceReturnStatus, WEXITSTATUS(status));
}