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
// 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
@ -19,11 +25,12 @@
#include <windows.h> // Must include this first.
#include <dbghelp.h>
#include <tchar.h>
#include "common/string_util.h"
namespace CrashHandler {
static LONG WINAPI MyUnhandledExceptionFilter(struct _EXCEPTION_POINTERS*);
static LONG WINAPI MyUnhandledExceptionFilter(_EXCEPTION_POINTERS*);
static std::string GetStackTrace(CONTEXT& c);
void Register() {
@ -36,11 +43,11 @@ void Register() {
* @param ep The exception pointer containing exception information.
* @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 detail = std::string() +
"Version: " + Common::g_scm_branch + ":" + Common::g_scm_desc + "\n"
std::string detail =
std::string("Version: ") + Common::g_scm_branch + ":" + Common::g_scm_desc + "\n"
"Commit: " + Common::g_scm_rev + "\n"
"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 message = std::string() +
"Press Ctrl+C to copy text\n" +
"Please also take a copy of the console window\n" +
"\n" +
detail;
std::string message = std::string("Press Ctrl+C to copy text\n"
"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);
FatalAppExit(0, _T("Terminating application"));
FatalAppExitA(0, "Terminating application");
return EXCEPTION_CONTINUE_SEARCH;
}
/**
* 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.
*/
static std::string GetStackTrace(CONTEXT& c) {
static std::string GetStackTrace(CONTEXT& ctx) {
HANDLE process = GetCurrentProcess();
HANDLE thread = GetCurrentThread();
@ -84,7 +91,7 @@ static std::string GetStackTrace(CONTEXT& c) {
SCOPE_EXIT({ free(symbol); });
// 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);
// Get undecorated name.
@ -92,12 +99,12 @@ static std::string GetStackTrace(CONTEXT& c) {
UnDecorateSymbolName(symbol->Name, &undecorated_name[0], SYMBOL_NAME_SIZE, UNDNAME_COMPLETE);
// Get source code line information.
DWORD line_displacement = 0; // The number of bytes return_address is offset from the first instruction of this line.
IMAGEHLP_LINE64 line = { 0 };
DWORD line_displacement = 0; // The offset of return_address from the first instruction of this line.
IMAGEHLP_LINE64 line = {};
line.SizeOfStruct = sizeof(IMAGEHLP_LINE64);
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)";
if (line.FileName) {
file_name = line.FileName;
@ -107,45 +114,41 @@ static std::string GetStackTrace(CONTEXT& c) {
}
// Format string
std::string ret;
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;
return Common::StringFromFormat("[%llx] %s+0x%llx (%s:%i)\n", return_address, undecorated_name, symbol_displacement, file_name.c_str(), line.LineNumber);
};
// Initialise symbols
if (SymInitialize(process, NULL, TRUE) == FALSE) {
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); });
// Extract stack information from context
STACKFRAME64 s;
s.AddrPC.Offset = c.Rip;
s.AddrPC.Mode = AddrModeFlat;
s.AddrStack.Offset = c.Rsp;
s.AddrStack.Mode = AddrModeFlat;
s.AddrFrame.Offset = c.Rbp;
s.AddrFrame.Mode = AddrModeFlat;
STACKFRAME64 sframe;
sframe.AddrPC.Offset = ctx.Rip;
sframe.AddrPC.Mode = AddrModeFlat;
sframe.AddrStack.Offset = ctx.Rsp;
sframe.AddrStack.Mode = AddrModeFlat;
sframe.AddrFrame.Offset = ctx.Rbp;
sframe.AddrFrame.Mode = AddrModeFlat;
// Return value
std::string stack_trace;
// Walk the stack
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";
return stack_trace;
}
if (s.AddrPC.Offset != 0) {
stack_trace += get_symbol_info(s.AddrPC.Offset);
if (sframe.AddrPC.Offset != 0) {
stack_trace += get_symbol_info(sframe.AddrPC.Offset);
} else {
stack_trace += "No Symbols: rip == 0\n";
}
} while (s.AddrReturn.Offset != 0);
} while (sframe.AddrReturn.Offset != 0);
return stack_trace;
}
@ -172,7 +175,7 @@ static const char* GetSignalName(int signal);
static std::string GetStackTrace();
#ifdef __APPLE__
static void OSXMessageBox(std::string title, std::string message);
static void OSXMessageBox(const std::string& title, const std::string& message);
#endif
void Register() {
@ -200,19 +203,20 @@ static void SignalHandler(int signal) {
fprintf(stderr, "Stack Trace:\n");
std::string stack_trace = GetStackTrace();
fflush(stderr);
#ifdef __APPLE__
std::string title = "Caught Signal " + std::to_string(signal) + " (" + GetSignalName(signal) + ")";
std::string message = std::string() +
"Version: " + Common::g_scm_branch + "-" + Common::g_scm_desc + "\n" +
std::string message =
std::string("Version: ") + Common::g_scm_branch + "-" + Common::g_scm_desc + "\n" +
"Commit: " + Common::g_scm_rev + "\n" +
"Stack Trace:\n" + stack_trace;
OSXMessageBox(title, message);
#endif
exit(-1);
exit(EXIT_FAILURE);
}
static const char* GetSignalName(int signal) {
@ -254,14 +258,17 @@ static std::string GetStackTrace() {
}
#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.
id alert = objc_msgSend(objc_getClass("NSAlert"), sel_registerName("alloc"));
alert = objc_msgSend(alert, sel_registerName("init"));
objc_msgSend(alert, sel_getUid("setAlertStyle:"), 1);
objc_msgSend(alert, sel_getUid("setMessageText:"), CFSTR(message.c_str()));
objc_msgSend(alert, sel_getUid("setInformativeText:"), CFSTR(title.c_str()));
objc_msgSend(alert, sel_getUid("runModal"));
// Casting objc_msgSend is recommended to ensure correct semantics (it doesn't use vararg semantics as declared).
CFStringRef cftitle = CFStringCreateWithCString(nullptr, title.c_str(), kCFStringEncodingMacRoman);
CFStringRef cfmessage = CFStringCreateWithCString(nullptr, message.c_str(), kCFStringEncodingMacRoman);
id alert = reinterpret_cast<id(*)(Class, SEL)>(objc_msgSend)(objc_getClass("NSAlert"), sel_registerName("alloc"));
alert = reinterpret_cast<id(*)(id, SEL)>(objc_msgSend)(alert, sel_registerName("init"));
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