aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGravatar Chris Xiong <chirs241097@gmail.com> 2019-12-02 01:19:30 +0800
committerGravatar Chris Xiong <chirs241097@gmail.com> 2019-12-02 01:19:30 +0800
commitc34daa26c67f4a879ff541775c099ddc19a1dec5 (patch)
treec7ee978b1d7c880ca8be746ba5dfd3d887239d95
parent59acbd03eaf082cea6c751ac6a759724bc3dc68f (diff)
downloadQMidiPlayer-c34daa26c67f4a879ff541775c099ddc19a1dec5.tar.xz
Add a stack trace printer for the beloved operating system.
-rw-r--r--CMakeLists.txt8
-rw-r--r--qmidiplayer-desktop/main.cpp5
-rw-r--r--third_party/backtrace-mingw/CMakeLists.txt11
-rw-r--r--third_party/backtrace-mingw/README.md20
-rw-r--r--third_party/backtrace-mingw/backtrace.c457
-rw-r--r--third_party/backtrace-mingw/test.c23
6 files changed, 524 insertions, 0 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 92ba7f7..6b92872 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -15,6 +15,9 @@ pkg_search_module(fluidsynth REQUIRED fluidsynth>=2.0.0)
pkg_search_module(rtmidi REQUIRED rtmidi)
option(BUILD_VISUALIZATION "Build visualization plugin" ON)
+if(WIN32)
+ option(BUILD_BACKTRACE "Build backtrace library" OFF)
+endif()
add_subdirectory(core)
add_subdirectory(qmidiplayer-desktop)
@@ -24,3 +27,8 @@ add_subdirectory(simple-visualization)
if(BUILD_VISUALIZATION)
add_subdirectory(visualization)
endif()
+if(WIN32)
+if(BUILD_BACKTRACE)
+ add_subdirectory(third_party/backtrace-mingw)
+endif()
+endif()
diff --git a/qmidiplayer-desktop/main.cpp b/qmidiplayer-desktop/main.cpp
index 37f6338..b1c9149 100644
--- a/qmidiplayer-desktop/main.cpp
+++ b/qmidiplayer-desktop/main.cpp
@@ -22,11 +22,16 @@
#include <QLibraryInfo>
#include <QCommandLineParser>
#ifdef _WIN32
+#include <cstdio>
#include <windows.h>
#endif
int main(int argc,char **argv)
{
+#ifdef _WIN32
+ if(!LoadLibraryA("libbacktrace.dll"))
+ fputs("Failed to load backtrace library. Stack trace will not be printed if unhandled exception occurs.\n",stderr);
+#endif
QCoreApplication::setAttribute(Qt::AA_UseHighDpiPixmaps);
QCoreApplication::setApplicationName("qmidiplayer");
QCoreApplication::setApplicationVersion(APP_VERSION);
diff --git a/third_party/backtrace-mingw/CMakeLists.txt b/third_party/backtrace-mingw/CMakeLists.txt
new file mode 100644
index 0000000..394d420
--- /dev/null
+++ b/third_party/backtrace-mingw/CMakeLists.txt
@@ -0,0 +1,11 @@
+add_library(backtrace SHARED backtrace.c)
+find_path(BFD_INCLUDE_DIR NAMES bfd.h)
+find_library(BFD_LIBRARIES NAMES bfd)
+find_library(IBERTY_LIBRARIES NAMES iberty)
+find_package(ZLIB REQUIRED)
+target_link_libraries(backtrace
+ ${BFD_LIBRARIES}
+ ${IBERTY_LIBRARIES}
+ ${ZLIB_LIBRARIES}
+ imagehlp
+)
diff --git a/third_party/backtrace-mingw/README.md b/third_party/backtrace-mingw/README.md
new file mode 100644
index 0000000..77cb879
--- /dev/null
+++ b/third_party/backtrace-mingw/README.md
@@ -0,0 +1,20 @@
+The original project doesn't come with a readme.
+
+The code has been slightly tweaked to add support for amd64 and to obtain a
+more readable output format.
+
+Tested with mxe (gcc 5.5.0, binutils 2.28). Obviously you need a shared build.
+mxe disables shared versions of bfd and iberty, but actually they work just
+fine. Simply remove this line
+```
+$(PKG)_BUILD_SHARED =
+```
+from their makefiles and change the options for `./configure`
+(`--enable-static` to `--disable-static`, `--disable-shared` to
+`--enable-shared`).
+
+[Source project](https://github.com/cloudwu/backtrace-mingw)
+
+References
+
+[Printing a Stack Trace with MinGW](http://theorangeduck.com/page/printing-stack-trace-mingw)
diff --git a/third_party/backtrace-mingw/backtrace.c b/third_party/backtrace-mingw/backtrace.c
new file mode 100644
index 0000000..b9ddcf4
--- /dev/null
+++ b/third_party/backtrace-mingw/backtrace.c
@@ -0,0 +1,457 @@
+/*
+ Copyright (c) 2010 ,
+ Cloud Wu . All rights reserved.
+
+ http://www.codingnow.com
+
+ Use, modification and distribution are subject to the "New BSD License"
+ as listed at <url: http://www.opensource.org/licenses/bsd-license.php >.
+
+ filename: backtrace.c
+
+ compiler: gcc 3.4.5 (mingw-win32)
+
+ build command: gcc -O2 -shared -Wall -o backtrace.dll backtrace.c -lbfd -liberty -limagehlp
+
+ how to use: Call LoadLibraryA("backtrace.dll"); at beginning of your program .
+
+*/
+
+#define PACKAGE "backtrace"
+#define PACKAGE_VERSION "0.1"
+
+#include <windows.h>
+#include <excpt.h>
+#include <imagehlp.h>
+#include <bfd.h>
+#include <psapi.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <string.h>
+#include <stdbool.h>
+
+#define BUFFER_MAX (16*1024)
+
+#define BFD_ERR_OK (0)
+#define BFD_ERR_OPEN_FAIL (1)
+#define BFD_ERR_BAD_FORMAT (2)
+#define BFD_ERR_NO_SYMBOLS (3)
+#define BFD_ERR_READ_SYMBOL (4)
+
+static const char *const bfd_errors[] = {
+ "",
+ "(Failed to open bfd)",
+ "(Bad format)",
+ "(No symbols)",
+ "(Failed to read symbols)",
+};
+
+struct bfd_ctx {
+ bfd * handle;
+ asymbol ** symbol;
+};
+
+struct bfd_set {
+ char * name;
+ struct bfd_ctx * bc;
+ struct bfd_set *next;
+};
+
+struct find_info {
+ asymbol **symbol;
+ bfd_vma counter;
+ const char *file;
+ const char *func;
+ unsigned line;
+};
+
+struct output_buffer {
+ char * buf;
+ size_t sz;
+ size_t ptr;
+};
+
+static const char*
+exception_name(DWORD code)
+{
+ switch(code)
+ {
+ case EXCEPTION_ACCESS_VIOLATION:
+ return "access violation";
+ case EXCEPTION_ARRAY_BOUNDS_EXCEEDED:
+ return "array index out of bound";
+ case EXCEPTION_BREAKPOINT:
+ return "breakpoint reached";
+ case EXCEPTION_DATATYPE_MISALIGNMENT:
+ return "misaligned data access";
+ case EXCEPTION_FLT_DENORMAL_OPERAND:
+ return "operand had denormal value";
+ case EXCEPTION_FLT_DIVIDE_BY_ZERO:
+ return "floating-point division by zero";
+ case EXCEPTION_FLT_INEXACT_RESULT:
+ return "no decimal fraction representation for value";
+ case EXCEPTION_FLT_INVALID_OPERATION:
+ return "invalid floating-point operation";
+ case EXCEPTION_FLT_OVERFLOW:
+ return "floating-point overflow";
+ case EXCEPTION_FLT_STACK_CHECK:
+ return "floating-point stack corruption";
+ case EXCEPTION_FLT_UNDERFLOW:
+ return "floating-point underflow";
+ case EXCEPTION_ILLEGAL_INSTRUCTION:
+ return "illegal instruction";
+ case EXCEPTION_IN_PAGE_ERROR:
+ return "inaccessible page";
+ case EXCEPTION_INT_DIVIDE_BY_ZERO:
+ return "integer division by zero";
+ case EXCEPTION_INT_OVERFLOW:
+ return "integer overflow";
+ case EXCEPTION_INVALID_DISPOSITION:
+ return "documentation says this should never happen";
+ case EXCEPTION_NONCONTINUABLE_EXCEPTION:
+ return "can't continue after a noncontinuable exception";
+ case EXCEPTION_PRIV_INSTRUCTION:
+ return "attempted to execute a privileged instruction";
+ case EXCEPTION_SINGLE_STEP:
+ return "one instruction has been executed";
+ case EXCEPTION_STACK_OVERFLOW:
+ return "stack overflow";
+ }
+ return "unknown exception";
+}
+
+static void
+output_init(struct output_buffer *ob, char * buf, size_t sz)
+{
+ ob->buf = buf;
+ ob->sz = sz;
+ ob->ptr = 0;
+ ob->buf[0] = '\0';
+}
+
+static void
+output_print(struct output_buffer *ob, const char * format, ...)
+{
+ if (ob->sz == ob->ptr)
+ return;
+ ob->buf[ob->ptr] = '\0';
+ va_list ap;
+ va_start(ap,format);
+ vsnprintf(ob->buf + ob->ptr , ob->sz - ob->ptr , format, ap);
+ va_end(ap);
+
+ ob->ptr = strlen(ob->buf + ob->ptr) + ob->ptr;
+}
+
+static void
+lookup_section(bfd *abfd, asection *sec, void *opaque_data)
+{
+ struct find_info *data = opaque_data;
+
+ if (data->func)
+ return;
+
+ if (!(bfd_get_section_flags(abfd, sec) & SEC_ALLOC))
+ return;
+
+ bfd_vma vma = bfd_get_section_vma(abfd, sec);
+ if (data->counter < vma || vma + bfd_get_section_size(sec) <= data->counter)
+ return;
+
+ bfd_find_nearest_line(abfd, sec, data->symbol, data->counter - vma, &(data->file), &(data->func), &(data->line));
+}
+
+static void
+find(struct bfd_ctx * b, DWORD offset, const char **file, const char **func, unsigned *line)
+{
+ struct find_info data;
+ data.func = NULL;
+ data.symbol = b->symbol;
+ data.counter = offset;
+ data.file = NULL;
+ data.func = NULL;
+ data.line = 0;
+
+ bfd_map_over_sections(b->handle, &lookup_section, &data);
+ if (file) {
+ *file = data.file;
+ }
+ if (func) {
+ *func = data.func;
+ }
+ if (line) {
+ *line = data.line;
+ }
+}
+
+static int
+init_bfd_ctx(struct bfd_ctx *bc, const char * procname, int *err)
+{
+ bc->handle = NULL;
+ bc->symbol = NULL;
+
+ bfd *b = bfd_openr(procname, 0);
+ if (!b) {
+ if(err) { *err = BFD_ERR_OPEN_FAIL; }
+ return 1;
+ }
+
+ if(!bfd_check_format(b, bfd_object)) {
+ bfd_close(b);
+ if(err) { *err = BFD_ERR_BAD_FORMAT; }
+ return 1;
+ }
+
+ if(!(bfd_get_file_flags(b) & HAS_SYMS)) {
+ bfd_close(b);
+ if(err) { *err = BFD_ERR_NO_SYMBOLS; }
+ return 1;
+ }
+
+ void *symbol_table;
+
+ unsigned dummy = 0;
+ if (bfd_read_minisymbols(b, FALSE, &symbol_table, &dummy) == 0) {
+ if (bfd_read_minisymbols(b, TRUE, &symbol_table, &dummy) < 0) {
+ free(symbol_table);
+ bfd_close(b);
+ if(err) { *err = BFD_ERR_READ_SYMBOL; }
+ return 1;
+ }
+ }
+
+ bc->handle = b;
+ bc->symbol = symbol_table;
+
+ if(err) { *err = BFD_ERR_OK; }
+ return 0;
+}
+
+static void
+close_bfd_ctx(struct bfd_ctx *bc)
+{
+ if (bc) {
+ if (bc->symbol) {
+ free(bc->symbol);
+ }
+ if (bc->handle) {
+ bfd_close(bc->handle);
+ }
+ }
+}
+
+static struct bfd_ctx *
+get_bc(struct bfd_set *set , const char *procname, int *err)
+{
+ while(set->name) {
+ if (strcmp(set->name , procname) == 0) {
+ return set->bc;
+ }
+ set = set->next;
+ }
+ struct bfd_ctx bc;
+ if (init_bfd_ctx(&bc, procname, err)) {
+ return NULL;
+ }
+ set->next = calloc(1, sizeof(*set));
+ set->bc = malloc(sizeof(struct bfd_ctx));
+ memcpy(set->bc, &bc, sizeof(bc));
+ set->name = strdup(procname);
+
+ return set->bc;
+}
+
+static void
+release_set(struct bfd_set *set)
+{
+ while(set) {
+ struct bfd_set * temp = set->next;
+ free(set->name);
+ close_bfd_ctx(set->bc);
+ free(set);
+ set = temp;
+ }
+}
+
+static void
+_backtrace(struct output_buffer *ob, struct bfd_set *set, int depth , LPCONTEXT context)
+{
+ char procname[MAX_PATH];
+ GetModuleFileNameA(NULL, procname, sizeof procname);
+
+ struct bfd_ctx *bc = NULL;
+ int err = BFD_ERR_OK;
+ DWORD machine_type = 0;
+
+ STACKFRAME frame;
+ memset(&frame,0,sizeof(frame));
+
+#if defined(_M_IX86) || defined(__i386__)
+ machine_type = IMAGE_FILE_MACHINE_I386;
+ frame.AddrPC.Offset = context->Eip;
+ frame.AddrStack.Offset = context->Esp;
+ frame.AddrFrame.Offset = context->Ebp;
+#elif defined(_M_IX64) || defined(__amd64__)
+ machine_type = IMAGE_FILE_MACHINE_AMD64;
+ frame.AddrPC.Offset = context->Rip;
+ frame.AddrStack.Offset = context->Rsp;
+ frame.AddrFrame.Offset = context->Rbp;
+#else
+#error "Unsupported platform"
+ //IA-64 anybody?
+#endif
+ frame.AddrPC.Mode = AddrModeFlat;
+ frame.AddrStack.Mode = AddrModeFlat;
+ frame.AddrFrame.Mode = AddrModeFlat;
+
+ HANDLE process = GetCurrentProcess();
+ HANDLE thread = GetCurrentThread();
+
+ char symbol_buffer[sizeof(IMAGEHLP_SYMBOL) + 255];
+ char module_name_raw[MAX_PATH];
+
+ while(StackWalk64(machine_type,
+ process,
+ thread,
+ &frame,
+ context,
+ 0,
+ SymFunctionTableAccess,
+ SymGetModuleBase, 0)) {
+
+ --depth;
+ if (depth < 0)
+ break;
+
+ IMAGEHLP_SYMBOL *symbol = (IMAGEHLP_SYMBOL *)symbol_buffer;
+ symbol->SizeOfStruct = (sizeof *symbol) + 255;
+ symbol->MaxNameLength = 254;
+
+ DWORD module_base = SymGetModuleBase(process, frame.AddrPC.Offset);
+
+ const char * module_name = "[unknown module]";
+ if (module_base &&
+ GetModuleFileNameA((HINSTANCE)module_base, module_name_raw, MAX_PATH)) {
+ module_name = module_name_raw;
+ bc = get_bc(set, module_name, &err);
+ }
+
+ const char * source_file = NULL;
+ const char * func = NULL;
+ unsigned line = 0;
+
+ if (bc) {
+ find(bc,frame.AddrPC.Offset,&source_file,&func,&line);
+ }
+
+ if (source_file == NULL) {
+ DWORD dummy = 0;
+ if (SymGetSymFromAddr(process, frame.AddrPC.Offset, &dummy, symbol)) {
+ source_file = symbol->Name;
+ }
+ else {
+ source_file = "[unknown file]";
+ }
+ }
+ if (func == NULL) {
+ output_print(ob,"0x%08x from %s in %s %s \n",
+ frame.AddrPC.Offset,
+ module_name,
+ source_file,
+ bfd_errors[err]);
+ }
+ else {
+ output_print(ob,"0x%08x in %s at %s:%d from %s \n",
+ frame.AddrPC.Offset,
+ func,
+ source_file,
+ line,
+ module_name);
+ }
+ }
+}
+
+static char * g_output = NULL;
+static LPTOP_LEVEL_EXCEPTION_FILTER g_prev = NULL;
+
+static LONG WINAPI
+exception_filter(LPEXCEPTION_POINTERS info)
+{
+ struct output_buffer ob;
+ output_init(&ob, g_output, BUFFER_MAX);
+
+ if (!SymInitialize(GetCurrentProcess(), 0, TRUE)) {
+ output_print(&ob,"Failed to init symbol context\n");
+ }
+ else {
+ bfd_init();
+ PEXCEPTION_RECORD rec = info->ExceptionRecord;
+ output_print(&ob,"Unhandled exception occured at 0x%08x: %s.\n",
+ rec->ExceptionAddress,
+ exception_name(rec->ExceptionCode)
+ );
+ if (rec->ExceptionCode == EXCEPTION_ACCESS_VIOLATION || rec->ExceptionCode == EXCEPTION_IN_PAGE_ERROR)
+ if (rec->NumberParameters >= 2) {
+ const char *op =
+ rec->ExceptionInformation[0] == 0 ? "read" :
+ rec->ExceptionInformation[0] == 1 ? "written" : "executed";
+ output_print(&ob, "The data at memory address 0x%08x could not be %s.\n",
+ rec->ExceptionInformation[1], op);
+ }
+ struct bfd_set *set = calloc(1,sizeof(*set));
+ _backtrace(&ob , set , 128 , info->ContextRecord);
+ release_set(set);
+
+ SymCleanup(GetCurrentProcess());
+ }
+
+ fputs(g_output , stderr);
+
+ return EXCEPTION_CONTINUE_SEARCH;
+}
+
+static void
+backtrace_register(void)
+{
+ if (g_output == NULL) {
+ g_output = malloc(BUFFER_MAX);
+ g_prev = SetUnhandledExceptionFilter(exception_filter);
+ }
+}
+
+static void
+backtrace_unregister(void)
+{
+ if (g_output) {
+ free(g_output);
+ SetUnhandledExceptionFilter(g_prev);
+ g_prev = NULL;
+ g_output = NULL;
+ }
+}
+
+int
+__printf__(const char * format, ...) {
+ int value;
+ va_list arg;
+ va_start(arg, format);
+ value = vprintf ( format, arg );
+ va_end(arg);
+ return value;
+}
+
+__declspec(dllexport) BOOL WINAPI
+DllMain(HINSTANCE hinstDLL, DWORD dwReason, LPVOID lpvReserved)
+{
+ switch (dwReason) {
+ case DLL_PROCESS_ATTACH:
+ backtrace_register();
+ break;
+ case DLL_PROCESS_DETACH:
+ backtrace_unregister();
+ break;
+ }
+ return TRUE;
+}
+
diff --git a/third_party/backtrace-mingw/test.c b/third_party/backtrace-mingw/test.c
new file mode 100644
index 0000000..731129f
--- /dev/null
+++ b/third_party/backtrace-mingw/test.c
@@ -0,0 +1,23 @@
+#include <windows.h>
+
+static void
+foo()
+{
+ int *f=NULL;
+ *f = 0;
+}
+
+static void
+bar()
+{
+ foo();
+}
+
+int
+main()
+{
+ LoadLibraryA("backtrace.dll");
+ bar();
+
+ return 0;
+}