backtrace.c 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228
  1. /* SPDX-License-Identifier: MIT */
  2. /* Copyright (c) 2013-2024 The SRS Authors */
  3. #ifdef __linux__
  4. #define _GNU_SOURCE
  5. #endif
  6. #include <execinfo.h>
  7. #include <stdio.h>
  8. #include <stdlib.h>
  9. #include <st.h>
  10. #ifdef __linux__
  11. #include <dlfcn.h>
  12. #include <stdio.h>
  13. #include <string.h>
  14. void* parse_symbol_offset(char* frame)
  15. {
  16. char* p = NULL;
  17. char* p_symbol = NULL;
  18. int nn_symbol = 0;
  19. char* p_offset = NULL;
  20. int nn_offset = 0;
  21. // Read symbol and offset, for example:
  22. // /tools/backtrace(foo+0x1820) [0x555555555820]
  23. for (p = frame; *p; p++) {
  24. if (*p == '(') {
  25. p_symbol = p + 1;
  26. } else if (*p == '+') {
  27. if (p_symbol) nn_symbol = p - p_symbol;
  28. p_offset = p + 1;
  29. } else if (*p == ')') {
  30. if (p_offset) nn_offset = p - p_offset;
  31. }
  32. }
  33. if (!nn_symbol && !nn_offset) {
  34. return NULL;
  35. }
  36. // Convert offset(0x1820) to pointer, such as 0x1820.
  37. char tmp[128];
  38. if (!nn_offset || nn_offset >= sizeof(tmp)) {
  39. return NULL;
  40. }
  41. int r0 = EOF;
  42. void* offset = NULL;
  43. tmp[nn_offset] = 0;
  44. if ((r0 = sscanf(strncpy(tmp, p_offset, nn_offset), "%p", &offset)) == EOF) {
  45. return NULL;
  46. }
  47. // Covert symbol(foo) to offset, such as 0x2fba.
  48. if (!nn_symbol || nn_symbol >= sizeof(tmp)) {
  49. return offset;
  50. }
  51. void* object_file;
  52. if ((object_file = dlopen(NULL, RTLD_LAZY)) == NULL) {
  53. return offset;
  54. }
  55. void* address;
  56. tmp[nn_symbol] = 0;
  57. if ((address = dlsym(object_file, strncpy(tmp, p_symbol, nn_symbol))) == NULL) {
  58. dlclose(object_file);
  59. return offset;
  60. }
  61. Dl_info symbol_info;
  62. if ((r0 = dladdr(address, &symbol_info)) == 0) {
  63. dlclose(object_file);
  64. return offset;
  65. }
  66. dlclose(object_file);
  67. return symbol_info.dli_saddr - symbol_info.dli_fbase + offset;
  68. }
  69. char* addr2line_format(void* addr, char* symbol, char* buffer, int nn_buffer)
  70. {
  71. char cmd[512] = {0};
  72. int r0 = snprintf(cmd, sizeof(cmd), "addr2line -C -p -s -f -a -e %s %p", "backtrace", addr - 1);
  73. if (r0 < 0 || r0 >= sizeof(cmd)) return symbol;
  74. FILE* fp = popen(cmd, "r");
  75. if (!fp) return symbol;
  76. char* p = fgets(buffer, nn_buffer, fp);
  77. pclose(fp);
  78. if (p == NULL) return symbol;
  79. if ((r0 = strlen(p)) == 0) return symbol;
  80. // Trait the last newline if exists.
  81. if (p[r0 - 1] == '\n') p[r0 - 1] = '\0';
  82. // Find symbol not match by addr2line, like
  83. // 0x0000000000021c87: ?? ??:0
  84. // 0x0000000000002ffa: _start at ??:?
  85. for (p = buffer; p < buffer + r0 - 1; p++) {
  86. if (p[0] == '?' && p[1] == '?') return symbol;
  87. }
  88. return buffer;
  89. }
  90. #endif
  91. #ifdef __linux__
  92. void bar2()
  93. {
  94. void* addresses[64];
  95. int nn_addresses = backtrace(addresses, sizeof(addresses) / sizeof(void*));
  96. printf("\naddresses:\n");
  97. for (int i = 0; i < nn_addresses; i++) {
  98. printf("%p\n", addresses[i]);
  99. }
  100. char** symbols = backtrace_symbols(addresses, nn_addresses);
  101. printf("\nsymbols:\n");
  102. for (int i = 0; i < nn_addresses; i++) {
  103. printf("%s\n", symbols[i]);
  104. }
  105. char buffer[128];
  106. printf("\nframes:\n");
  107. for (int i = 0; i < nn_addresses; i++) {
  108. void* frame = parse_symbol_offset(symbols[i]);
  109. char* fmt = addr2line_format(frame, symbols[i], buffer, sizeof(buffer));
  110. int parsed = (fmt == buffer);
  111. printf("%p %d %s\n", frame, parsed, fmt);
  112. }
  113. free(symbols);
  114. printf("bar2 OK\n");
  115. return;
  116. }
  117. #endif
  118. int always_use_builtin = 0;
  119. #pragma GCC diagnostic push
  120. #pragma GCC diagnostic ignored "-Wframe-address"
  121. void bar() {
  122. // Each item in the array pointed to by buffer is of type void *, and is the return address from the corresponding
  123. // stack frame.
  124. void* addresses[64];
  125. int nn_addresses = backtrace(addresses, sizeof(addresses) / sizeof(void*));
  126. if (!nn_addresses || always_use_builtin) {
  127. printf("Try to get return addresses by __builtin_return_address\n");
  128. void* p = NULL; nn_addresses = 0;
  129. if ((p = __builtin_return_address(0)) != NULL) {
  130. addresses[nn_addresses++] = p;
  131. if ((p = __builtin_return_address(1)) != NULL) {
  132. addresses[nn_addresses++] = p;
  133. if ((p = __builtin_return_address(2)) != NULL) {
  134. addresses[nn_addresses++] = p;
  135. if ((p = __builtin_return_address(3)) != NULL) {
  136. addresses[nn_addresses++] = p;
  137. if ((p = __builtin_return_address(4)) != NULL) {
  138. addresses[nn_addresses++] = p;
  139. if ((p = __builtin_return_address(5)) != NULL) {
  140. addresses[nn_addresses++] = p;
  141. }
  142. }
  143. }
  144. }
  145. }
  146. }
  147. }
  148. char** symbols = backtrace_symbols(addresses, nn_addresses);
  149. printf("nn_addresses=%d, symbols=%p, symbols[0]=%p\n", nn_addresses, symbols, symbols[0]);
  150. printf("\naddresses:\n");
  151. for (int i = 0; i < nn_addresses; i++) {
  152. printf("%p\n", addresses[i]);
  153. }
  154. printf("\nsymbols:\n");
  155. for (int i = 0; i < nn_addresses; i++) {
  156. printf("%s\n", symbols[i]);
  157. }
  158. free(symbols);
  159. printf("bar OK\n");
  160. return;
  161. }
  162. #pragma GCC diagnostic pop
  163. void foo() {
  164. bar();
  165. #ifdef __linux__
  166. bar2();
  167. #endif
  168. printf("foo OK\n");
  169. return;
  170. }
  171. void* start(void* arg)
  172. {
  173. foo();
  174. printf("coroutine OK\n");
  175. return NULL;
  176. }
  177. int main(int argc, char** argv)
  178. {
  179. if (argc > 1) {
  180. always_use_builtin = 1;
  181. }
  182. st_init();
  183. st_thread_create(start, NULL, 0, 0);
  184. st_thread_exit(NULL);
  185. printf("main done\n");
  186. return 0;
  187. }