backtrace.c 2.5 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798
  1. /*
  2. * Perform stack unwinding by using the _Unwind_Backtrace.
  3. *
  4. * User application that wants to use backtrace needs to be
  5. * compiled with -fasynchronous-unwid-tables option and -rdynamic i
  6. * to get full symbols printed.
  7. *
  8. * Author(s): Khem Raj <raj.khem@gmail.com>
  9. * - ARM specific implementation of backtrace
  10. *
  11. * Licensed under the LGPL v2.1, see the file COPYING.LIB in this tarball.
  12. *
  13. */
  14. #include <libgcc_s.h>
  15. #include <execinfo.h>
  16. #include <dlfcn.h>
  17. #include <stdlib.h>
  18. #include <unwind.h>
  19. #include <assert.h>
  20. #include <stdio.h>
  21. struct trace_arg
  22. {
  23. void **array;
  24. int cnt, size;
  25. };
  26. #ifdef SHARED
  27. static _Unwind_Reason_Code (*unwind_backtrace) (_Unwind_Trace_Fn, void *);
  28. static _Unwind_VRS_Result (*unwind_vrs_get) (_Unwind_Context *,
  29. _Unwind_VRS_RegClass,
  30. _uw,
  31. _Unwind_VRS_DataRepresentation,
  32. void *);
  33. static void backtrace_init (void)
  34. {
  35. void *handle = dlopen (LIBGCC_S_SO, RTLD_LAZY);
  36. if (handle == NULL
  37. || ((unwind_backtrace = dlsym (handle, "_Unwind_Backtrace")) == NULL)
  38. || ((unwind_vrs_get = dlsym (handle, "_Unwind_VRS_Get")) == NULL)) {
  39. printf(LIBGCC_S_SO " must be installed for backtrace to work\n");
  40. abort();
  41. }
  42. }
  43. #else
  44. # define unwind_backtrace _Unwind_Backtrace
  45. # define unwind_vrs_get _Unwind_VRS_Get
  46. #endif
  47. /* This function is identical to "_Unwind_GetGR", except that it uses
  48. "unwind_vrs_get" instead of "_Unwind_VRS_Get". */
  49. static inline _Unwind_Word
  50. unwind_getgr (_Unwind_Context *context, int regno)
  51. {
  52. _uw val;
  53. unwind_vrs_get (context, _UVRSC_CORE, regno, _UVRSD_UINT32, &val);
  54. return val;
  55. }
  56. /* This macro is identical to the _Unwind_GetIP macro, except that it
  57. uses "unwind_getgr" instead of "_Unwind_GetGR". */
  58. #define unwind_getip(context) \
  59. (unwind_getgr (context, 15) & ~(_Unwind_Word)1)
  60. static _Unwind_Reason_Code
  61. backtrace_helper (struct _Unwind_Context *ctx, void *a)
  62. {
  63. struct trace_arg *arg = a;
  64. assert (unwind_getip(ctx) != NULL);
  65. /* We are first called with address in the __backtrace function. Skip it. */
  66. if (arg->cnt != -1)
  67. arg->array[arg->cnt] = (void *) unwind_getip (ctx);
  68. if (++arg->cnt == arg->size)
  69. return _URC_END_OF_STACK;
  70. return _URC_NO_REASON;
  71. }
  72. /*
  73. * Perform stack unwinding by using the _Unwind_Backtrace.
  74. *
  75. */
  76. int backtrace (void **array, int size)
  77. {
  78. struct trace_arg arg = { .array = array, .size = size, .cnt = -1 };
  79. #ifdef SHARED
  80. if (unwind_backtrace == NULL)
  81. backtrace_init();
  82. #endif
  83. if (size >= 1)
  84. unwind_backtrace (backtrace_helper, &arg);
  85. return arg.cnt != -1 ? arg.cnt : 0;
  86. }