impl/linux/debug: Attempt to not hang or crash when printing signal backtrace
This commit is contained in:
parent
bd4219d7bd
commit
c77343cc1a
@ -14,6 +14,7 @@ namespace magic = Urho3D;
|
||||
|
||||
client::Config g_client_config;
|
||||
|
||||
// TODO: This isn't thread-safe
|
||||
bool g_sigint_received = false;
|
||||
void sigint_handler(int sig)
|
||||
{
|
||||
|
@ -33,6 +33,7 @@ static const bool use_colors = true;
|
||||
static interface::Mutex log_mutex;
|
||||
//static interface::Thread *log_active_thread = nullptr;
|
||||
|
||||
static std::atomic_bool disable_bloat(false);
|
||||
static std::atomic_bool line_begin(true);
|
||||
static std::atomic_int current_level(0);
|
||||
static std::atomic_int max_level(CORE_INFO);
|
||||
@ -74,6 +75,11 @@ void log_close()
|
||||
log_mutex.unlock();
|
||||
}
|
||||
|
||||
void log_disable_bloat()
|
||||
{
|
||||
disable_bloat = true;
|
||||
}
|
||||
|
||||
void log_nl_nolock()
|
||||
{
|
||||
if(current_level <= max_level){
|
||||
@ -126,13 +132,17 @@ static void print(int level, const char *sys, const char *fmt, va_list va_args)
|
||||
if(line_begin){
|
||||
time_t now = time(NULL);
|
||||
char timestr[30];
|
||||
size_t timestr_len = strftime(timestr, sizeof(timestr),
|
||||
"%b %d %H:%M:%S", localtime(&now));
|
||||
if(timestr_len == 0)
|
||||
timestr[0] = '\0';
|
||||
int ms = (get_timeofday_us() % 1000000) / 1000;
|
||||
timestr_len += snprintf(timestr + timestr_len,
|
||||
sizeof(timestr) - timestr_len, ".%03i", ms);
|
||||
if(disable_bloat){
|
||||
timestr[0] = 0;
|
||||
} else {
|
||||
size_t timestr_len = strftime(timestr, sizeof(timestr),
|
||||
"%b %d %H:%M:%S", localtime(&now));
|
||||
if(timestr_len == 0)
|
||||
timestr[0] = '\0';
|
||||
int ms = (get_timeofday_us() % 1000000) / 1000;
|
||||
timestr_len += snprintf(timestr + timestr_len,
|
||||
sizeof(timestr) - timestr_len, ".%03i", ms);
|
||||
}
|
||||
char sysstr[9];
|
||||
snprintf(sysstr, 9, "%s ", sys);
|
||||
const char *levelcs = "FEWIVDT";
|
||||
|
@ -18,6 +18,11 @@ int log_get_max_level();
|
||||
void log_set_file(const char *path);
|
||||
void log_close();
|
||||
|
||||
// Try to stop using malloc() and other heavyweight interfaces. Call when
|
||||
// SIGSEGV or SIGABRT occurs to make the program much more likely to be able to
|
||||
// print out the necessary errors.
|
||||
void log_disable_bloat();
|
||||
|
||||
void log_nl();
|
||||
void log_(int level, const char *sys, const char *fmt, ...)
|
||||
__attribute__((format(printf, 3, 4)));
|
||||
|
@ -58,7 +58,7 @@ static void* get_library_executable_address(const ss_ &lib_path)
|
||||
//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);
|
||||
void *address = (void*)strtoul(address_s.c_str(), NULL, BACKTRACE_SIZE);
|
||||
return address;
|
||||
}
|
||||
|
||||
@ -141,9 +141,12 @@ static void bt_print_backtrace(char **symbols, void* const *trace, int trace_siz
|
||||
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);
|
||||
ss_ addr2line_output;
|
||||
if(address != nullptr){
|
||||
snprintf(cmdbuf, sizeof cmdbuf, "addr2line %p -e %s",
|
||||
address, cs(file_path));
|
||||
addr2line_output = exec_get_stdout_without_newline(cmdbuf);
|
||||
}
|
||||
|
||||
auto r = filter_f(i, first_real_i, cppfilt_symbol,addr2line_output);
|
||||
if(r == BFA_PASS){
|
||||
@ -191,12 +194,14 @@ static void log_backtrace(void* const *trace, int trace_size, const ss_ &title,
|
||||
// Unlock spinlock
|
||||
backtrace_mutex.unlock();
|
||||
}
|
||||
|
||||
free(symbols);
|
||||
}
|
||||
|
||||
void log_current_backtrace(const ss_ &title)
|
||||
{
|
||||
void *trace[16];
|
||||
int trace_size = backtrace(trace, 16);
|
||||
void *trace[BACKTRACE_SIZE];
|
||||
int trace_size = backtrace(trace, BACKTRACE_SIZE);
|
||||
|
||||
log_backtrace(trace, trace_size, title);
|
||||
}
|
||||
@ -204,7 +209,7 @@ void log_current_backtrace(const ss_ &title)
|
||||
#include <cxxabi.h>
|
||||
#include <dlfcn.h>
|
||||
|
||||
static void *last_exception_frames[16];
|
||||
static void *last_exception_frames[BACKTRACE_SIZE];
|
||||
static int last_exception_num_frames = 0;
|
||||
static ss_ last_exception_name;
|
||||
|
||||
@ -240,7 +245,7 @@ void log_exception_backtrace(const ss_ &title)
|
||||
void get_current_backtrace(StoredBacktrace &result)
|
||||
{
|
||||
result.exception_name.clear();
|
||||
result.num_frames = backtrace(result.frames, 16);
|
||||
result.num_frames = backtrace(result.frames, BACKTRACE_SIZE);
|
||||
}
|
||||
|
||||
void get_exception_backtrace(StoredBacktrace &result)
|
||||
@ -304,29 +309,95 @@ void log_backtrace_chain(const std::list<ThreadBacktrace> &chain,
|
||||
bt_print_backtrace(symbols, bt_step.bt.frames, bt_step.bt.num_frames,
|
||||
1, bt_filter);
|
||||
bt_print_newline();
|
||||
|
||||
free(symbols);
|
||||
}
|
||||
// Print to log
|
||||
backtrace_buffer[sizeof backtrace_buffer - 1] = 0;
|
||||
log_i(MODULE, "%s", backtrace_buffer);
|
||||
}
|
||||
|
||||
// Used for signals because a signal often occurs inside core/log's mutex
|
||||
// synchronization, causing a deadlock. Also, does not allocate new memory.
|
||||
static void stderr_backtrace(void* const *trace, int trace_size, int sig)
|
||||
{
|
||||
char **symbols = backtrace_symbols(trace, trace_size);
|
||||
|
||||
backtrace_mutex.lock();
|
||||
|
||||
backtrace_buffer_len = 0;
|
||||
// The first stack frame points to this functiton
|
||||
bt_print_backtrace(symbols, trace, trace_size, 1);
|
||||
|
||||
// Print to stderr
|
||||
backtrace_buffer[sizeof backtrace_buffer - 1] = 0;
|
||||
fprintf(stderr, " Backtrace for signal %i:\n%s\n", sig, backtrace_buffer);
|
||||
|
||||
backtrace_mutex.unlock();
|
||||
|
||||
free(symbols);
|
||||
}
|
||||
|
||||
// Used for avoiding the handing of a SIGSEGV caused by trying to handle a
|
||||
// SIGABRT or so.
|
||||
static std::atomic_int g_signal_handlers_active(0);
|
||||
|
||||
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);
|
||||
int num_handlers_active = g_signal_handlers_active.fetch_add(1);
|
||||
if(num_handlers_active != 0){
|
||||
// Get out of here, we're in deep trouble
|
||||
g_signal_handlers_active--;
|
||||
exit(1);
|
||||
}
|
||||
|
||||
void *trace[16];
|
||||
int trace_size = backtrace(trace, 16);
|
||||
log_disable_bloat(); // First get this out of the way
|
||||
|
||||
// NOTE: Do not use log in here, because a signal can occur inside the
|
||||
// logging functions too
|
||||
|
||||
ucontext_t *uc = (ucontext_t*)secret;
|
||||
bool malloc_might_not_work = false;
|
||||
fprintf(stderr, "\n");
|
||||
if(sig == SIGSEGV){
|
||||
fprintf(stderr, "Crash: SIGSEGV: Address %p (executing %p)\n",
|
||||
info->si_addr, (void*)uc->uc_mcontext.gregs[REG_EIP]);
|
||||
// If segfault isn't a nullptr dereference, do not fetch symbols; it
|
||||
// would just cause a deadlock in glibc (due to malloc mutex)
|
||||
if(info->si_addr != nullptr)
|
||||
malloc_might_not_work = true;
|
||||
} else if(sig == SIGABRT){
|
||||
fprintf(stderr, "Crash: SIGABRT\n");
|
||||
// glibc SIGABRT is a can of worms
|
||||
malloc_might_not_work = true;
|
||||
} else {
|
||||
fprintf(stderr, "Crash: Signal %d\n", sig);
|
||||
}
|
||||
|
||||
void *trace[BACKTRACE_SIZE];
|
||||
int trace_size = backtrace(trace, BACKTRACE_SIZE);
|
||||
// Overwrite sigaction with caller's address
|
||||
trace[1] = (void*) uc->uc_mcontext.gregs[REG_EIP];
|
||||
|
||||
log_backtrace(trace, trace_size, "Backtrace for signal:");
|
||||
if(malloc_might_not_work){
|
||||
// Can't use backtrace_symbols() due to whatever situation we're in.
|
||||
// Just print the symbols directly to stderr (fd=2).
|
||||
backtrace_symbols_fd(trace, trace_size, 2);
|
||||
fprintf(stderr, "\n");
|
||||
fprintf(stderr, "Will not attempt to print the backtrace with symbols.\n");
|
||||
fprintf(stderr, "\n");
|
||||
/*fprintf(stderr, "\n");
|
||||
fprintf(stderr, "Attempting to print the backtrace with symbols - if it"
|
||||
" does not work, you will see some kind of a crash instead:\n");
|
||||
fprintf(stderr, "Backtrace:\n");
|
||||
stderr_backtrace(trace, trace_size, sig);*/
|
||||
} else {
|
||||
stderr_backtrace(trace, trace_size, sig);
|
||||
}
|
||||
|
||||
g_signal_handlers_active--;
|
||||
// In case of SIGSEGV or SIGABRT, all is fucked and exit() can deadlock due
|
||||
// to not being able to use malloc(), but whatever...
|
||||
exit(1);
|
||||
}
|
||||
|
||||
|
@ -11,8 +11,10 @@ namespace interface
|
||||
void log_current_backtrace(const ss_ &title="Current backtrace:");
|
||||
void log_exception_backtrace(const ss_ &title="Exception backtrace:");
|
||||
|
||||
static const size_t BACKTRACE_SIZE = 48;
|
||||
|
||||
struct StoredBacktrace {
|
||||
void *frames[16];
|
||||
void *frames[BACKTRACE_SIZE];
|
||||
int num_frames = 0;
|
||||
ss_ exception_name;
|
||||
};
|
||||
|
Loading…
x
Reference in New Issue
Block a user