|
|
|
|
|
#define UNW_LOCAL_ONLY |
|
#include <libunwind.h> |
|
#include <stdio.h> |
|
#include <assert.h> |
|
#include <signal.h> |
|
#include <stdlib.h> |
|
#include <stdio.h> |
|
#include <memory.h> |
|
|
|
|
|
#include <unwind.h> |
|
#include <execinfo.h> |
|
|
|
static const int SIGNAL_MAX = 64; |
|
|
|
// This array contains the default behavior for each signal. |
|
static struct sigaction sigdfl[SIGNAL_MAX]; |
|
|
|
|
|
// Call this function to get a backtrace. |
|
void unwind(unw_context_t* ctx) { |
|
unw_cursor_t cursor; |
|
unw_context_t context; |
|
|
|
// Initialize cursor to current frame for local unwinding. |
|
if (ctx == 0) |
|
{ |
|
unw_getcontext(&context); |
|
unw_init_local(&cursor, &context); |
|
} else |
|
{ |
|
unw_init_local(&cursor, ctx); |
|
} |
|
|
|
// Unwind frames one by one, going up the frame stack. |
|
while (unw_step(&cursor) > 0) { |
|
unw_word_t offset, pc; |
|
unw_get_reg(&cursor, UNW_REG_IP, &pc); |
|
if (pc == 0) { |
|
break; |
|
} |
|
printf("0x%llx:", pc); |
|
|
|
char sym[256]; |
|
if (unw_get_proc_name(&cursor, sym, sizeof(sym), &offset) == 0) { |
|
printf(" (%s+0x%llx)\n", sym, offset); |
|
} else { |
|
printf(" -- error: unable to obtain symbol name for this frame\n"); |
|
} |
|
} |
|
} |
|
|
|
void backtrace_execinfo() |
|
{ |
|
const int maxptrs = 32; |
|
void* ptrs[maxptrs]; |
|
int ptrcount = backtrace(ptrs, maxptrs); |
|
|
|
char** stacktrace = backtrace_symbols(ptrs, ptrcount); |
|
for (uint32_t i = 0; i < ptrcount; ++i) |
|
{ |
|
printf("%p: %s\n", ptrs[i], stacktrace[i]); |
|
} |
|
} |
|
|
|
|
|
static _Unwind_Reason_Code unwind_backtrace_callback(struct _Unwind_Context* context, void* arg) { |
|
|
|
uintptr_t pc = _Unwind_GetIP(context); |
|
if (pc) { |
|
printf("unwind got pc ... %p\n", (void*)pc); |
|
} |
|
|
|
return _URC_NO_REASON; |
|
} |
|
|
|
ssize_t unwind_backtrace() { |
|
_Unwind_Reason_Code rc = _Unwind_Backtrace(unwind_backtrace_callback, 0); |
|
|
|
return rc == _URC_END_OF_STACK ? 0 : -1; |
|
} |
|
|
|
// ************************************************************************************************************************ |
|
// ************************************************************************************************************************ |
|
// ************************************************************************************************************************ |
|
|
|
void do_backtrace(void* ctx) |
|
{ |
|
if (getenv("TEST_UNW") != 0) |
|
unwind(ctx); |
|
if (getenv("TEST_UNW_BACKTRACE") != 0) |
|
unwind_backtrace(); |
|
if (getenv("TEST_BACKTRACE") != 0) |
|
backtrace_execinfo(); |
|
} |
|
|
|
void foo() { |
|
if (getenv("TEST_ABORT") != 0) |
|
{ |
|
printf("ABORT\n"); |
|
assert(0 && "Make some noise!"); |
|
} |
|
|
|
if (getenv("TEST_SEGV") != 0) |
|
{ |
|
printf("SEGV\n"); |
|
int* i = 0; |
|
*i = 18; |
|
} |
|
|
|
printf("BACKTRACE\n"); |
|
do_backtrace(0); |
|
} |
|
|
|
void bar() { |
|
foo(); |
|
} |
|
|
|
static void Handler(const int signum, siginfo_t *const si, void *const sc) |
|
{ |
|
// The previous (default) behavior is restored for the signal. |
|
// Unless this is done first thing in the signal handler we'll |
|
// be stuck in a signal-handler loop forever. |
|
|
|
ucontext_t* ctx = 0;//(ucontext_t*)sc; |
|
|
|
sigaction(signum, &sigdfl[signum], NULL); |
|
do_backtrace(ctx); |
|
} |
|
|
|
void InstallOnSignal(int signum) |
|
{ |
|
//assert(signum >= 0 && signum < SIGNAL_MAX); |
|
|
|
struct sigaction sa; |
|
memset(&sa, 0, sizeof(sa)); |
|
sigemptyset(&sa.sa_mask); |
|
sa.sa_sigaction = Handler; |
|
sa.sa_flags = SA_SIGINFO; |
|
|
|
// The current (default) behavior is stored in sigdfl. |
|
sigaction(signum, &sa, &sigdfl[signum]); |
|
} |
|
|
|
void InstallHandler() |
|
{ |
|
InstallOnSignal(SIGSEGV); |
|
InstallOnSignal(SIGBUS); |
|
InstallOnSignal(SIGTRAP); |
|
InstallOnSignal(SIGILL); |
|
InstallOnSignal(SIGABRT); |
|
} |
|
|
|
int main(int argc, char **argv) { |
|
InstallHandler(); |
|
|
|
bar(); |
|
|
|
return 0; |
|
} |