// http://www.apache.org/licenses/LICENSE-2.0 // Copyright 2014 Perttu Ahola #include "interface/debug.h" #include "core/log.h" #include #include #include #include #include #include #include #include #include #include #include #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 // REG_EIP (or REG_RIP) #if __WORDSIZE == 64 // REG_RIP replaces REG_EIP on x86_64 #define REG_EIP REG_RIP #endif static void log_backtrace(void* const *trace, int trace_size, const ss_ &title); 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]; log_backtrace(trace, trace_size, "Backtrace for signal:"); 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); } static std::atomic_int log_backtrace_spinlock(0); static char backtrace_buffer[10000]; static size_t backtrace_buffer_len = 0; static void bt_print(const char *fmt, ...) { va_list va_args; va_start(va_args, fmt); backtrace_buffer_len += vsnprintf( backtrace_buffer + backtrace_buffer_len, sizeof backtrace_buffer - backtrace_buffer_len, fmt, va_args); va_end(va_args); backtrace_buffer_len += snprintf( backtrace_buffer + backtrace_buffer_len, sizeof backtrace_buffer - backtrace_buffer_len, "\n"); } static void log_backtrace(void* const *trace, int trace_size, const ss_ &title) { char **symbols = backtrace_symbols(trace, trace_size); // Lock spinlock for(;;){ int previous_value = log_backtrace_spinlock.fetch_add(1); if(previous_value == 0) break; log_backtrace_spinlock--; } // The first stack frame points to this functiton backtrace_buffer_len = 0; bt_print("\n %s", cs(title)); int first_real_i = 1; 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); // Clean up the beginning of the backtrace (for whatever reason there // often seems to be two basic_string-related lines at the beginnong of // the backtrace) if(i <= 2 && i <= first_real_i && addr2line_output.find("/basic_string.h") != ss_::npos){ first_real_i = i + 1; continue; } if(addr2line_output.size() > 4){ bt_print(" #%i %s", i-first_real_i, cs(addr2line_output)); //bt_print(" = %s", cs(cppfilt_symbol)); } else { bt_print(" #%i %s", i-first_real_i, cs(cppfilt_symbol)); } } // Print to log backtrace_buffer[sizeof backtrace_buffer - 1] = 0; log_i(MODULE, "%s", backtrace_buffer); // Unlock spinlock log_backtrace_spinlock--; } void log_current_backtrace(const ss_ &title) { void *trace[16]; int trace_size = backtrace(trace, 16); log_backtrace(trace, trace_size, title); } #include #include static void *last_exception_frames[16]; static int last_exception_num_frames = 0; static ss_ last_exception_name; // GCC-specific static ss_ demangle(const char *name) { int status; std::unique_ptr realname(abi::__cxa_demangle(name, 0, 0, &status), &std::free); return status ? "failed" : &*realname; } // GCC-specific extern "C" { void __cxa_throw(void *ex, void *info, void (*dest)(void *)) { last_exception_name = demangle( reinterpret_cast(info)->name()); last_exception_num_frames = backtrace(last_exception_frames, sizeof last_exception_frames / sizeof(void*)); static void (*const rethrow)(void*,void*,void(*)(void*)) __attribute__((noreturn)) = (void (*)(void*,void*,void(*)(void*))) dlsym(RTLD_NEXT, "__cxa_throw"); rethrow(ex,info,dest); } } void log_exception_backtrace(const ss_ &title) { log_backtrace(last_exception_frames, last_exception_num_frames, title); } void get_current_backtrace(StoredBacktrace &result) { result.exception_name.clear(); result.num_frames = backtrace(result.frames, 16); } void get_exception_backtrace(StoredBacktrace &result) { result.exception_name = last_exception_name; result.num_frames = last_exception_num_frames; for(int i = 0; i < result.num_frames; i++){ result.frames[i] = last_exception_frames[i]; } } void log_backtrace(const StoredBacktrace &result, const ss_ &title) { log_backtrace(result.frames, result.num_frames, title); } } } // vim: set noet ts=4 sw=4: