Implement crash report system on Windows.

This commit is contained in:
Andrew 2014-05-09 20:08:07 -05:00
parent 489cb4dbf5
commit 9e80ddb040
2 changed files with 111 additions and 72 deletions

View File

@ -81,14 +81,6 @@ set(CRASH_HANDLER_IMPL "")
message(STATUS "Crash dumps are ${MultiMC_HANDLE_SEGV}") message(STATUS "Crash dumps are ${MultiMC_HANDLE_SEGV}")
if (MultiMC_HANDLE_SEGV) if (MultiMC_HANDLE_SEGV)
add_definitions(-DHANDLE_SEGV) add_definitions(-DHANDLE_SEGV)
if (UNIX)
set(CRASH_HANDLER_IMPL "UnixCrash.cpp")
elseif (WIN32)
message(WARNING "The crash dump system is not currently implemented on Windows")
#set(CRASH_HANDLER_IMPL "WinCrash.cpp")
else ()
message(WARNING "The crash dump system is not supported on this platform.")
endif ()
endif () endif ()
option(MultiMC_TEST_SEGV "Intentionally segfault on startup to test crash handling." OFF) option(MultiMC_TEST_SEGV "Intentionally segfault on startup to test crash handling." OFF)
@ -275,7 +267,7 @@ SET(MULTIMC_SOURCES
# Crash handling # Crash handling
HandleCrash.h HandleCrash.h
${CRASH_HANDLER_IMPL} HandleCrash.cpp
# Logging # Logging
logger/QsDebugOutput.cpp logger/QsDebugOutput.cpp

View File

@ -1,6 +1,6 @@
// This is the Unix implementation of MultiMC's crash handling system. // This is the Unix implementation of MultiMC's crash handling system.
#include <stdio.h> #include <stdio.h>
#include <execinfo.h> #include <iostream>
#include <signal.h> #include <signal.h>
#include <stdlib.h> #include <stdlib.h>
#include <unistd.h> #include <unistd.h>
@ -8,7 +8,11 @@
#include <fcntl.h> #include <fcntl.h>
#include <errno.h> #include <errno.h>
#include <sys/stat.h> #include <sys/stat.h>
#ifdef Q_OS_UNIX
#include <sys/utsname.h> #include <sys/utsname.h>
#include <execinfo.h>
#endif
#include <MultiMC.h> #include <MultiMC.h>
@ -20,7 +24,7 @@
#define BT_SIZE 20 #define BT_SIZE 20
#define DUMPF_NAME_FMT "mmc-crash-%X.bm\0" // Black magic? Bowel movement? Dump? #define DUMPF_NAME_FMT "mmc-crash-%X.bm" // Black magic? Bowel movement? Dump?
// 1234567890 1234 // 1234567890 1234
// The maximum number of digits in a unix timestamp when encoded in hexadecimal is about 17. // The maximum number of digits in a unix timestamp when encoded in hexadecimal is about 17.
// Our format string is ~14 characters long. // Our format string is ~14 characters long.
@ -29,44 +33,109 @@
// {{{ Handling // {{{ Handling
#ifdef Q_OS_WIN32
// #ThanksMicrosoft
// Blame Microsoft for this atrocity.
void dprintf(int fd, const char* fmt...)
{
va_list args;
va_start(args, 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);
printf(buffer, fmt, args);
write(fd, buffer, len);
va_end(args);
}
#endif
void getVsnType(char* out); void getVsnType(char* out);
void readFromTo(int from, int to); void readFromTo(int from, int to);
// 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 dumpMiscInfo(int dumpFile)
void handler(int sig)
{ {
char vsnType[42]; // The version type. If it's more than 42 chars, the universe might implode...
// Get MMC info.
getVsnType(vsnType);
// Get MMC info.
getVsnType(vsnType);
dprintf(dumpFile, "MultiMC Version: %s\n", BuildConfig.VERSION_CSTR);
dprintf(dumpFile, "MultiMC Version Type: %s\n", vsnType);
}
void dumpBacktrace(int dumpFile)
{
#ifdef Q_OS_UNIX
// Variables for storing crash info. // Variables for storing crash info.
void* trace[BT_SIZE]; // Backtrace frames void* trace[BT_SIZE]; // Backtrace frames
size_t size; // The backtrace size size_t size; // The backtrace size
// Get the backtrace.
size = backtrace(trace, BT_SIZE);
// Dump the backtrace
dprintf(dumpFile, "---- BEGIN BACKTRACE ----\n");
backtrace_symbols_fd(trace, size, 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");
dprintf(dumpFile, "---- END BACKTRACE ----\n");
#endif
}
void dumpSysInfo(int dumpFile)
{
#ifdef Q_OS_UNIX
bool gotSysInfo = false; // True if system info check succeeded bool gotSysInfo = false; // True if system info check succeeded
utsname sysinfo; // System information utsname sysinfo; // System information
// Dump system info
if (uname(&sysinfo) >= 0)
{
dprintf(dumpFile, "OS System: %s\n", sysinfo.sysname);
dprintf(dumpFile, "OS Machine: %s\n", sysinfo.machine);
dprintf(dumpFile, "OS Release: %s\n", sysinfo.release);
dprintf(dumpFile, "OS Version: %s\n", sysinfo.version);
} else {
dprintf(dumpFile, "OS System: Unknown Unix");
}
#else
// TODO: Get more information here.
dprintf(dumpFile, "OS System: Windows");
#endif
}
void dumpLogs(int dumpFile)
{
int otherFile;
// Attempt to attach the log file if the logger was initialized.
dprintf(dumpFile, "---- BEGIN LOGS ----\n");
if (loggerInitialized)
{
otherFile = open("MultiMC-0.log", O_RDONLY);
readFromTo(otherFile, dumpFile);
} else {
dprintf(dumpFile, "Logger not initialized.\n");
}
dprintf(dumpFile, "---- END LOGS ----\n");
}
// 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)
{
fprintf(stderr, "Fatal error! Received signal %d\n", sig);
time_t unixTime = 0; // Unix timestamp. Used to give our crash dumps "unique" names. 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. char dumpFileName[DUMPF_NAME_LEN]; // The name of the file we're dumping to.
int dumpFile; // File descriptor for our dump file. int dumpFile; // File descriptor for our dump file.
char vsnType[42]; // The version type. If it's more than 42 chars, the universe might implode...
int otherFile; // File descriptor for other things.
fprintf(stderr, "Fatal error! Received signal %d\n", sig);
// Get the backtrace.
size = backtrace(trace, BT_SIZE);
// Get system info.
gotSysInfo = uname(&sysinfo) >= 0;
// Get MMC info.
getVsnType(vsnType);
// Determine what our dump file should be called. // Determine what our dump file should be called.
// We'll just call it "mmc-crash-<unixtime>.dump" // We'll just call it "mmc-crash-<unixtime>.dump"
// First, check the time. // First, check the time.
@ -84,6 +153,7 @@ void handler(int sig)
// Fail if it already exists. This should never happen. // Fail if it already exists. This should never happen.
dumpFile = open(dumpFileName, O_WRONLY | O_CREAT | O_TRUNC, 0644); dumpFile = open(dumpFileName, O_WRONLY | O_CREAT | O_TRUNC, 0644);
if (dumpFile >= 0) if (dumpFile >= 0)
{ {
// If we opened the dump file successfully. // If we opened the dump file successfully.
@ -92,53 +162,30 @@ void handler(int sig)
// Dump misc info // Dump misc info
dprintf(dumpFile, "Unix Time: %d\n", unixTime); dprintf(dumpFile, "Unix Time: %d\n", unixTime);
dprintf(dumpFile, "MultiMC Version: %s\n", BuildConfig.VERSION_CSTR);
dprintf(dumpFile, "MultiMC Version Type: %s\n", vsnType);
dprintf(dumpFile, "Signal: %d\n", sig); dprintf(dumpFile, "Signal: %d\n", sig);
dumpMiscInfo(dumpFile);
// Dump system info
if (gotSysInfo)
{
dprintf(dumpFile, "OS System: %s\n", sysinfo.sysname);
dprintf(dumpFile, "OS Machine: %s\n", sysinfo.machine);
dprintf(dumpFile, "OS Release: %s\n", sysinfo.release);
dprintf(dumpFile, "OS Version: %s\n", sysinfo.version);
} else {
dprintf(dumpFile, "OS System: Unknown Unix");
}
dprintf(dumpFile, "\n"); dprintf(dumpFile, "\n");
// Dump the backtrace dumpSysInfo(dumpFile);
dprintf(dumpFile, "---- BEGIN BACKTRACE ----\n");
backtrace_symbols_fd(trace, size, dumpFile);
dprintf(dumpFile, "---- END BACKTRACE ----\n");
dprintf(dumpFile, "\n"); dprintf(dumpFile, "\n");
// Attempt to attach the log file if the logger was initialized. dumpBacktrace(dumpFile);
dprintf(dumpFile, "---- BEGIN LOGS ----\n");
if (loggerInitialized) dprintf(dumpFile, "\n");
{
otherFile = open("MultiMC-0.log", O_RDONLY);
readFromTo(otherFile, dumpFile);
} else {
dprintf(dumpFile, "Logger not initialized.\n");
}
dprintf(dumpFile, "---- END LOGS ----\n");
// DIE DIE DIE! // DIE DIE DIE!
exit(1); exit(1);
} }
else else
{ {
fprintf(stderr, "Failed to open dump file %s to write crash info (%d). Here's a backtrace on stderr instead.\n", dumpFileName, errno); fprintf(stderr, "Failed to open dump file %s to write crash info (ERRNO: %d)\n", dumpFileName, errno);
// If we can't dump to the file, dump a backtrace to stderr and give up.
backtrace_symbols_fd(trace, size, STDERR_FILENO);
exit(2); exit(2);
} }
} }
// Reads data from the file descriptor on the first argument into the second argument. // Reads data from the file descriptor on the first argument into the second argument.
void readFromTo(int from, int to) void readFromTo(int from, int to)
{ {