summaryrefslogtreecommitdiffstats
path: root/HandleCrash.cpp
diff options
context:
space:
mode:
authorAndrew <forkk@forkk.net>2014-05-09 20:08:07 -0500
committerAndrew <forkk@forkk.net>2014-05-09 20:08:07 -0500
commit9e80ddb0405ecd5d45d65e5385d0fbd1f3e734e3 (patch)
tree0544b5b1816773aef63fb29a3234eeb1e73163dd /HandleCrash.cpp
parent489cb4dbf597818ea2dc463961cc86e7c1261412 (diff)
downloadMultiMC-9e80ddb0405ecd5d45d65e5385d0fbd1f3e734e3.tar
MultiMC-9e80ddb0405ecd5d45d65e5385d0fbd1f3e734e3.tar.gz
MultiMC-9e80ddb0405ecd5d45d65e5385d0fbd1f3e734e3.tar.lz
MultiMC-9e80ddb0405ecd5d45d65e5385d0fbd1f3e734e3.tar.xz
MultiMC-9e80ddb0405ecd5d45d65e5385d0fbd1f3e734e3.zip
Implement crash report system on Windows.
Diffstat (limited to 'HandleCrash.cpp')
-rw-r--r--HandleCrash.cpp251
1 files changed, 251 insertions, 0 deletions
diff --git a/HandleCrash.cpp b/HandleCrash.cpp
new file mode 100644
index 00000000..3d6a425b
--- /dev/null
+++ b/HandleCrash.cpp
@@ -0,0 +1,251 @@
+// This is the Unix implementation of MultiMC's crash handling system.
+#include <stdio.h>
+#include <iostream>
+#include <signal.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <time.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <sys/stat.h>
+
+#ifdef Q_OS_UNIX
+#include <sys/utsname.h>
+#include <execinfo.h>
+#endif
+
+#include <MultiMC.h>
+
+#include "BuildConfig.h"
+
+#include "HandleCrash.h"
+
+// The maximum number of frames to include in the backtrace.
+#define BT_SIZE 20
+
+
+#define DUMPF_NAME_FMT "mmc-crash-%X.bm" // Black magic? Bowel movement? Dump?
+// 1234567890 1234
+// The maximum number of digits in a unix timestamp when encoded in hexadecimal is about 17.
+// Our format string is ~14 characters long.
+// 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
+
+// {{{ 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 readFromTo(int from, int to);
+
+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);
+
+ // 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.
+ void* trace[BT_SIZE]; // Backtrace frames
+ 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
+ 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.
+
+ char dumpFileName[DUMPF_NAME_LEN]; // The name of the file we're dumping to.
+ int dumpFile; // File descriptor for our dump file.
+
+ // Determine what our dump file should be called.
+ // We'll just call it "mmc-crash-<unixtime>.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
+ // than a certain number of digits, so this should work just fine.
+ // sprintf doesn't support writing signed values as hex, so this breaks on negative timestamps.
+ // It really shouldn't matter, though...
+ sprintf(dumpFileName, DUMPF_NAME_FMT, unixTime);
+
+ // Now, we need to open the file.
+ // Fail if it already exists. This should never happen.
+ dumpFile = open(dumpFileName, O_WRONLY | O_CREAT | O_TRUNC, 0644);
+
+
+ if (dumpFile >= 0)
+ {
+ // If we opened the dump file successfully.
+ // Dump everything we can and GTFO.
+ fprintf(stderr, "Dumping crash report to %s\n", dumpFileName);
+
+ // Dump misc info
+ dprintf(dumpFile, "Unix Time: %d\n", unixTime);
+ dprintf(dumpFile, "Signal: %d\n", sig);
+ dumpMiscInfo(dumpFile);
+
+ dprintf(dumpFile, "\n");
+
+ dumpSysInfo(dumpFile);
+
+ dprintf(dumpFile, "\n");
+
+ dumpBacktrace(dumpFile);
+
+ dprintf(dumpFile, "\n");
+
+ // DIE DIE DIE!
+ exit(1);
+ }
+ else
+ {
+ fprintf(stderr, "Failed to open dump file %s to write crash info (ERRNO: %d)\n", dumpFileName, errno);
+ exit(2);
+ }
+}
+
+
+// Reads data from the file descriptor on the first argument into the second argument.
+void readFromTo(int from, int to)
+{
+ char buffer[1024];
+ size_t lastread = 1;
+ while (lastread > 0)
+ {
+ lastread = read(from, buffer, 1024);
+ if (lastread > 0) write(to, buffer, lastread);
+ }
+}
+
+// Writes the current version type to the given char buffer.
+void getVsnType(char* out)
+{
+ switch (BuildConfig.versionTypeEnum)
+ {
+ case Config::Release:
+ sprintf(out, "Release");
+ break;
+ case Config::ReleaseCandidate:
+ sprintf(out, "ReleaseCandidate");
+ break;
+ case Config::Development:
+ sprintf(out, "Development");
+ break;
+ default:
+ sprintf(out, "Unknown");
+ break;
+ }
+}
+
+// }}}
+
+// {{{ Misc
+
+#if defined TEST_SEGV
+// Causes a crash. For testing.
+void testCrash()
+{
+ char* lol = (char*)MMC->settings().get();
+ lol -= 8;
+
+ // Throw shit at the fan.
+ for (int i = 0; i < 8; i++)
+ lol[i] = 'f';
+}
+#endif
+
+// Initializes the Unix crash handler.
+void initBlackMagic()
+{
+ // Register the handler.
+ signal(SIGSEGV, handler);
+ signal(SIGABRT, handler);
+
+#ifdef TEST_SEGV
+ testCrash();
+#endif
+}
+
+// }}}
+