From 5099964c673cd45e16d443a3b02f51bade303eee Mon Sep 17 00:00:00 2001 From: Andrew Date: Sat, 10 May 2014 14:16:27 -0500 Subject: Implement backtraces on Windows. Much !!FUN!! was had --- HandleCrash.cpp | 159 ++++++++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 142 insertions(+), 17 deletions(-) (limited to 'HandleCrash.cpp') diff --git a/HandleCrash.cpp b/HandleCrash.cpp index 3d6a425b..91a5f2a1 100644 --- a/HandleCrash.cpp +++ b/HandleCrash.cpp @@ -1,3 +1,18 @@ +/* Copyright 2014 MultiMC Contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + // This is the Unix implementation of MultiMC's crash handling system. #include #include @@ -9,13 +24,17 @@ #include #include -#ifdef Q_OS_UNIX +#include + +#if defined Q_OS_UNIX #include #include +#elif defined Q_OS_WIN32 +#include +#include +#include #endif -#include - #include "BuildConfig.h" #include "HandleCrash.h" @@ -31,6 +50,51 @@ // The maximum length of the dump file's filename should be well over both of these. 42 is a good number. #define DUMPF_NAME_LEN 42 +// {{{ Platform hackery + +#if defined Q_OS_UNIX + +struct CrashData +{ + int signal = 0; +}; + +// This has to be declared here, after the CrashData struct, but before the function that uses it. +void handleCrash(CrashData); + +void handle(int sig) +{ + CrashData cData; + cData.signal = sig; + handleCrash(cData); +} + +#elif defined Q_OS_WIN32 + +// Struct for storing platform specific crash information. +// This gets passed into the generic handler, which will use +// it to access platform specific information. +struct CrashData +{ + EXCEPTION_RECORD* exceptionInfo; + CONTEXT* context; +}; + +void handleCrash(CrashData); + +LONG WINAPI ExceptionFilter(EXCEPTION_POINTERS* eInfo) +{ + CrashData cData; + cData.exceptionInfo = eInfo->ExceptionRecord; + cData.context = eInfo->ContextRecord; + handleCrash(cData); + return EXCEPTION_EXECUTE_HANDLER; +} + +#endif + +// }}} + // {{{ Handling #ifdef Q_OS_WIN32 @@ -43,7 +107,7 @@ void dprintf(int fd, const char* fmt...) char buffer[10240]; // Just sprintf to a really long string and hope it works... // This is a hack, but I can't think of a better way to do it easily. - int len = vsprintf(buffer, fmt, args); + int len = vsnprintf(buffer, 10240, fmt, args); printf(buffer, fmt, args); write(fd, buffer, len); va_end(args); @@ -53,10 +117,23 @@ void dprintf(int fd, const char* fmt...) void getVsnType(char* out); void readFromTo(int from, int to); +void dumpErrorInfo(int dumpFile, CrashData crash) +{ +#ifdef Q_OS_UNIX + // TODO: Moar unix + dprintf(dumpFile, "Signal: %d\n", crash.signal); +#elif defined Q_OS_WIN32 + EXCEPTION_RECORD* excInfo = crash.exceptionInfo; + + dprintf(dumpFile, "Exception Code: %d\n", excInfo->ExceptionCode); + dprintf(dumpFile, "Exception Address: 0x%0X\n", excInfo->ExceptionAddress); +#endif +} + void dumpMiscInfo(int dumpFile) { char vsnType[42]; // The version type. If it's more than 42 chars, the universe might implode... - + // Get MMC info. getVsnType(vsnType); @@ -67,7 +144,7 @@ void dumpMiscInfo(int dumpFile) dprintf(dumpFile, "MultiMC Version Type: %s\n", vsnType); } -void dumpBacktrace(int dumpFile) +void dumpBacktrace(int dumpFile, CrashData crash) { #ifdef Q_OS_UNIX // Variables for storing crash info. @@ -83,7 +160,47 @@ void dumpBacktrace(int dumpFile) dprintf(dumpFile, "---- END BACKTRACE ----\n"); #elif defined Q_OS_WIN32 dprintf(dumpFile, "---- BEGIN BACKTRACE ----\n"); - dprintf(dumpFile, "Not yet implemented on this platform.\n"); + + StackFrame stack[BT_SIZE]; + size_t size; + + SYMBOL_INFO *symbol; + HANDLE process; + + size = getBacktrace(stack, BT_SIZE, *crash.context); + + // FIXME: Accessing heap memory is supposedly "dangerous", + // but I can't find another way of doing this. + + // Initialize + process = GetCurrentProcess(); + if (!SymInitialize(process, NULL, true)) + { + dprintf(dumpFile, "Failed to initialize symbol handler. Can't print stack trace.\n"); + dprintf(dumpFile, "Here's a list of addresses in the call stack instead:\n"); + for(int i = 0; i < size; i++) + { + dprintf(dumpFile, "0x%0X\n", (DWORD64)stack[i].address); + } + } else { + // Allocate memory... ._. + symbol = (SYMBOL_INFO *) calloc(sizeof(SYMBOL_INFO) + 256 * sizeof(char), 1); + symbol->MaxNameLen = 255; + symbol->SizeOfStruct = sizeof(SYMBOL_INFO); + + // Dump stacktrace + for(int i = 0; i < size; i++) + { + DWORD64 addr = (DWORD64)stack[i].address; + if (!SymFromAddr(process, (DWORD64)(addr), 0, symbol)) + dprintf(dumpFile, "?? - 0x%0X\n", addr); + else + dprintf(dumpFile, "%s - 0x%0X\n", symbol->Name, symbol->Address); + } + + free(symbol); + } + dprintf(dumpFile, "---- END BACKTRACE ----\n"); #endif } @@ -93,7 +210,7 @@ void dumpSysInfo(int dumpFile) #ifdef Q_OS_UNIX bool gotSysInfo = false; // True if system info check succeeded utsname sysinfo; // System information - + // Dump system info if (uname(&sysinfo) >= 0) { @@ -127,10 +244,13 @@ void dumpLogs(int dumpFile) } // The signal handler. If this function is called, it means shit has probably collided with some sort of device one might use to keep oneself cool. -void handler(int sig) +// This is the generic part of the code that will be called after platform specific handling is finished. +void handleCrash(CrashData crash) { - fprintf(stderr, "Fatal error! Received signal %d\n", sig); - +#ifdef Q_OS_UNIX + fprintf(stderr, "Fatal error! Received signal %d\n", crash.signal); +#endif + time_t unixTime = 0; // Unix timestamp. Used to give our crash dumps "unique" names. char dumpFileName[DUMPF_NAME_LEN]; // The name of the file we're dumping to. @@ -140,7 +260,7 @@ void handler(int sig) // We'll just call it "mmc-crash-.dump" // First, check the time. time(&unixTime); - + // Now we get to do some !!FUN!! hackery to ensure we don't use the stack when we convert // the timestamp from an int to a string. To do this, we just allocate a fixed size array // of chars on the stack, and sprintf into it. We know the timestamp won't ever be longer @@ -162,16 +282,16 @@ void handler(int sig) // Dump misc info dprintf(dumpFile, "Unix Time: %d\n", unixTime); - dprintf(dumpFile, "Signal: %d\n", sig); + dumpErrorInfo(dumpFile, crash); dumpMiscInfo(dumpFile); - + dprintf(dumpFile, "\n"); - + dumpSysInfo(dumpFile); dprintf(dumpFile, "\n"); - - dumpBacktrace(dumpFile); + + dumpBacktrace(dumpFile, crash); dprintf(dumpFile, "\n"); @@ -238,9 +358,14 @@ void testCrash() // Initializes the Unix crash handler. void initBlackMagic() { +#ifdef Q_OS_UNIX // Register the handler. signal(SIGSEGV, handler); signal(SIGABRT, handler); +#elif defined Q_OS_WIN32 + // I hate Windows + SetUnhandledExceptionFilter(ExceptionFilter); +#endif #ifdef TEST_SEGV testCrash(); -- cgit v1.2.3