Skip to content

Instantly share code, notes, and snippets.

@astarasikov
Created September 3, 2014 22:20
Show Gist options
  • Save astarasikov/f80a0c1ea71d58fade2e to your computer and use it in GitHub Desktop.
Save astarasikov/f80a0c1ea71d58fade2e to your computer and use it in GitHub Desktop.
Backtrace in C for Linux/OS X
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#include <errno.h>
#include <unwind.h>
#include <execinfo.h>
struct UnwindClosure {
void **array;
void *last;
int count_so_far;
int size;
};
static _Unwind_Reason_Code
BacktraceHelper(struct _Unwind_Context *ctx, void *a)
{
struct UnwindClosure *arg = a;
/* skip self */
if (arg->count_so_far == -1) {
arg->count_so_far = 0;
return _URC_NO_REASON;
}
void *curr = (void *)_Unwind_GetIP(ctx);
void *last = arg->last;
arg->last = curr;
if (curr == last) {
return _URC_NO_REASON;
}
arg->array[arg->count_so_far] = curr;
if (++arg->count_so_far == arg->size) {
return _URC_END_OF_STACK;
}
return _URC_NO_REASON;
}
static int
GetBacktraceEntries(void **buffer, size_t nptrs)
{
struct UnwindClosure arg = {
.array = buffer,
.size = nptrs,
.count_so_far = -1,
};
if (nptrs > 0) {
_Unwind_Backtrace(BacktraceHelper, &arg);
}
if (arg.count_so_far > 1 && arg.array[arg.count_so_far - 1] == NULL) {
arg.count_so_far--;
}
return arg.count_so_far;
}
enum {
MAX_ENTRIES = 256,
};
#define DIE(fmt, ...) do { \
fprintf(stderr, fmt " ; error '%s'\n", ##__VA_ARGS__, strerror(errno)); \
exit(EXIT_FAILURE); \
} while (0)
static void
initSignalStacks(void)
{
stack_t alt_stack = {};
alt_stack.ss_sp = malloc(SIGSTKSZ);
alt_stack.ss_size = SIGSTKSZ;
if (!alt_stack.ss_sp) {
DIE("Failed to allocate memory for signal stack");
} else if (sigaltstack(&alt_stack, NULL) < 0) {
DIE("Failed to set alternative stack for signals");
}
}
static void handle_sigsegv(int signo, siginfo_t *si, void *data)
{
void *buffer [MAX_ENTRIES];
char **bt_strings = NULL;
int n_entries = GetBacktraceEntries(buffer, MAX_ENTRIES);
int i;
if (n_entries < 1) {
DIE("Failed to get backtrace");
}
bt_strings = backtrace_symbols(buffer, n_entries);
for (i = 0; i < n_entries; i++) {
if (bt_strings) {
fprintf(stderr, "BT: %s (%16p)\n", bt_strings[i], buffer[i]);
}
else {
fprintf(stderr, "BT: %16p\n", buffer[i]);
}
}
DIE("in SIGSEGV");
}
static void setupSigsegvHandler(void)
{
struct sigaction sa = {};
sa.sa_flags = SA_SIGINFO | SA_ONSTACK;
sa.sa_sigaction = handle_sigsegv;
if (sigemptyset(&sa.sa_mask)) {
DIE("sigemptyset");
}
if (sigaction(SIGSEGV, &sa, NULL)) {
DIE("sigaction");
}
}
static int crashRecursively(volatile int *a)
{
if (*a) {
crashRecursively(a);
}
return *a;
}
int
main(int argc, char **argv)
{
initSignalStacks();
setupSigsegvHandler();
if (argc > 1) {
volatile int a = 42;
puts("crashing via unbounded recursion");
(void)crashRecursively(&a);
}
else {
puts("crashing via an invalid pointer access");
volatile int *a = (void*)42;
*a = 42;
}
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment