interface/debug: Backtrace logging on Linux
This commit is contained in:
parent
3501693188
commit
25d9e994a3
@ -135,9 +135,11 @@ set(BUILDAT_CORE_SRCS
|
||||
if(WIN32)
|
||||
set(BUILDAT_CORE_SRCS ${BUILDAT_CORE_SRCS} src/impl/windows/file_watch.cpp)
|
||||
set(BUILDAT_CORE_SRCS ${BUILDAT_CORE_SRCS} src/impl/windows/process.cpp)
|
||||
set(BUILDAT_CORE_SRCS ${BUILDAT_CORE_SRCS} src/impl/windows/debug.cpp)
|
||||
else()
|
||||
set(BUILDAT_CORE_SRCS ${BUILDAT_CORE_SRCS} src/impl/linux/file_watch.cpp)
|
||||
set(BUILDAT_CORE_SRCS ${BUILDAT_CORE_SRCS} src/impl/linux/process.cpp)
|
||||
set(BUILDAT_CORE_SRCS ${BUILDAT_CORE_SRCS} src/impl/linux/debug.cpp)
|
||||
endif()
|
||||
add_library(${BUILDAT_CORE_NAME} SHARED ${BUILDAT_CORE_SRCS})
|
||||
target_link_libraries(${BUILDAT_CORE_NAME}
|
||||
|
@ -70,3 +70,5 @@ Buildat TODO
|
||||
and implement the required interfaces
|
||||
- Fix everything in /games/
|
||||
- Use builtin/main_context
|
||||
- A debug handler that is attached to every thread, which will log a backtrace
|
||||
of exceptions and segfaults
|
||||
|
139
src/impl/linux/debug.cpp
Normal file
139
src/impl/linux/debug.cpp
Normal file
@ -0,0 +1,139 @@
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
// Copyright 2014 Perttu Ahola <celeron55@gmail.com>
|
||||
#include "interface/debug.h"
|
||||
#include "core/log.h"
|
||||
#include <c55/string_util.h>
|
||||
#include <stdexcept>
|
||||
#include <iostream>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <execinfo.h>
|
||||
#include <signal.h>
|
||||
#include <sys/ucontext.h>
|
||||
#include <unistd.h>
|
||||
#define MODULE "debug"
|
||||
|
||||
namespace interface {
|
||||
namespace debug {
|
||||
|
||||
// Execute a program and return whatever it writes to stdout
|
||||
static ss_ exec_get_stdout(char *cmd)
|
||||
{
|
||||
FILE* pipe = popen(cmd, "r");
|
||||
if(!pipe) return "ERROR";
|
||||
char buffer[50];
|
||||
ss_ result;
|
||||
try{
|
||||
while(!feof(pipe)){
|
||||
if(fgets(buffer, 50, pipe) != NULL)
|
||||
result += buffer; // Can cause std::bad_alloc
|
||||
}
|
||||
pclose(pipe);
|
||||
return result;
|
||||
}catch(std::bad_alloc){
|
||||
return ss_();
|
||||
}
|
||||
}
|
||||
|
||||
// Execute a program and return whatever it writes to stdout, without trailing
|
||||
// newline
|
||||
static ss_ exec_get_stdout_without_newline(char *cmd)
|
||||
{
|
||||
ss_ s = exec_get_stdout(cmd);
|
||||
if(!s.empty() && s[s.size()-1] == '\n')
|
||||
s = s.substr(0, s.size()-1);
|
||||
return s;
|
||||
}
|
||||
|
||||
static void* get_library_executable_address(const ss_ &lib_path)
|
||||
{
|
||||
char cmdbuf[500];
|
||||
snprintf(cmdbuf, sizeof cmdbuf,
|
||||
"cat /proc/%i/maps | grep %s | grep ' ..x. ' | cut -d- -f1",
|
||||
getpid(), cs(lib_path));
|
||||
ss_ address_s = exec_get_stdout_without_newline(cmdbuf);
|
||||
//log_v(MODULE, "address_s=\"%s\"", cs(address_s));
|
||||
if(address_s.empty())
|
||||
return (void*)0x0;
|
||||
void *address = (void*)strtoul(address_s.c_str(), NULL, 16);
|
||||
return address;
|
||||
}
|
||||
|
||||
//#define __USE_GNU
|
||||
#include <ucontext.h> // REG_EIP (or REG_RIP)
|
||||
|
||||
#if __WORDSIZE == 64 // REG_RIP replaces REG_EIP on x86_64
|
||||
#define REG_EIP REG_RIP
|
||||
#endif
|
||||
|
||||
static void debug_sighandler(int sig, siginfo_t *info, void *secret)
|
||||
{
|
||||
ucontext_t *uc = (ucontext_t*)secret;
|
||||
log_i(MODULE, " ");
|
||||
if(sig == SIGSEGV)
|
||||
log_w(MODULE, "Crash: SIGSEGV: Address %p (executing %p)",
|
||||
info->si_addr, (void*)uc->uc_mcontext.gregs[REG_EIP]);
|
||||
else
|
||||
log_w(MODULE, "Crash: Signal %d", sig);
|
||||
|
||||
void *trace[16];
|
||||
int trace_size = backtrace(trace, 16);
|
||||
// Overwrite sigaction with caller's address
|
||||
trace[1] = (void*) uc->uc_mcontext.gregs[REG_EIP];
|
||||
char **symbols = backtrace_symbols(trace, trace_size);
|
||||
|
||||
// The first stack frame points to this functiton
|
||||
log_i(MODULE, "Backtrace:");
|
||||
for(int i = 1; i < trace_size; i++){
|
||||
char cmdbuf[500];
|
||||
// Parse symbol to get file name
|
||||
// Example: "../cache/rccpp_build/main.so(+0x14e2c) [0x7f7880d36e2c]"
|
||||
//log_v(MODULE, "symbol: %s", symbols[i]);
|
||||
c55::Strfnd f(symbols[i]);
|
||||
ss_ file_path = f.next("(");
|
||||
//log_v(MODULE, "file_path: %s", cs(file_path));
|
||||
|
||||
void *address = trace[i];
|
||||
|
||||
bool is_shared_lib = (file_path.find(".so") != ss_::npos);
|
||||
//log_v(MODULE, "is_shared_lib=%s", is_shared_lib?"true":"false");
|
||||
if(is_shared_lib){
|
||||
void *base_addr = get_library_executable_address(file_path);
|
||||
//log_v(MODULE, "base_addr=%p", base_addr);
|
||||
address = (void*)((char*)trace[i] - (char*)base_addr);
|
||||
//log_v(MODULE, "address=%p", address);
|
||||
}
|
||||
|
||||
snprintf(cmdbuf, sizeof cmdbuf, "echo '%s' | c++filt", symbols[i]);
|
||||
ss_ cppfilt_symbol = exec_get_stdout_without_newline(cmdbuf);
|
||||
|
||||
snprintf(cmdbuf, sizeof cmdbuf, "addr2line %p -e %s",
|
||||
address, cs(file_path));
|
||||
ss_ addr2line_output = exec_get_stdout_without_newline(cmdbuf);
|
||||
|
||||
if(addr2line_output.size() > 4){
|
||||
log_i(MODULE, "#%i %s", i-1, cs(addr2line_output));
|
||||
log_v(MODULE, " = %s", cs(cppfilt_symbol));
|
||||
} else {
|
||||
log_i(MODULE, "#%i %s", i-1, cs(cppfilt_symbol));
|
||||
}
|
||||
}
|
||||
exit(1);
|
||||
}
|
||||
|
||||
void init_signal_handlers(const SigConfig &config)
|
||||
{
|
||||
struct sigaction sa;
|
||||
sa.sa_sigaction = debug_sighandler;
|
||||
sigemptyset (&sa.sa_mask);
|
||||
sa.sa_flags = SA_RESTART | SA_SIGINFO;
|
||||
|
||||
if(config.catch_segfault)
|
||||
sigaction(SIGSEGV, &sa, NULL);
|
||||
if(config.catch_abort)
|
||||
sigaction(SIGABRT, &sa, NULL);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
// vim: set noet ts=4 sw=4:
|
17
src/impl/windows/debug.cpp
Normal file
17
src/impl/windows/debug.cpp
Normal file
@ -0,0 +1,17 @@
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
// Copyright 2014 Perttu Ahola <celeron55@gmail.com>
|
||||
#include "interface/debug.h"
|
||||
#include "core/log.h"
|
||||
#define MODULE "debug"
|
||||
|
||||
namespace interface {
|
||||
namespace debug {
|
||||
|
||||
void init_signal_handlers(const SigConfig &config)
|
||||
{
|
||||
// No-op
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
// vim: set noet ts=4 sw=4:
|
18
src/interface/debug.h
Normal file
18
src/interface/debug.h
Normal file
@ -0,0 +1,18 @@
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
// Copyright 2014 Perttu Ahola <celeron55@gmail.com>
|
||||
#pragma once
|
||||
#include "core/types.h"
|
||||
|
||||
namespace interface
|
||||
{
|
||||
namespace debug
|
||||
{
|
||||
struct SigConfig {
|
||||
bool catch_segfault = true;
|
||||
bool catch_abort = true;
|
||||
};
|
||||
|
||||
void init_signal_handlers(const SigConfig &config);
|
||||
}
|
||||
}
|
||||
// vim: set noet ts=4 sw=4:
|
@ -5,6 +5,7 @@
|
||||
#include "server/config.h"
|
||||
#include "server/state.h"
|
||||
#include "interface/server.h"
|
||||
#include "interface/debug.h"
|
||||
#include <c55/getopt.h>
|
||||
#include <c55/os.h>
|
||||
#ifdef _WIN32
|
||||
@ -57,6 +58,9 @@ void basic_init()
|
||||
|
||||
log_init();
|
||||
log_set_max_level(LOG_VERBOSE);
|
||||
|
||||
interface::debug::SigConfig debug_sig_config;
|
||||
interface::debug::init_signal_handlers(debug_sig_config);
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
|
Loading…
x
Reference in New Issue
Block a user