diff options
Diffstat (limited to 'save_backtrace.c')
-rw-r--r-- | save_backtrace.c | 61 |
1 files changed, 61 insertions, 0 deletions
diff --git a/save_backtrace.c b/save_backtrace.c new file mode 100644 index 0000000..e366abf --- /dev/null +++ b/save_backtrace.c @@ -0,0 +1,61 @@ +/* See LICENSE file for copyright and license details. */ +#include "internal.h" + +#define UNW_LOCAL_ONLY +#include <libunwind.h> + + +struct partial { + uintptr_t rips[8]; + struct partial *next; +}; + + +int +liberror_save_backtrace(struct liberror_error *error) +{ + struct liberror_backtrace *backtrace = NULL; + unw_word_t rip; + unw_cursor_t cursor; + unw_context_t context; + size_t i = 0; + struct partial *current, head; + int saved_errno = errno; + int ret = -1; + + if (unw_getcontext(&context)) + goto out; + if (unw_init_local(&cursor, &context)) + goto out; + + current = &head; + + for (; unw_step(&cursor) > 0; i++) { + if (!(i & 7)) + current = current->next = alloca(sizeof(*current)); + if (unw_get_reg(&cursor, UNW_REG_IP, &rip)) + goto out; + current->rips[i & 7] = (uintptr_t)rip; + } + + backtrace = malloc(offsetof(struct liberror_backtrace, rips) + i * sizeof(*backtrace->rips)); + if (!backtrace) + goto out; + backtrace->refcount = 1; + + backtrace->n = i; + current = &head; + for (i = 0; i < backtrace->n; i++) { + if (!(i & 7)) + current = current->next; + backtrace->rips[i] = current->rips[i & 7]; + } + + ret = 0; +out: + if (error->backtrace && !--error->backtrace->refcount) + free(error->backtrace); + error->backtrace = backtrace; + errno = saved_errno; + return ret; +} |