backtrace.c 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185
  1. /*
  2. * Copyright (c) 2006-2022, RT-Thread Development Team
  3. *
  4. * SPDX-License-Identifier: Apache-2.0
  5. *
  6. * Change Logs:
  7. * Date Author Notes
  8. * 2022-06-02 Jesven the first version
  9. * 2023-06-24 WangXiaoyao Support backtrace for non-active thread
  10. */
  11. #include "mm_aspace.h"
  12. #include "mmu.h"
  13. #include <rtthread.h>
  14. #include <backtrace.h>
  15. #include <stdlib.h>
  16. #define BT_NESTING_MAX 100
  17. static int unwind_frame(struct bt_frame *frame)
  18. {
  19. unsigned long fp = frame->fp;
  20. if ((fp & 0x7)
  21. #ifdef RT_USING_LWP
  22. || (rt_kmem_v2p((void *)fp) == ARCH_MAP_FAILED)
  23. #endif
  24. )
  25. {
  26. return 1;
  27. }
  28. frame->fp = *(unsigned long *)fp;
  29. frame->pc = *(unsigned long *)(fp + 8);
  30. if ((rt_kmem_v2p((void *)frame->pc) == ARCH_MAP_FAILED))
  31. return 1;
  32. return 0;
  33. }
  34. static void walk_unwind(unsigned long pc, unsigned long fp)
  35. {
  36. struct bt_frame frame = {fp, 1};
  37. unsigned long lr = pc;
  38. int nesting = 0;
  39. while (nesting < BT_NESTING_MAX)
  40. {
  41. rt_kprintf(" %p", (void *)lr);
  42. if (unwind_frame(&frame))
  43. {
  44. break;
  45. }
  46. lr = frame.pc;
  47. nesting++;
  48. }
  49. }
  50. void backtrace(unsigned long pc, unsigned long lr, unsigned long fp)
  51. {
  52. rt_kprintf("please use: addr2line -e rtthread.elf -a -f");
  53. if (pc)
  54. rt_kprintf(" %p", (void *)pc);
  55. if (lr && fp)
  56. walk_unwind(lr, fp);
  57. rt_kprintf("\n");
  58. }
  59. int rt_backtrace(void)
  60. {
  61. unsigned long ra = (unsigned long)__builtin_return_address(0U);
  62. unsigned long fr = (unsigned long)__builtin_frame_address(0U);
  63. backtrace(0, ra, fr);
  64. return 0;
  65. }
  66. MSH_CMD_EXPORT_ALIAS(rt_backtrace, bt_test, backtrace test);
  67. #define ARCH_CONTEXT_FETCH(pctx, id) (*(((unsigned long *)pctx) + (id)))
  68. int rt_backtrace_thread(rt_thread_t thread)
  69. {
  70. unsigned long lr;
  71. unsigned long fp;
  72. if (thread == rt_thread_self())
  73. {
  74. return -RT_EINVAL;
  75. }
  76. else
  77. {
  78. lr = ARCH_CONTEXT_FETCH(thread->sp, 3);
  79. fp = ARCH_CONTEXT_FETCH(thread->sp, 7);
  80. backtrace(0, lr, fp);
  81. return 0;
  82. }
  83. }
  84. #ifdef RT_USING_SMART
  85. int rt_backtrace_user_thread(rt_thread_t thread)
  86. {
  87. unsigned long pc;
  88. unsigned long lr;
  89. unsigned long fp;
  90. unsigned long ctx = (unsigned long)thread->user_ctx.ctx;
  91. if (ctx > (unsigned long)thread->stack_addr
  92. && ctx < (unsigned long)thread->stack_addr + thread->stack_size)
  93. {
  94. pc = ARCH_CONTEXT_FETCH(thread->user_ctx.ctx, 0);
  95. lr = ARCH_CONTEXT_FETCH(thread->user_ctx.ctx, 3);
  96. fp = ARCH_CONTEXT_FETCH(thread->user_ctx.ctx, 7);
  97. backtrace(pc, lr, fp);
  98. return 0;
  99. }
  100. else
  101. return -1;
  102. }
  103. #endif /* RT_USING_SMART */
  104. static long custom_hex_to_long(const char* hex)
  105. {
  106. long result = 0;
  107. int i = 0;
  108. // Skip the "0x" prefix
  109. if (hex[0] == '0' && (hex[1] == 'x' || hex[1] == 'X'))
  110. {
  111. i = 2;
  112. }
  113. // Convert each hex digit to its decimal value
  114. for (; hex[i] != '\0'; i++)
  115. {
  116. char digit = hex[i];
  117. if (digit >= '0' && digit <= '9')
  118. {
  119. result = result * 16 + (digit - '0');
  120. }
  121. else if (digit >= 'a' && digit <= 'f')
  122. {
  123. result = result * 16 + (digit - 'a' + 10);
  124. }
  125. else if (digit >= 'A' && digit <= 'F')
  126. {
  127. result = result * 16 + (digit - 'A' + 10);
  128. }
  129. else
  130. {
  131. // Invalid hex digit
  132. return 0;
  133. }
  134. }
  135. return result;
  136. }
  137. static void cmd_backtrace(int argc, char** argv)
  138. {
  139. long pid;
  140. if (argc < 2)
  141. {
  142. rt_kprintf("please use: backtrace pid\n");
  143. return;
  144. }
  145. if (strncmp(argv[1], "0x", 2) == 0)
  146. {
  147. pid = custom_hex_to_long(argv[1]);
  148. }
  149. else
  150. {
  151. pid = atol(argv[1]);
  152. }
  153. if (pid)
  154. {
  155. rt_kprintf("backtrace %s(0x%lx), from %s\n", ((rt_thread_t)pid)->parent.name, pid, argv[1]);
  156. rt_backtrace_thread((rt_thread_t)pid);
  157. }
  158. }
  159. MSH_CMD_EXPORT_ALIAS(cmd_backtrace, backtrace, print backtrace of a thread);