diff --git a/CMakeLists.txt b/CMakeLists.txt index 5afeef9f0..443a7295f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -39,6 +39,7 @@ endif() if(UNIX AND NOT APPLE) option(USE_XRANDR "Use xrandr instead of vidmode" ON) option(USE_ASAN "Build with Leak/Address sanitizer" OFF) + option(USE_LIBBFD "Use libbfd for crash reporting and leak check" OFF) endif() set(STK_SOURCE_DIR "src") @@ -364,6 +365,10 @@ if(UNIX AND NOT APPLE) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=address -fno-omit-frame-pointer") target_link_libraries(supertuxkart "-fsanitize=address") endif() + if(USE_LIBBFD) + add_definitions(-DENABLE_LIBBFD) + target_link_libraries(supertuxkart "-lbfd") + endif() endif() # FreeBSD does not search in /usr/local/lib, but at least Freetype is installed there :( diff --git a/src/utils/crash_reporting.cpp b/src/utils/crash_reporting.cpp index 034425aac..0ce8e5d94 100644 --- a/src/utils/crash_reporting.cpp +++ b/src/utils/crash_reporting.cpp @@ -320,19 +320,201 @@ } } // end namespace CrashReporting -#else +#elif ENABLE_LIBBFD // --------------------- Unix version ----------------------- + /* Derived from addr2line.c from binutils + + addr2line.c -- convert addresses to line number and function name + Copyright (C) 1997-2015 Free Software Foundation, Inc. + Contributed by Ulrich Lauther + + This file is part of GNU Binutils. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, 51 Franklin Street - Fifth Floor, Boston, + MA 02110-1301, USA. */ + + #define PACKAGE 1 + #define PACKAGE_VERSION 1 + #include + #include + #include + + #include "string_utils.hpp" + namespace CrashReporting { + // BFD of current running STK binary, only can be useful + // if compiled with debug symbols + static bfd *m_stk_bfd = NULL; + + // Symbol table + static asymbol **m_syms = NULL; + + // Address in BFD to find file names or function name + static bfd_vma m_adress = 0; + + static const char *m_file_name = NULL; + static const char *m_function_name = NULL; + static unsigned int m_line = 0; + static int m_found = 0; + + // Look for an address in a section. This is called via + // bfd_map_over_sections. + void findAddressInSection(bfd* input, asection *section, + void *data ATTRIBUTE_UNUSED) + { + bfd_vma vma = 0; + bfd_size_type size = 0; + + if (m_found) + return; + + if ((bfd_get_section_flags(m_stk_bfd, section) & SEC_ALLOC) == 0) + return; + + vma = bfd_get_section_vma(m_stk_bfd, section); + if (m_adress < vma) + return; + + size = bfd_section_size(m_stk_bfd, section); + if (m_adress >= vma + size) + return; + + m_found = bfd_find_nearest_line(m_stk_bfd, section, m_syms, + m_adress - vma, &m_file_name, &m_function_name, &m_line); + } + + void signalHandler(int signal_no) + { + if (m_stk_bfd == NULL) + { + Log::warn("CrashReporting", "Failed loading or missing BFD of " + "STK binary, no backtrace available when reporting"); + exit(0); + } + + Log::error("CrashReporting", "STK has crashed! Backtrace info:"); + std::string stack; + getCallStack(stack); + + std::vector each = StringUtils::split(stack, '\n'); + for (unsigned int i = 3; i < each.size(); i++) + { + // Skip 3 stacks which are crash_reporting doing + Log::error("CrashReporting", "%s", each[i].c_str()); + } + exit(0); + } + + void loadSTKBFD() + { + const char* path = realpath("/proc/self/exe", NULL); + m_stk_bfd = bfd_openr(path, NULL); + free((void*)path); + + if (m_stk_bfd == NULL) + { + return; + } + + if (bfd_check_format(m_stk_bfd, bfd_archive)) + { + m_stk_bfd = NULL; + return; + } + + if (!bfd_check_format(m_stk_bfd, bfd_object)) + { + m_stk_bfd = NULL; + return; + } + + // Read in the symbol table. + unsigned int size = 0; + long symcount = 0; + + if ((bfd_get_file_flags(m_stk_bfd) & HAS_SYMS) == 0) + { + m_stk_bfd = NULL; + return; + } + + symcount = bfd_read_minisymbols(m_stk_bfd, false, (void**)&m_syms, + &size); + if (symcount == 0) + { + symcount = bfd_read_minisymbols(m_stk_bfd, true/* dynamic*/, + (void**)&m_syms, &size); + } + + if (symcount < 0) + { + m_stk_bfd = NULL; + m_syms = NULL; + return; + } + } + void installHandlers() { - // TODO! + loadSTKBFD(); + struct sigaction sa = {0}; + sa.sa_handler = &signalHandler; + sigemptyset(&sa.sa_mask); + sa.sa_flags = SA_RESTART; + sigaction(SIGSEGV, &sa, NULL); + sigaction(SIGUSR1, &sa, NULL); } void getCallStack(std::string& callstack) { - // TODO! + if (m_stk_bfd == NULL) return; + + void *trace[16]; + int i, trace_size = 0; + trace_size = backtrace(trace, 16); + for (i = 0; i < trace_size; i++) + { + m_adress = (bfd_vma)(trace[i]); + m_found = 0; + m_file_name = NULL; + m_function_name = NULL; + m_line = 0; + + bfd_map_over_sections(m_stk_bfd, findAddressInSection, NULL); + if (m_found && m_file_name != NULL) + { + callstack = callstack + m_file_name + ":" + + StringUtils::toString(m_line) + "\n"; + } + else if (m_function_name != NULL) + { + callstack = callstack + m_function_name + "\n"; + } + else + callstack = callstack + "No symbol found" + "\n"; + } } } // end namespace CrashReporting +#else + + namespace CrashReporting + { + void installHandlers() {} + void getCallStack(std::string& callstack) {} + } // end namespace CrashReporting + #endif diff --git a/src/utils/leak_check.cpp b/src/utils/leak_check.cpp index 2f8920ee7..3869aff20 100644 --- a/src/utils/leak_check.cpp +++ b/src/utils/leak_check.cpp @@ -26,8 +26,7 @@ #ifdef DEBUG -/** Switch this to 1 to get the backtrace of the leaks (slows down execution a little) - * Atm only implemented for OSX and windows. */ +/** Define this to get the backtrace of the leaks (slows down execution a little) */ #undef GET_STACK_TRACE Synchronised m_lock_stacktrace; @@ -59,7 +58,7 @@ namespace MemoryLeaks m_stack_size = backtrace(callstack, max_size); m_stack = backtrace_symbols(callstack, m_stack_size); -# elif defined(WIN32) +# elif defined(WIN32) || ENABLE_LIBBFD m_lock_stacktrace.lock(); CrashReporting::getCallStack(m_stack); m_lock_stacktrace.unlock(); @@ -79,7 +78,14 @@ namespace MemoryLeaks void AllocatedObject::print() const { #ifdef GET_STACK_TRACE -# if defined(__APPLE__) +# if defined ENABLE_LIBBFD + std::vector calls = StringUtils::split(m_stack, '\n'); + // Ignore the first 3 entries + for (unsigned int i = 3; i < calls.size(); ++i) + { + Log::error("LeakCheck", " %s", calls[i].c_str()); + } +# elif defined(__APPLE__) for (int i = 0; i < m_stack_size; ++i) { Log::error("LeakCheck", " %s\n", m_stack[i]); diff --git a/src/utils/leak_check.hpp b/src/utils/leak_check.hpp index b04a6db80..2a1ee903b 100644 --- a/src/utils/leak_check.hpp +++ b/src/utils/leak_check.hpp @@ -40,9 +40,9 @@ namespace MemoryLeaks char **m_stack; /** Keeps stacksize information if available (OSX only). */ int m_stack_size; -#elif defined(WIN32) - /** Keep the stack information the way it is returned by windows, - * a flat string, which will be split when printing it. */ +#elif defined(WIN32) || ENABLE_LIBBFD + /** Keep the stack information the way it is returned by windows or + * libbfd, a flat string, which will be split when printing it. */ std::string m_stack; #endif