crash_handler: Catch unhandled structured exceptions on Windows

This commit is contained in:
MerryMage 2016-12-19 19:24:52 +00:00
parent c96acc1941
commit 710b70f555
3 changed files with 180 additions and 0 deletions

View File

@ -3,6 +3,7 @@ configure_file("${CMAKE_CURRENT_SOURCE_DIR}/scm_rev.cpp.in" "${CMAKE_CURRENT_SOU
set(SRCS set(SRCS
break_points.cpp break_points.cpp
crash_handler.cpp
emu_window.cpp emu_window.cpp
file_util.cpp file_util.cpp
framebuffer_layout.cpp framebuffer_layout.cpp
@ -34,6 +35,7 @@ set(HEADERS
common_funcs.h common_funcs.h
common_paths.h common_paths.h
common_types.h common_types.h
crash_handler.h
emu_window.h emu_window.h
file_util.h file_util.h
framebuffer_layout.h framebuffer_layout.h

View File

@ -0,0 +1,161 @@
// Copyright 2016 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <string>
#include "common/crash_handler.h"
#include "common/scope_exit.h"
#ifdef _WIN32
#pragma comment(lib, "dbghelp.lib")
#include <windows.h>
// windows.h must be included first.
#include <dbghelp.h>
#include "common/string_util.h"
namespace Common {
static bool unhandled_exception_called;
static jmp_buf unhandled_exception_jmp_buf;
static std::vector<std::string> unhandled_exception_stack_trace;
static LONG WINAPI MyUnhandledExceptionFilter(_EXCEPTION_POINTERS*);
static void GetStackTrace(CONTEXT& c);
void CrashHandler(std::function<void()> try_,
std::function<void(const Common::CrashInformation&)> catch_) {
unhandled_exception_called = false;
SetUnhandledExceptionFilter(MyUnhandledExceptionFilter);
if (setjmp(unhandled_exception_jmp_buf) != 0) {
catch_({unhandled_exception_stack_trace});
return;
}
try_();
}
/**
* This function is called by the operating system when an unhandled exception occurs.
* This includes things like debug breakpoints when not connected to a debugger.
* @param ep The exception pointer containing exception information.
* @return See Microsoft's documentation on SetUnhandledExceptionFilter for possible return values.
*/
static LONG WINAPI MyUnhandledExceptionFilter(_EXCEPTION_POINTERS* ep) {
// Prevent re-entry.
if (unhandled_exception_called)
return EXCEPTION_CONTINUE_SEARCH;
unhandled_exception_called = true;
GetStackTrace(*ep->ContextRecord);
// Ensure we have a log of everything in the console first.
fprintf(stderr, "Unhandled Exception:\n");
for (const auto& line : unhandled_exception_stack_trace) {
fprintf(stderr, "%s\n", line.c_str());
}
fflush(stderr);
longjmp(unhandled_exception_jmp_buf, 1);
return EXCEPTION_CONTINUE_SEARCH;
}
/**
* This function walks the stack of the current thread using StackWalk64.
* @param ctx The context record that contains the information on the stack of interest.
* @return A string containing a human-readable stack trace.
*/
static void GetStackTrace(CONTEXT& ctx) {
HANDLE process = GetCurrentProcess();
HANDLE thread = GetCurrentThread();
// This function generates a single line of the stack trace.
// `return_address` is a return address found on the stack.
auto get_symbol_info = [&process](DWORD64 return_address) -> std::string {
constexpr size_t SYMBOL_NAME_SIZE = 512; // arbitrary value
// Allocate space for symbol info.
IMAGEHLP_SYMBOL64* symbol = static_cast<IMAGEHLP_SYMBOL64*>(
calloc(sizeof(IMAGEHLP_SYMBOL64) + SYMBOL_NAME_SIZE * sizeof(char), 1));
symbol->MaxNameLength = SYMBOL_NAME_SIZE;
symbol->SizeOfStruct = sizeof(SYMBOL_INFO);
SCOPE_EXIT({ free(symbol); });
// Actually get symbol info.
DWORD64 symbol_displacement; // Offset of return_address from function entry point.
SymGetSymFromAddr64(process, return_address, &symbol_displacement, symbol);
// Get undecorated name.
char undecorated_name[SYMBOL_NAME_SIZE + 1];
UnDecorateSymbolName(symbol->Name, &undecorated_name[0], SYMBOL_NAME_SIZE,
UNDNAME_COMPLETE);
// Get source code line information.
DWORD line_displacement = 0; // Offset of return_address from first instruction of line.
IMAGEHLP_LINE64 line = {};
line.SizeOfStruct = sizeof(IMAGEHLP_LINE64);
SymGetLineFromAddr64(process, return_address, &line_displacement, &line);
// Remove unnecessary path information before the "\\src\\" directory.
std::string file_name = "(null)";
if (line.FileName) {
file_name = line.FileName;
size_t found = file_name.find("\\src\\");
if (found != std::string::npos)
file_name = file_name.substr(found + 1);
}
// Format string
return Common::StringFromFormat("[%llx] %s+0x%llx (%s:%i)", return_address,
undecorated_name, symbol_displacement, file_name.c_str(),
line.LineNumber);
};
// NOTE: SymFunctionTableAccess64 doesn't work with the non-standard stack frames our JIT
// produces. Thus we elect to not use StackWalk64, but instead manually use the Rtl* functions.
// Initialise symbols
if (SymInitialize(process, nullptr, TRUE) == FALSE) {
fprintf(stderr, "Failed to get symbols. Continuing anyway...\n");
}
SymSetOptions(SymGetOptions() | SYMOPT_LOAD_LINES | SYMOPT_UNDNAME);
SCOPE_EXIT({ SymCleanup(process); });
// Walk the stack
unhandled_exception_stack_trace = {};
while (ctx.Rip != 0) {
unhandled_exception_stack_trace.emplace_back(get_symbol_info(ctx.Rip));
DWORD64 image_base;
PRUNTIME_FUNCTION runtime_function = RtlLookupFunctionEntry(ctx.Rip, &image_base, nullptr);
if (!runtime_function) {
// This is likely a leaf function. Adjust the stack appropriately.
if (!ctx.Rsp) {
unhandled_exception_stack_trace.emplace_back("Invalid rsp");
return;
}
ctx.Rip = *(reinterpret_cast<u64*>(ctx.Rsp));
ctx.Rsp += 8;
continue;
}
PVOID handler_data;
ULONG64 establisher_frame;
RtlVirtualUnwind(UNW_FLAG_NHANDLER, image_base, ctx.Rip, runtime_function, &ctx,
&handler_data, &establisher_frame, nullptr);
}
}
} // namespace Common
#else
#error "Unimplemented"
#endif

View File

@ -0,0 +1,17 @@
// Copyright 2016 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <functional>
#include <string>
#include <vector>
namespace Common {
struct CrashInformation {
std::vector<std::string> stack_trace;
};
void CrashHandler(std::function<void()> try_, std::function<void(const CrashInformation&)> catch_);
} // namespace Common