Add optional linux crash reporting with backtrace (using libbfd)

Notice:

To get backtrace too for leak check, you need to
define GET_STACK_TRACE in leak_check.cpp, but will slow down STK
This commit is contained in:
Benau 2016-06-23 16:18:17 +08:00
parent c17070209d
commit 6222ce650f
4 changed files with 203 additions and 10 deletions

View File

@ -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 :(

View File

@ -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 <Ulrich.Lauther@mchp.siemens.de>
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 <signal.h>
#include <execinfo.h>
#include <bfd.h>
#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<std::string> 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

View File

@ -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<int> 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<std::string> 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]);

View File

@ -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