diff options
author | Khem Raj <raj.khem@gmail.com> | 2012-01-21 05:59:49 -0800 |
---|---|---|
committer | Khem Raj <raj.khem@gmail.com> | 2012-01-23 16:10:39 -0800 |
commit | a0d7a4587584b1c217183d7f17a2d55ab44aaf3a (patch) | |
tree | eb501879d7ba3c8a3c65e2db8604ea587ed811c5 /libubacktrace/arm/backtrace.c | |
parent | bee3ea0f5a8d2cc2cfaeebdc4ef65f2d6b30b749 (diff) |
libubacktrace: Add arm implementation
Arm has a different mechanism of getting
_Unwind_GetIP. Therefore we provide arch
specific backtrace file.
Signed-off-by: Khem Raj <raj.khem@gmail.com>
Diffstat (limited to 'libubacktrace/arm/backtrace.c')
-rw-r--r-- | libubacktrace/arm/backtrace.c | 90 |
1 files changed, 90 insertions, 0 deletions
diff --git a/libubacktrace/arm/backtrace.c b/libubacktrace/arm/backtrace.c new file mode 100644 index 000000000..d4eca3224 --- /dev/null +++ b/libubacktrace/arm/backtrace.c @@ -0,0 +1,90 @@ +/* + * Perform stack unwinding by using the _Unwind_Backtrace. + * + * User application that wants to use backtrace needs to be + * compiled with -fasynchronous-unwid-tables option and -rdynamic i + * to get full symbols printed. + * + * Author(s): Khem Raj <raj.khem@gmail.com> + * - ARM specific implementation of backtrace + * + * Licensed under the LGPL v2.1, see the file COPYING.LIB in this tarball. + * + */ + +#include <execinfo.h> +#include <dlfcn.h> +#include <stdlib.h> +#include <unwind.h> +#include <assert.h> +#include <stdio.h> + +struct trace_arg +{ + void **array; + int cnt, size; +}; + +static _Unwind_Reason_Code (*unwind_backtrace) (_Unwind_Trace_Fn, void *); +static _Unwind_VRS_Result (*unwind_vrs_get) (_Unwind_Context *, + _Unwind_VRS_RegClass, + _uw, + _Unwind_VRS_DataRepresentation, + void *); + +static void backtrace_init (void) +{ + void *handle = dlopen ("libgcc_s.so.1", RTLD_LAZY); + if (handle == NULL + || ((unwind_backtrace = dlsym (handle, "_Unwind_Backtrace")) == NULL) + || ((unwind_vrs_get = dlsym (handle, "_Unwind_VRS_Get")) == NULL)) { + printf("libgcc_s.so.1 must be installed for backtrace to work\n"); + abort(); + } +} +/* This function is identical to "_Unwind_GetGR", except that it uses + "unwind_vrs_get" instead of "_Unwind_VRS_Get". */ +static inline _Unwind_Word +unwind_getgr (_Unwind_Context *context, int regno) +{ + _uw val; + unwind_vrs_get (context, _UVRSC_CORE, regno, _UVRSD_UINT32, &val); + return val; +} + +/* This macro is identical to the _Unwind_GetIP macro, except that it + uses "unwind_getgr" instead of "_Unwind_GetGR". */ +#define unwind_getip(context) \ + (unwind_getgr (context, 15) & ~(_Unwind_Word)1) + +static _Unwind_Reason_Code +backtrace_helper (struct _Unwind_Context *ctx, void *a) +{ + struct trace_arg *arg = a; + + assert (unwind_getip != NULL); + + /* We are first called with address in the __backtrace function. Skip it. */ + if (arg->cnt != -1) + arg->array[arg->cnt] = (void *) unwind_getip (ctx); + if (++arg->cnt == arg->size) + return _URC_END_OF_STACK; + return _URC_NO_REASON; +} + +/* + * Perform stack unwinding by using the _Unwind_Backtrace. + * + */ +int backtrace (void **array, int size) +{ + struct trace_arg arg = { .array = array, .size = size, .cnt = -1 }; + + if (unwind_backtrace == NULL) + backtrace_init(); + + if (size >= 1) + unwind_backtrace (backtrace_helper, &arg); + + return arg.cnt != -1 ? arg.cnt : 0; +} |