From 8e716a3a7eec9f03ab42b78d27a362b9bce2817d Mon Sep 17 00:00:00 2001 From: "Dr. Chat" Date: Fri, 11 Sep 2015 17:07:12 -0500 Subject: [PATCH 1/3] Exception handler abstraction class --- src/xenia/base/exception_handler.h | 49 ++++++++++++++++++++ src/xenia/base/exception_handler_win.cc | 59 +++++++++++++++++++++++++ 2 files changed, 108 insertions(+) create mode 100644 src/xenia/base/exception_handler.h create mode 100644 src/xenia/base/exception_handler_win.cc diff --git a/src/xenia/base/exception_handler.h b/src/xenia/base/exception_handler.h new file mode 100644 index 000000000..8c387eb12 --- /dev/null +++ b/src/xenia/base/exception_handler.h @@ -0,0 +1,49 @@ +/** + ****************************************************************************** + * 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. * + ****************************************************************************** + */ + +#ifndef XENIA_BASE_EXCEPTION_HANDLER_H_ +#define XENIA_BASE_EXCEPTION_HANDLER_H_ + +#include +#include + +namespace xe { +class ExceptionHandler { + public: + struct Info { + enum { + kInvalidException = 0, + kAccessViolation, + } code = kInvalidException; + + uint64_t pc = 0; // Program counter address. RIP on x64. + uint64_t fault_address = + 0; // In case of AV, address that was read from/written to. + + void* thread_context = nullptr; // Platform-specific thread context info. + }; + typedef std::function Handler; + + // Static initialization. Only call this once! + static bool Initialize(); + + // Install an exception handler. Returns an ID which you can save to remove + // this later. This will install the exception handler in the last place + // on Windows. + static uint32_t Install(Handler fn); + static bool Remove(uint32_t id); + + static const std::vector& handlers() { return handlers_; } + + private: + static std::vector handlers_; +}; +}; // namespace xe + +#endif // XENIA_BASE_EXCEPTION_HANDLER_H_ \ No newline at end of file diff --git a/src/xenia/base/exception_handler_win.cc b/src/xenia/base/exception_handler_win.cc new file mode 100644 index 000000000..0c432ace3 --- /dev/null +++ b/src/xenia/base/exception_handler_win.cc @@ -0,0 +1,59 @@ +/** + ****************************************************************************** + * 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/platform_win.h" + +namespace xe { +std::vector ExceptionHandler::handlers_; + +LONG CALLBACK ExceptionHandlerCallback(PEXCEPTION_POINTERS ex_info) { + // Visual Studio SetThreadName + if (ex_info->ExceptionRecord->ExceptionCode == 0x406D1388) { + return EXCEPTION_CONTINUE_SEARCH; + } + + auto code = ex_info->ExceptionRecord->ExceptionCode; + ExceptionHandler::Info info; + info.pc = ex_info->ContextRecord->Rip; + info.thread_context = ex_info->ContextRecord; + + switch (code) { + case STATUS_ACCESS_VIOLATION: + info.code = ExceptionHandler::Info::kAccessViolation; + info.fault_address = ex_info->ExceptionRecord->ExceptionInformation[1]; + break; + } + + // Only call a handler if we support this type of exception. + if (info.code != ExceptionHandler::Info::kInvalidException) { + for (auto handler : ExceptionHandler::handlers()) { + if (handler(&info)) { + // Exception handled. + return EXCEPTION_CONTINUE_EXECUTION; + } + } + } + + return EXCEPTION_CONTINUE_SEARCH; +} + +bool ExceptionHandler::Initialize() { + AddVectoredExceptionHandler(0, ExceptionHandlerCallback); + + // TODO: Do we need a continue handler if a debugger is attached? + return true; +} + +uint32_t ExceptionHandler::Install(std::function fn) { + handlers_.push_back(fn); + return 0; +} +} \ No newline at end of file From cad23cea81526e21650a51e7f025b4f17dd9dfba Mon Sep 17 00:00:00 2001 From: "Dr. Chat" Date: Fri, 11 Sep 2015 17:09:25 -0500 Subject: [PATCH 2/3] Pause the emulator if the guest crashes and the VS debugger isn't attached. --- src/xenia/emulator.cc | 68 +++++++++++++++++++++++++++++++++++++++++++ src/xenia/emulator.h | 3 ++ 2 files changed, 71 insertions(+) diff --git a/src/xenia/emulator.cc b/src/xenia/emulator.cc index ded391eca..87c5d987a 100644 --- a/src/xenia/emulator.cc +++ b/src/xenia/emulator.cc @@ -14,8 +14,11 @@ #include "xenia/apu/audio_system.h" #include "xenia/base/assert.h" #include "xenia/base/clock.h" +#include "xenia/base/debugging.h" +#include "xenia/base/exception_handler.h" #include "xenia/base/logging.h" #include "xenia/base/string.h" +#include "xenia/cpu/backend/code_cache.h" #include "xenia/gpu/graphics_system.h" #include "xenia/hid/input_system.h" #include "xenia/kernel/kernel_state.h" @@ -27,6 +30,8 @@ #include "xenia/vfs/devices/stfs_container_device.h" #include "xenia/vfs/virtual_file_system.h" +#include "el/elements/message_form.h" + DEFINE_double(time_scalar, 1.0, "Scalar used to speed or slow time (1x, 2x, 1/2x, etc)."); @@ -159,6 +164,11 @@ X_STATUS Emulator::Setup(ui::Window* display_window) { Profiler::set_display(std::move(profiler_display)); }); + // Install an exception handler. + ExceptionHandler::Initialize(); + ExceptionHandler::Install( + std::bind(&Emulator::ExceptionCallback, this, std::placeholders::_1)); + return result; } @@ -262,6 +272,64 @@ X_STATUS Emulator::LaunchStfsContainer(std::wstring path) { return CompleteLaunch(path, "game:\\default.xex"); } +bool Emulator::ExceptionCallback(ExceptionHandler::Info* info) { + // Check to see if the exception occurred in guest code. + auto code_cache = processor()->backend()->code_cache(); + auto code_base = code_cache->base_address(); + auto code_end = code_base + code_cache->total_size(); + + if (!debugger()->is_attached() && debugging::IsDebuggerAttached()) { + // If Xenia's debugger isn't attached but another one is, pass it to that + // debugger. + return false; + } + + if (info->pc >= code_base && info->pc < code_end) { + using namespace xe::kernel; + auto global_lock = global_critical_region::AcquireDirect(); + + // Within range. Pause the emulator and eat the exception. + auto threads = kernel_state()->object_table()->GetObjectsByType( + XObject::kTypeThread); + auto cur_thread = XThread::GetCurrentThread(); + for (auto thread : threads) { + if (!thread->is_guest_thread()) { + // Don't pause host threads. + continue; + } + + if (cur_thread == thread.get()) { + continue; + } + + thread->Suspend(nullptr); + } + + if (debugger()->is_attached()) { + // TODO: Send the exception to Xenia's debugger + } + + // Display a dialog telling the user the guest has crashed. + display_window()->loop()->PostSynchronous([&]() { + auto msgbox = new el::elements::MessageForm( + display_window()->root_element(), "none"); + msgbox->Show("Uh-oh!", + "The guest has crashed.\n\n" + "Xenia has now paused itself."); + }); + + // Now suspend ourself (we should be a guest thread). + cur_thread->Suspend(nullptr); + + // We should not arrive here! + assert_always(); + return false; + } + + // Didn't occur in guest code. Let it pass. + return false; +} + X_STATUS Emulator::CompleteLaunch(const std::wstring& path, const std::string& module_path) { // Allow xam to request module loads. diff --git a/src/xenia/emulator.h b/src/xenia/emulator.h index b8b1e5bed..2a370c6c7 100644 --- a/src/xenia/emulator.h +++ b/src/xenia/emulator.h @@ -12,6 +12,7 @@ #include +#include "xenia/base/exception_handler.h" #include "xenia/debug/debugger.h" #include "xenia/kernel/kernel_state.h" #include "xenia/memory.h" @@ -75,6 +76,8 @@ class Emulator { X_STATUS LaunchStfsContainer(std::wstring path); private: + bool ExceptionCallback(ExceptionHandler::Info* info); + X_STATUS CompleteLaunch(const std::wstring& path, const std::string& module_path); From 2983b0c090bb50e570a2a614edb70a0aab340dc7 Mon Sep 17 00:00:00 2001 From: "Dr. Chat" Date: Fri, 11 Sep 2015 17:12:49 -0500 Subject: [PATCH 3/3] Update some documentation. --- src/xenia/base/exception_handler.h | 4 ++-- src/xenia/base/exception_handler_win.cc | 2 ++ 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/xenia/base/exception_handler.h b/src/xenia/base/exception_handler.h index 8c387eb12..cf7cc9fbb 100644 --- a/src/xenia/base/exception_handler.h +++ b/src/xenia/base/exception_handler.h @@ -34,8 +34,8 @@ class ExceptionHandler { static bool Initialize(); // Install an exception handler. Returns an ID which you can save to remove - // this later. This will install the exception handler in the last place - // on Windows. + // this later. This will install the exception handler in the last place. + // TODO: ID support! static uint32_t Install(Handler fn); static bool Remove(uint32_t id); diff --git a/src/xenia/base/exception_handler_win.cc b/src/xenia/base/exception_handler_win.cc index 0c432ace3..56667a0a3 100644 --- a/src/xenia/base/exception_handler_win.cc +++ b/src/xenia/base/exception_handler_win.cc @@ -54,6 +54,8 @@ bool ExceptionHandler::Initialize() { uint32_t ExceptionHandler::Install(std::function fn) { handlers_.push_back(fn); + + // TODO: ID support! return 0; } } \ No newline at end of file