Files
Xenia-Canary/src/xenia/base/exception_handler_win.cc

143 lines
4.7 KiB
C++

/**
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2015 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#include "xenia/base/exception_handler.h"
#include "xenia/base/assert.h"
#include "xenia/base/math.h"
#include "xenia/base/platform_win.h"
namespace xe {
// Handle of the added VectoredExceptionHandler.
void* veh_handle_ = nullptr;
// Handle of the added VectoredContinueHandler.
void* vch_handle_ = nullptr;
// This can be as large as needed, but isn't often needed.
// As we will be sometimes firing many exceptions we want to avoid having to
// scan the table too much or invoke many custom handlers.
constexpr size_t kMaxHandlerCount = 8;
// All custom handlers, left-aligned and null terminated.
// Executed in order.
std::pair<ExceptionHandler::Handler, void*> handlers_[kMaxHandlerCount];
LONG CALLBACK ExceptionHandlerCallback(PEXCEPTION_POINTERS ex_info) {
// Visual Studio SetThreadName.
if (ex_info->ExceptionRecord->ExceptionCode == 0x406D1388) {
return EXCEPTION_CONTINUE_SEARCH;
}
// TODO(benvanik): avoid this by mapping X64Context virtual?
X64Context thread_context;
thread_context.rip = ex_info->ContextRecord->Rip;
thread_context.eflags = ex_info->ContextRecord->EFlags;
std::memcpy(thread_context.int_registers, &ex_info->ContextRecord->Rax,
sizeof(thread_context.int_registers));
std::memcpy(thread_context.xmm_registers, &ex_info->ContextRecord->Xmm0,
sizeof(thread_context.xmm_registers));
// https://msdn.microsoft.com/en-us/library/ms679331(v=vs.85).aspx
// https://msdn.microsoft.com/en-us/library/aa363082(v=vs.85).aspx
Exception ex;
switch (ex_info->ExceptionRecord->ExceptionCode) {
case STATUS_ILLEGAL_INSTRUCTION:
ex.InitializeIllegalInstruction(&thread_context);
break;
case STATUS_ACCESS_VIOLATION: {
Exception::AccessViolationOperation access_violation_operation;
switch (ex_info->ExceptionRecord->ExceptionInformation[0]) {
case 0:
access_violation_operation =
Exception::AccessViolationOperation::kRead;
break;
case 1:
access_violation_operation =
Exception::AccessViolationOperation::kWrite;
break;
default:
access_violation_operation =
Exception::AccessViolationOperation::kUnknown;
break;
}
ex.InitializeAccessViolation(
&thread_context, ex_info->ExceptionRecord->ExceptionInformation[1],
access_violation_operation);
} break;
default:
// Unknown/unhandled type.
return EXCEPTION_CONTINUE_SEARCH;
}
for (size_t i = 0; i < xe::countof(handlers_) && handlers_[i].first; ++i) {
if (handlers_[i].first(&ex, handlers_[i].second)) {
// Exception handled.
// TODO(benvanik): update all thread state? Dirty flags?
ex_info->ContextRecord->Rip = thread_context.rip;
return EXCEPTION_CONTINUE_EXECUTION;
}
}
return EXCEPTION_CONTINUE_SEARCH;
}
void ExceptionHandler::Install(Handler fn, void* data) {
if (!veh_handle_) {
veh_handle_ = AddVectoredExceptionHandler(1, ExceptionHandlerCallback);
if (IsDebuggerPresent()) {
// TODO(benvanik): do we need a continue handler if a debugger is
// attached?
// vch_handle_ = AddVectoredContinueHandler(1, ExceptionHandlerCallback);
}
}
for (size_t i = 0; i < xe::countof(handlers_); ++i) {
if (!handlers_[i].first) {
handlers_[i].first = fn;
handlers_[i].second = data;
return;
}
}
assert_always("Too many exception handlers installed");
}
void ExceptionHandler::Uninstall(Handler fn, void* data) {
for (size_t i = 0; i < xe::countof(handlers_); ++i) {
if (handlers_[i].first == fn && handlers_[i].second == data) {
for (; i < xe::countof(handlers_) - 1; ++i) {
handlers_[i] = handlers_[i + 1];
}
handlers_[i].first = nullptr;
handlers_[i].second = nullptr;
break;
}
}
bool has_any = false;
for (size_t i = 0; i < xe::countof(handlers_); ++i) {
if (handlers_[i].first) {
has_any = true;
break;
}
}
if (!has_any) {
if (veh_handle_) {
RemoveVectoredExceptionHandler(veh_handle_);
veh_handle_ = nullptr;
}
if (vch_handle_) {
RemoveVectoredContinueHandler(vch_handle_);
vch_handle_ = nullptr;
}
}
}
} // namespace xe