diff --git a/CMakeLists.txt b/CMakeLists.txt index 78a68aa8..e6eab676 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -21,6 +21,7 @@ set(BUILD_GUI_DEFAULT ON) set(USE_SDL2_DEFAULT ON) set(USE_SNDFILE_DEFAULT ON) set(SYSTEM_SDL2_DEFAULT OFF) +set(USE_BACKTRACE_MINGW64_DEFAULT OFF) include(CheckIncludeFile) include(TestBigEndian) @@ -34,8 +35,19 @@ if (ANDROID) endif() else() set(USE_RTMIDI_DEFAULT ON) - if (WIN32 OR APPLE) + if (APPLE) set(USE_BACKWARD_DEFAULT ON) + elseif (WIN32) + if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU") + set(USE_BACKWARD_DEFAULT OFF) + set(USE_BACKTRACE_MINGW64_DEFAULT ON) + elseif (MSVC OR CMAKE_CXX_COMPILER_ID STREQUAL "Clang") + set(USE_BACKWARD_DEFAULT ON) + set(USE_BACKTRACE_MINGW64_DEFAULT OFF) + else() + set(USE_BACKWARD_DEFAULT OFF) + set(USE_BACKTRACE_MINGW64_DEFAULT OFF) + endif() else() CHECK_INCLUDE_FILE(execinfo.h EXECINFO_FOUND) if (EXECINFO_FOUND) @@ -59,6 +71,11 @@ option(USE_RTMIDI "Build with MIDI support using RtMidi." ${USE_RTMIDI_DEFAULT}) option(USE_SDL2 "Build with SDL2. Required to build with GUI." ${USE_SDL2_DEFAULT}) option(USE_SNDFILE "Build with libsndfile. Required in order to work with audio files." ${USE_SNDFILE_DEFAULT}) option(USE_BACKWARD "Use backward-cpp to print a backtrace on crash/abort." ${USE_BACKWARD_DEFAULT}) +if (WIN32) + option(USE_BACKTRACE_MINGW64 "Use a modified backtrace-mingw64 to print a backtrace on crash/abort." ${USE_BACKTRACE_MINGW64_DEFAULT}) +else() + set(USE_BACKTRACE_MINGW64 OFF) +endif() option(WITH_JACK "Whether to build with JACK support. Auto-detects if JACK is available" ${WITH_JACK_DEFAULT}) option(SYSTEM_FFTW "Use a system-installed version of FFTW instead of the vendored one" OFF) option(SYSTEM_FMT "Use a system-installed version of fmt instead of the vendored one" OFF) @@ -704,6 +721,10 @@ endif() set(USED_SOURCES ${ENGINE_SOURCES} ${AUDIO_SOURCES} ${CLI_SOURCES} src/main.cpp) +if (USE_BACKWARD AND USE_BACKTRACE_MINGW64) + message(FATAL_ERROR "either USE_BACKTRACE_MINGW64 or USE_BACKWARD, but not both!") +endif() + if (USE_BACKWARD) list(APPEND USED_SOURCES src/backtrace.cpp) if (WIN32 AND CMAKE_CXX_COMPILER_ID STREQUAL "GNU") @@ -718,6 +739,17 @@ else() message(STATUS "Not using backward-cpp") endif() +if (WIN32) + if (USE_BACKTRACE_MINGW64) + list(APPEND USED_SOURCES extern/backtrace-mingw64/backtrace.c) + list(APPEND DEPENDENCIES_DEFINES HAVE_BACKTRACE_MINGW64) + list(APPEND DEPENDENCIES_LIBRARIES bfd imagehlp) + message(STATUS "Using backtrace-mingw64") + else() + message(STATUS "Not using backtrace-mingw64") + endif() +endif() + if (BUILD_GUI) list(APPEND USED_SOURCES ${GUI_SOURCES}) list(APPEND DEPENDENCIES_INCLUDE_DIRS diff --git a/extern/backtrace-mingw64/README.md b/extern/backtrace-mingw64/README.md new file mode 100644 index 00000000..6663f1eb --- /dev/null +++ b/extern/backtrace-mingw64/README.md @@ -0,0 +1,15 @@ +# MODIFIED + +the code has been modified in order to suit Furnace's needs. + +# backtrace library for mingw64 and mingw32 + +This library produces backtraces for program crashes. + +## History + +Clone of 32 bit version from this [backtrace-mingw](https://github.com/cloudwu/backtrace-mingw) repo, which was exported from code.google.com/p/backtrace-mingw + +64 bit version made the necessary minor tweaks to get the library working under x86_64 architecture + +Furnace version... changes by tildearrow diff --git a/extern/backtrace-mingw64/backtrace.c b/extern/backtrace-mingw64/backtrace.c new file mode 100644 index 00000000..602b9780 --- /dev/null +++ b/extern/backtrace-mingw64/backtrace.c @@ -0,0 +1,479 @@ +/* + 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 . + + 190623 jpmattia: Updated to work for x86_64 under msys2 + 230424 tildearrow: Merged with 32-bit backtrace and tied to Furnace + + filename: backtrace64.c + + how to use: Be Luigi and don't touch the controller. + + +*/ + +#define TA_64BIT + +#define PACKAGE "furnace" /* for libbfd */ +#define PACKAGE_VERSION + +#include +#include +#include +#include +#include +#include +#include +#ifdef TA_64BIT +#include +#endif +#include +#include +#include + +#include "backtrace.h" + +#define BUFFER_MAX (16*1024) + +#ifndef TA_64BIT +#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)", +}; +#endif + +struct bfd_ctx { + bfd *handle; + asymbol **symbol; +}; + +#ifndef TA_64BIT +struct bfd_set { + char *name; + struct bfd_ctx *bc; + struct bfd_set *next; ++}; +#endif + +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 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) { +#ifdef TA_64BIT + assert(sec); + assert(opaque_data); +#endif + 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; + } +} + +#ifdef TA_64BIT +static int init_bfd_ctx(struct bfd_ctx *bc, struct output_buffer *ob) { +#else +static int init_bfd_ctx(struct bfd_ctx *bc, const char *procname, int *err) { +#endif + bc->handle = NULL; + bc->symbol = NULL; + +#ifdef TA_64BIT + char procname[MAX_PATH]; + GetModuleFileNameA(NULL, procname, sizeof procname); + + bfd_init(); +#endif + bfd *b = bfd_openr(procname, 0); + if (!b) { +#ifdef TA_64BIT + output_print(ob, "Failed to init bfd\n"); +#else + if (err) { + *err = BFD_ERR_OPEN_FAIL; + } +#endif + return 1; + } + +#ifdef TA_64BIT + int r1 = bfd_check_format(b, bfd_object); + int r2 = bfd_check_format_matches(b, bfd_object, NULL); + int r3 = bfd_get_file_flags(b) & HAS_SYMS; +#else + if (!bfd_check_format(b, bfd_object)) { + bfd_close(b); + if (err) { + *err = BFD_ERR_BAD_FORMAT; + } + return 1; + } +#endif + +#ifdef TA_64BIT + if (!(r1 && r2 && r3)) { +#else + if (!(bfd_get_file_flags(b) & HAS_SYMS)) { +#endif + bfd_close(b); +#ifdef TA_64BIT + output_print(ob, "Failed to init bfd\n"); +#else + if (err) { + *err = BFD_ERR_NO_SYMBOLS; + } +#endif + 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); +#ifdef TA_64BIT + output_print(ob, "Failed to init bfd\n"); +#else + if (err) { + *err = BFD_ERR_READ_SYMBOL; + } +#endif + return 1; + } + } + + bc->handle = b; + bc->symbol = symbol_table; + +#ifndef TA_64BIT + if (err) { + *err = BFD_ERR_OK; + } +#endif + return 0; +} + +static void close_bfd_ctx(struct bfd_ctx *bc) { +#ifdef TA_64BIT + if (bc->symbol) { + free(bc->symbol); + } + if (bc->handle) { + bfd_close(bc->handle); + } +#else + if (bc) { + if (bc->symbol) { + free(bc->symbol); + } + if (bc->handle) { + bfd_close(bc->handle); + } + } +#endif +} + +#ifndef TA_64BIT +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; + } +} +#endif + +#ifdef TA_64BIT +static void +_backtrace(struct output_buffer *ob, struct bfd_ctx *bc, int depth, + LPCONTEXT context) { +#else +static void +_backtrace(struct output_buffer *ob, struct bfd_set *set, int depth, + LPCONTEXT context) { +#endif +#ifdef TA_64BIT + if (init_bfd_ctx(bc, ob)) + return; +#else + char procname[MAX_PATH]; + GetModuleFileNameA(NULL, procname, sizeof procname); + + struct bfd_ctx *bc = NULL; + int err = BFD_ERR_OK; +#endif + + STACKFRAME frame; + memset(&frame, 0, sizeof(frame)); + +#ifdef TA_64BIT + frame.AddrPC.Offset = context->Rip; + frame.AddrPC.Mode = AddrModeFlat; + frame.AddrStack.Offset = context->Rsp; + frame.AddrStack.Mode = AddrModeFlat; + frame.AddrFrame.Offset = context->Rbp; + frame.AddrFrame.Mode = AddrModeFlat; +#else + frame.AddrPC.Offset = context->Eip; + frame.AddrPC.Mode = AddrModeFlat; + frame.AddrStack.Offset = context->Esp; + frame.AddrStack.Mode = AddrModeFlat; + frame.AddrFrame.Offset = context->Ebp; + frame.AddrFrame.Mode = AddrModeFlat; +#endif + + HANDLE process = GetCurrentProcess(); + HANDLE thread = GetCurrentThread(); + + char symbol_buffer[sizeof(IMAGEHLP_SYMBOL) + 255]; + char module_name_raw[MAX_PATH]; + +#ifdef TA_64BIT + while (StackWalk(IMAGE_FILE_MACHINE_AMD64, /* walk the stack for the x86_64 architecture */ +#else + while (StackWalk(IMAGE_FILE_MACHINE_I386, +#endif + 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; + + +#ifdef TA_64BIT + /* SymGetModuleBase returns a DWORD (which is 32 bits). + We need module_base for GetModuleFileNameA, which requires a 64 bit HINSTANCE, so + do the conversion here to avoid a compiler warning. + */ + HINSTANCE module_base = + (HINSTANCE) SymGetModuleBase(process, frame.AddrPC.Offset); +#else + DWORD module_base = SymGetModuleBase(process, frame.AddrPC.Offset); +#endif + + const char *module_name = "[unknown module]"; +#ifdef TA_64BIT + if (module_base + && GetModuleFileNameA(module_base, module_name_raw, MAX_PATH)) { + module_name = module_name_raw; + } +#else + if (module_base && + GetModuleFileNameA((HINSTANCE) module_base, module_name_raw, + MAX_PATH)) { + module_name = module_name_raw; + bc = get_bc(set, module_name, &err); + } +#endif + +#ifdef TA_64BIT + const char *file; + const char *func; + unsigned line; +#else + const char *file = NULL; + const char *func = NULL; + unsigned line = 0; +#endif + +#ifdef TA_64BIT + find(bc, frame.AddrPC.Offset, &file, &func, &line); +#else + if (bc) { + find(bc, frame.AddrPC.Offset, &file, &func, &line); + } +#endif + + if (file == NULL) { +#ifdef TA_64BIT + DWORD64 dummy = 0; +#else + DWORD dummy = 0; +#endif + if (SymGetSymFromAddr(process, frame.AddrPC.Offset, &dummy, symbol)) { + file = symbol->Name; + } else { + file = "[unknown file]"; + } + } +#ifdef TA_64BIT + if (func == NULL) { + func = "[unknown func]"; + } + + output_print(ob, "0x%x : %s : %s (%d) : in function (%s) \n", + frame.AddrPC.Offset, module_name, file, line, func); +#else + if (func == NULL) { + output_print(ob, "0x%08x : %s : %s %s \n", + frame.AddrPC.Offset, + module_name, file, bfd_errors[err]); + } else { + output_print(ob, "0x%08x : %s : %s (%d) : in function (%s) \n", + frame.AddrPC.Offset, module_name, file, line, func); + } + +#endif + } +} + +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 { +#ifdef TA_64BIT + struct bfd_ctx bc; + _backtrace(&ob, &bc, 128, info->ContextRecord); + close_bfd_ctx(&bc); +#else + bfd_init(); + struct bfd_set *set = calloc(1, sizeof(*set)); + _backtrace(&ob, set, 128, info->ContextRecord); + release_set(set); +#endif + + SymCleanup(GetCurrentProcess()); + } + + fputs(g_output, stderr); + +#ifdef TA_64BIT + // huh? + exit(1); + + return 0; +#else + return EXCEPTION_CONTINUE_SEARCH; +#endif +} + +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; + } +} diff --git a/extern/backtrace-mingw64/backtrace.h b/extern/backtrace-mingw64/backtrace.h new file mode 100644 index 00000000..a846f49c --- /dev/null +++ b/extern/backtrace-mingw64/backtrace.h @@ -0,0 +1,2 @@ +static void backtrace_register(void); +static void backtrace_unregister(void);