callstack_linux.cpp 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210
  1. /*
  2. * Copyright (c) 2012-2026 Daniele Bartolini et al.
  3. * SPDX-License-Identifier: MIT
  4. */
  5. #include "core/platform.h"
  6. #if CROWN_PLATFORM_LINUX && (CROWN_COMPILER_GCC || CROWN_COMPILER_CLANG)
  7. #include "core/debug/callstack.h"
  8. #include "core/process.h"
  9. #include "core/strings/string.inl"
  10. #include <cxxabi.h> // __cxa_demangle
  11. #include <execinfo.h> // backtrace, backtrace_symbols_fd
  12. #include <stdio.h> // fdopen, fgets
  13. #include <stdlib.h> // free
  14. #include <string.h> // strchr
  15. #include <sys/wait.h> // waitpid
  16. #include <unistd.h> // getpid
  17. #include <stb_sprintf.h>
  18. namespace crown
  19. {
  20. namespace debug
  21. {
  22. static int symbol_fds[2];
  23. static int demangled_fds[2];
  24. static int exit_fds[2];
  25. static pid_t demangler;
  26. static const char *addr2line(char *line, int len, const char *addr)
  27. {
  28. char process_exe[256];
  29. stbsp_snprintf(process_exe, sizeof(process_exe), "/proc/%u/exe", getpid());
  30. const char *argv[] =
  31. {
  32. "addr2line",
  33. "-s",
  34. "-e",
  35. process_exe,
  36. addr,
  37. NULL
  38. };
  39. Process pr;
  40. if (pr.spawn(argv, CROWN_PROCESS_STDOUT_PIPE | CROWN_PROCESS_STDERR_MERGE) == 0) {
  41. u32 num_read;
  42. pr.read(&num_read, line, len);
  43. line[num_read - 1] = '\0';
  44. pr.wait();
  45. return line;
  46. }
  47. return "<addr2line missing>";
  48. }
  49. static int demangler_main()
  50. {
  51. FILE *fp = fdopen(symbol_fds[0], "r");
  52. if (fp == NULL)
  53. return EXIT_FAILURE;
  54. while (true) {
  55. fd_set fdset;
  56. FD_ZERO(&fdset);
  57. FD_SET(symbol_fds[0], &fdset);
  58. FD_SET(exit_fds[0], &fdset);
  59. int maxfd = max(symbol_fds[0], exit_fds[0]);
  60. if (select(maxfd + 1, &fdset, NULL, NULL, NULL) <= 0)
  61. continue;
  62. if (FD_ISSET(exit_fds[0], &fdset)) {
  63. break;
  64. } else if (FD_ISSET(symbol_fds[0], &fdset)) {
  65. char msg[512];
  66. char buf[512];
  67. while (fgets(msg, sizeof(msg), fp) != NULL) {
  68. u32 len = strlen32(msg);
  69. if (len <= 1)
  70. break;
  71. msg[len - 1] = '\0';
  72. char *mangled_name = strchr(msg, '(');
  73. char *offset_begin = strchr(msg, '+');
  74. char *offset_end = strchr(msg, ')');
  75. char *addr_begin = strchr(msg, '[');
  76. char *addr_end = strchr(msg, ']');
  77. // Attempt to demangle the symbol.
  78. if (mangled_name && offset_begin && offset_end && mangled_name < offset_begin) {
  79. *mangled_name++ = '\0';
  80. *offset_begin++ = '\0';
  81. *offset_end++ = '\0';
  82. *addr_begin++ = '\0';
  83. *addr_end++ = '\0';
  84. int demangle_ok;
  85. char *real_name = abi::__cxa_demangle(mangled_name, 0, 0, &demangle_ok);
  86. char line[256];
  87. memset(line, 0, sizeof(line));
  88. stbsp_snprintf(buf
  89. , sizeof(buf)
  90. , "%s(%s+%s) in %s"
  91. , msg
  92. , (demangle_ok == 0 ? real_name : mangled_name)
  93. , offset_begin
  94. , addr2line(line, sizeof(line), addr_begin)
  95. );
  96. free(real_name);
  97. } else {
  98. stbsp_snprintf(buf, sizeof(buf), "%s", msg);
  99. }
  100. write(demangled_fds[1], buf, sizeof(buf));
  101. }
  102. }
  103. }
  104. fclose(fp);
  105. return EXIT_SUCCESS;
  106. }
  107. void callstack_shutdown()
  108. {
  109. int wstatus;
  110. if (demangler <= 0)
  111. return;
  112. write(exit_fds[1], "q", 1);
  113. waitpid(demangler, &wstatus, 0);
  114. demangler = 0;
  115. }
  116. s32 callstack_init()
  117. {
  118. s32 ret = -1;
  119. if (pipe(symbol_fds) < 0)
  120. goto close_0;
  121. if (pipe(demangled_fds) < 0)
  122. goto close_1;
  123. if (pipe(exit_fds) < 0)
  124. goto close_2;
  125. demangler = fork();
  126. if (demangler == -1) {
  127. close(symbol_fds[0]);
  128. close(symbol_fds[1]);
  129. close(demangled_fds[0]);
  130. close(demangled_fds[1]);
  131. close(exit_fds[0]);
  132. close(exit_fds[1]);
  133. return -1;
  134. } else if (demangler == 0) {
  135. // Block all signals.
  136. sigset_t set;
  137. sigfillset(&set);
  138. sigprocmask(SIG_BLOCK, &set, NULL);
  139. close(symbol_fds[1]);
  140. close(demangled_fds[0]);
  141. close(exit_fds[1]);
  142. exit(demangler_main());
  143. } else {
  144. ret = 0;
  145. }
  146. close(exit_fds[0]);
  147. close_2:
  148. close(demangled_fds[1]);
  149. close_1:
  150. close(symbol_fds[0]);
  151. close_0:
  152. return ret;
  153. }
  154. void callstack(log_internal::System system, LogSeverity::Enum severity)
  155. {
  156. char msg[512] = {};
  157. void *array[64];
  158. if (demangler <= 0) {
  159. log_internal::logx(severity, system, "Callstack unavailable.");
  160. return;
  161. }
  162. // Get symbols and write them to demangler process.
  163. int size = backtrace(array, countof(array));
  164. backtrace_symbols_fd(array, size, symbol_fds[1]);
  165. write(symbol_fds[1], "\n", 1);
  166. // Log demangled symbols.
  167. for (int i = 0; i < size; ++i) {
  168. if (read(demangled_fds[0], msg, sizeof(msg)) <= 0)
  169. break;
  170. if (i >= 1) // Skip this very function.
  171. log_internal::logx(severity, system, "[%2d] %s", i, msg);
  172. }
  173. }
  174. } // namespace debug
  175. } // namespace crown
  176. #endif // if CROWN_PLATFORM_LINUX && (CROWN_COMPILER_GCC || CROWN_COMPILER_CLANG)