fixup! citra-qt: Print stack trace

This commit is contained in:
MerryMage 2016-06-03 10:03:22 +01:00
parent 88cfb313ba
commit fa25484515

View File

@ -11,7 +11,13 @@
// the crash handler is called, we're going to have to use reasonably // the crash handler is called, we're going to have to use reasonably
// low-level methods to display things to the user. // low-level methods to display things to the user.
// //
// An advisable strategy is to get things onto the console first before trying to do fancy GUI stuff. // Keep it as simple as possible.
//
// An advisable strategy is to get things onto the console first before
// trying to do fancy GUI stuff. TODO(merry): An even better strategy would
// be to not do any processing at all and punt to a watchdog process, because
// ideally one cannot assume memory allocation works (what if the crash was
// in the allocator?) or stack allocate (what if rsp isn't in a valid state?).
#ifdef _WIN32 #ifdef _WIN32
@ -19,11 +25,12 @@
#include <windows.h> // Must include this first. #include <windows.h> // Must include this first.
#include <dbghelp.h> #include <dbghelp.h>
#include <tchar.h>
#include "common/string_util.h"
namespace CrashHandler { namespace CrashHandler {
static LONG WINAPI MyUnhandledExceptionFilter(struct _EXCEPTION_POINTERS*); static LONG WINAPI MyUnhandledExceptionFilter(_EXCEPTION_POINTERS*);
static std::string GetStackTrace(CONTEXT& c); static std::string GetStackTrace(CONTEXT& c);
void Register() { void Register() {
@ -36,11 +43,11 @@ void Register() {
* @param ep The exception pointer containing exception information. * @param ep The exception pointer containing exception information.
* @return See Microsoft's documentation on SetUnhandledExceptionFilter for possible return values. * @return See Microsoft's documentation on SetUnhandledExceptionFilter for possible return values.
*/ */
static LONG WINAPI MyUnhandledExceptionFilter(struct _EXCEPTION_POINTERS* ep) { static LONG WINAPI MyUnhandledExceptionFilter(_EXCEPTION_POINTERS* ep) {
std::string stack_trace = GetStackTrace(*ep->ContextRecord); std::string stack_trace = GetStackTrace(*ep->ContextRecord);
std::string detail = std::string() + std::string detail =
"Version: " + Common::g_scm_branch + ":" + Common::g_scm_desc + "\n" std::string("Version: ") + Common::g_scm_branch + ":" + Common::g_scm_desc + "\n"
"Commit: " + Common::g_scm_rev + "\n" "Commit: " + Common::g_scm_rev + "\n"
"Stack Trace:\n" + stack_trace; "Stack Trace:\n" + stack_trace;
@ -50,25 +57,25 @@ static LONG WINAPI MyUnhandledExceptionFilter(struct _EXCEPTION_POINTERS* ep) {
std::string title = "Citra: Caught Unhandled Exception"; std::string title = "Citra: Caught Unhandled Exception";
std::string message = std::string() + std::string message = std::string("Press Ctrl+C to copy text\n"
"Press Ctrl+C to copy text\n" + "Please also take a copy of the console window\n\n") + detail;
"Please also take a copy of the console window\n" +
"\n" +
detail;
// QMessageBox does not work and when it does it isn't happy. We need to use something lower-level. // QMessageBox is not guaranteed to work here since:
// 1. We're not guaranteed to be in the GUI thread
// 2. Qt is not guaranteed to be in a valid state
// We need to use something lower-level.
MessageBoxA(nullptr, message.c_str(), title.c_str(), MB_ICONSTOP); MessageBoxA(nullptr, message.c_str(), title.c_str(), MB_ICONSTOP);
FatalAppExit(0, _T("Terminating application")); FatalAppExitA(0, "Terminating application");
return EXCEPTION_CONTINUE_SEARCH; return EXCEPTION_CONTINUE_SEARCH;
} }
/** /**
* This function walks the stack of the current thread using StackWalk64. * This function walks the stack of the current thread using StackWalk64.
* @param c The context record that contains the information on the stack of interest. * @param ctx The context record that contains the information on the stack of interest.
* @return A string containing a human-readable stack trace. * @return A string containing a human-readable stack trace.
*/ */
static std::string GetStackTrace(CONTEXT& c) { static std::string GetStackTrace(CONTEXT& ctx) {
HANDLE process = GetCurrentProcess(); HANDLE process = GetCurrentProcess();
HANDLE thread = GetCurrentThread(); HANDLE thread = GetCurrentThread();
@ -84,7 +91,7 @@ static std::string GetStackTrace(CONTEXT& c) {
SCOPE_EXIT({ free(symbol); }); SCOPE_EXIT({ free(symbol); });
// Actually get symbol info. // Actually get symbol info.
DWORD64 symbol_displacement; // The number of bytes return_address is offset from the start of the function. DWORD64 symbol_displacement; // The offset of return_address from the start of the function.
SymGetSymFromAddr64(process, return_address, &symbol_displacement, symbol); SymGetSymFromAddr64(process, return_address, &symbol_displacement, symbol);
// Get undecorated name. // Get undecorated name.
@ -92,12 +99,12 @@ static std::string GetStackTrace(CONTEXT& c) {
UnDecorateSymbolName(symbol->Name, &undecorated_name[0], SYMBOL_NAME_SIZE, UNDNAME_COMPLETE); UnDecorateSymbolName(symbol->Name, &undecorated_name[0], SYMBOL_NAME_SIZE, UNDNAME_COMPLETE);
// Get source code line information. // Get source code line information.
DWORD line_displacement = 0; // The number of bytes return_address is offset from the first instruction of this line. DWORD line_displacement = 0; // The offset of return_address from the first instruction of this line.
IMAGEHLP_LINE64 line = { 0 }; IMAGEHLP_LINE64 line = {};
line.SizeOfStruct = sizeof(IMAGEHLP_LINE64); line.SizeOfStruct = sizeof(IMAGEHLP_LINE64);
SymGetLineFromAddr64(process, return_address, &line_displacement, &line); SymGetLineFromAddr64(process, return_address, &line_displacement, &line);
// Remove unnecessary path information before the "\\src\\" directory.s // Remove unnecessary path information before the "\\src\\" directory.
std::string file_name = "(null)"; std::string file_name = "(null)";
if (line.FileName) { if (line.FileName) {
file_name = line.FileName; file_name = line.FileName;
@ -107,45 +114,41 @@ static std::string GetStackTrace(CONTEXT& c) {
} }
// Format string // Format string
std::string ret; return Common::StringFromFormat("[%llx] %s+0x%llx (%s:%i)\n", return_address, undecorated_name, symbol_displacement, file_name.c_str(), line.LineNumber);
ret.resize(1024);
size_t ret_size = snprintf(&ret[0], ret.size(), "[%llx] %s+0x%llx (%s:%i)\n", return_address, undecorated_name, symbol_displacement, file_name.c_str(), line.LineNumber);
ret.resize(ret_size);
return ret;
}; };
// Initialise symbols // Initialise symbols
if (SymInitialize(process, NULL, TRUE) == FALSE) { if (SymInitialize(process, nullptr, TRUE) == FALSE) {
fprintf(stderr, "Failed to get symbols. Continuing anyway...\n"); fprintf(stderr, "Failed to get symbols. Continuing anyway...\n");
} }
SymSetOptions(SymGetOptions() | SYMOPT_LOAD_LINES | SYMOPT_UNDNAME); SymSetOptions(SymGetOptions() | SYMOPT_LOAD_LINES | SYMOPT_UNDNAME);
SCOPE_EXIT({ SymCleanup(process); }); SCOPE_EXIT({ SymCleanup(process); });
// Extract stack information from context // Extract stack information from context
STACKFRAME64 s; STACKFRAME64 sframe;
s.AddrPC.Offset = c.Rip; sframe.AddrPC.Offset = ctx.Rip;
s.AddrPC.Mode = AddrModeFlat; sframe.AddrPC.Mode = AddrModeFlat;
s.AddrStack.Offset = c.Rsp; sframe.AddrStack.Offset = ctx.Rsp;
s.AddrStack.Mode = AddrModeFlat; sframe.AddrStack.Mode = AddrModeFlat;
s.AddrFrame.Offset = c.Rbp; sframe.AddrFrame.Offset = ctx.Rbp;
s.AddrFrame.Mode = AddrModeFlat; sframe.AddrFrame.Mode = AddrModeFlat;
// Return value // Return value
std::string stack_trace; std::string stack_trace;
// Walk the stack // Walk the stack
do { do {
if (!StackWalk64(IMAGE_FILE_MACHINE_AMD64, process, thread, &s, &c, NULL, SymFunctionTableAccess64, SymGetModuleBase64, NULL)) { if (!StackWalk64(IMAGE_FILE_MACHINE_AMD64, process, thread, &sframe, &ctx, nullptr, SymFunctionTableAccess64, SymGetModuleBase64, nullptr)) {
stack_trace += "Last StackWalk64 failed\n"; stack_trace += "Last StackWalk64 failed\n";
return stack_trace; return stack_trace;
} }
if (s.AddrPC.Offset != 0) { if (sframe.AddrPC.Offset != 0) {
stack_trace += get_symbol_info(s.AddrPC.Offset); stack_trace += get_symbol_info(sframe.AddrPC.Offset);
} else { } else {
stack_trace += "No Symbols: rip == 0\n"; stack_trace += "No Symbols: rip == 0\n";
} }
} while (s.AddrReturn.Offset != 0); } while (sframe.AddrReturn.Offset != 0);
return stack_trace; return stack_trace;
} }
@ -172,7 +175,7 @@ static const char* GetSignalName(int signal);
static std::string GetStackTrace(); static std::string GetStackTrace();
#ifdef __APPLE__ #ifdef __APPLE__
static void OSXMessageBox(std::string title, std::string message); static void OSXMessageBox(const std::string& title, const std::string& message);
#endif #endif
void Register() { void Register() {
@ -200,19 +203,20 @@ static void SignalHandler(int signal) {
fprintf(stderr, "Stack Trace:\n"); fprintf(stderr, "Stack Trace:\n");
std::string stack_trace = GetStackTrace(); std::string stack_trace = GetStackTrace();
fflush(stderr);
#ifdef __APPLE__ #ifdef __APPLE__
std::string title = "Caught Signal " + std::to_string(signal) + " (" + GetSignalName(signal) + ")"; std::string title = "Caught Signal " + std::to_string(signal) + " (" + GetSignalName(signal) + ")";
std::string message = std::string() + std::string message =
"Version: " + Common::g_scm_branch + "-" + Common::g_scm_desc + "\n" + std::string("Version: ") + Common::g_scm_branch + "-" + Common::g_scm_desc + "\n" +
"Commit: " + Common::g_scm_rev + "\n" + "Commit: " + Common::g_scm_rev + "\n" +
"Stack Trace:\n" + stack_trace; "Stack Trace:\n" + stack_trace;
OSXMessageBox(title, message); OSXMessageBox(title, message);
#endif #endif
exit(-1); exit(EXIT_FAILURE);
} }
static const char* GetSignalName(int signal) { static const char* GetSignalName(int signal) {
@ -254,14 +258,17 @@ static std::string GetStackTrace() {
} }
#ifdef __APPLE__ #ifdef __APPLE__
static void OSXMessageBox(std::string title, std::string message) { static void OSXMessageBox(const std::string& title, const std::string& message) {
// This implementation leaks memory, but at this point we don't really care. // This implementation leaks memory, but at this point we don't really care.
id alert = objc_msgSend(objc_getClass("NSAlert"), sel_registerName("alloc")); // Casting objc_msgSend is recommended to ensure correct semantics (it doesn't use vararg semantics as declared).
alert = objc_msgSend(alert, sel_registerName("init")); CFStringRef cftitle = CFStringCreateWithCString(nullptr, title.c_str(), kCFStringEncodingMacRoman);
objc_msgSend(alert, sel_getUid("setAlertStyle:"), 1); CFStringRef cfmessage = CFStringCreateWithCString(nullptr, message.c_str(), kCFStringEncodingMacRoman);
objc_msgSend(alert, sel_getUid("setMessageText:"), CFSTR(message.c_str())); id alert = reinterpret_cast<id(*)(Class, SEL)>(objc_msgSend)(objc_getClass("NSAlert"), sel_registerName("alloc"));
objc_msgSend(alert, sel_getUid("setInformativeText:"), CFSTR(title.c_str())); alert = reinterpret_cast<id(*)(id, SEL)>(objc_msgSend)(alert, sel_registerName("init"));
objc_msgSend(alert, sel_getUid("runModal")); reinterpret_cast<void(*)(id, SEL, int)>(objc_msgSend)(alert, sel_getUid("setAlertStyle:"), 1);
reinterpret_cast<void(*)(id, SEL, CFStringRef)>(objc_msgSend)(alert, sel_getUid("setMessageText:"), cftitle);
reinterpret_cast<void(*)(id, SEL, CFStringRef)>(objc_msgSend)(alert, sel_getUid("setInformativeText:"), cfmessage);
reinterpret_cast<int(*)(id, SEL)>(objc_msgSend)(alert, sel_getUid("runModal"));
} }
#endif #endif