debug.cpp 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567
  1. /*
  2. * Copyright 2010-2025 Branimir Karadzic. All rights reserved.
  3. * License: https://github.com/bkaradzic/bx/blob/master/LICENSE
  4. */
  5. #include <bx/debug.h>
  6. #include <bx/string.h> // isPrint
  7. #include <bx/readerwriter.h> // WriterI
  8. #include <bx/os.h> // exit
  9. #include <inttypes.h> // PRIx*
  10. #ifndef BX_CONFIG_CALLSTACK_USE_LIB_BACKTRACE
  11. # define BX_CONFIG_CALLSTACK_USE_LIB_BACKTRACE 0
  12. #elif BX_CONFIG_CALLSTACK_USE_LIB_BACKTRACE
  13. # if !BX_PLATFORM_LINUX || !BX_COMPILER_GCC
  14. # error "libbackrace is only supported on GCC/Linux."
  15. # endif // BX_PLATFORM_LINUX && BX_COMPILER_GCC
  16. #endif // BX_CONFIG_CALLSTACK_USE_LIB_BACKTRACE
  17. #if BX_CONFIG_CALLSTACK_USE_LIB_BACKTRACE
  18. # include <execinfo.h> // backtrace
  19. # include <backtrace.h> // backtrace_syminfo
  20. # include <cxxabi.h> // abi::__cxa_demangle
  21. #endif // BX_CONFIG_CALLSTACK_*
  22. #ifndef BX_CONFIG_EXCEPTION_HANDLING_USE_WINDOWS_SEH
  23. # define BX_CONFIG_EXCEPTION_HANDLING_USE_WINDOWS_SEH BX_PLATFORM_WINDOWS
  24. #endif // BX_CONFIG_EXCEPTION_HANDLING_USE_WINDOWS_SEH
  25. #ifndef BX_CONFIG_EXCEPTION_HANDLING_USE_POSIX_SIGNALS
  26. # define BX_CONFIG_EXCEPTION_HANDLING_USE_POSIX_SIGNALS 0 \
  27. | (BX_PLATFORM_LINUX && !BX_CRT_NONE)
  28. #endif // BX_CONFIG_EXCEPTION_HANDLING_USE_POSIX_SIGNALS
  29. #if BX_CONFIG_EXCEPTION_HANDLING_USE_POSIX_SIGNALS
  30. # include <signal.h>
  31. #elif BX_CONFIG_EXCEPTION_HANDLING_USE_WINDOWS_SEH
  32. struct ExceptionRecord
  33. {
  34. uint32_t exceptionCode;
  35. uint32_t exceptionFlags;
  36. ExceptionRecord* exceptionRecord;
  37. uintptr_t exceptionAddress;
  38. uint32_t numberParameters;
  39. uintptr_t exceptionInformation[15];
  40. };
  41. struct ExceptionPointers
  42. {
  43. ExceptionRecord* exceptionRecord;
  44. void* contextRecord;
  45. };
  46. typedef uint32_t (__stdcall* TopLevelExceptionFilterFn)(ExceptionPointers* _exceptionInfo);
  47. extern "C" __declspec(dllimport) TopLevelExceptionFilterFn __stdcall SetUnhandledExceptionFilter(TopLevelExceptionFilterFn _topLevelExceptionFilter);
  48. #endif // BX_CONFIG_EXCEPTION_HANDLING_*
  49. #if BX_CRT_NONE
  50. # include <bx/crt0.h>
  51. #elif BX_PLATFORM_ANDROID
  52. # include <android/log.h>
  53. #elif BX_PLATFORM_WINDOWS \
  54. || BX_PLATFORM_WINRT \
  55. || BX_PLATFORM_XBOXONE
  56. extern "C" __declspec(dllimport) void __stdcall OutputDebugStringA(const char* _str);
  57. #elif BX_PLATFORM_IOS || BX_PLATFORM_OSX || BX_PLATFORM_VISIONOS
  58. # if defined(__OBJC__)
  59. # import <Foundation/NSObjCRuntime.h>
  60. # else
  61. # include <CoreFoundation/CFString.h>
  62. extern "C" void NSLog(CFStringRef _format, ...);
  63. # endif // defined(__OBJC__)
  64. #elif BX_PLATFORM_EMSCRIPTEN
  65. # include <emscripten/emscripten.h>
  66. #else
  67. # include <stdio.h> // fputs, fflush
  68. #endif // BX_PLATFORM_WINDOWS
  69. namespace bx
  70. {
  71. void debugBreak()
  72. {
  73. #if BX_COMPILER_MSVC
  74. __debugbreak();
  75. #elif BX_CPU_ARM
  76. __builtin_trap();
  77. // asm("bkpt 0");
  78. #elif BX_CPU_X86 && (BX_COMPILER_GCC || BX_COMPILER_CLANG)
  79. // NaCl doesn't like int 3:
  80. // NativeClient: NaCl module load failed: Validation failure. File violates Native Client safety rules.
  81. __asm__ ("int $3");
  82. #elif BX_PLATFORM_EMSCRIPTEN
  83. emscripten_log(0
  84. | EM_LOG_CONSOLE
  85. | EM_LOG_ERROR
  86. | EM_LOG_C_STACK
  87. | EM_LOG_JS_STACK
  88. , "debugBreak!"
  89. );
  90. // Doing emscripten_debugger() disables asm.js validation due to an emscripten bug
  91. //emscripten_debugger();
  92. EM_ASM({ debugger; });
  93. #else // cross platform implementation
  94. int* int3 = (int*)3L;
  95. *int3 = 3;
  96. #endif // BX
  97. }
  98. void debugOutput(const char* _out)
  99. {
  100. #if BX_CRT_NONE
  101. crt0::debugOutput(_out);
  102. #elif BX_PLATFORM_ANDROID
  103. # ifndef BX_ANDROID_LOG_TAG
  104. # define BX_ANDROID_LOG_TAG ""
  105. # endif // BX_ANDROID_LOG_TAG
  106. __android_log_write(ANDROID_LOG_DEBUG, BX_ANDROID_LOG_TAG, _out);
  107. #elif BX_PLATFORM_WINDOWS \
  108. || BX_PLATFORM_WINRT \
  109. || BX_PLATFORM_XBOXONE
  110. OutputDebugStringA(_out);
  111. #elif BX_PLATFORM_IOS \
  112. || BX_PLATFORM_OSX \
  113. || BX_PLATFORM_VISIONOS
  114. # if defined(__OBJC__)
  115. NSLog(@"%s", _out);
  116. # else
  117. NSLog(__CFStringMakeConstantString("%s"), _out);
  118. # endif // defined(__OBJC__)
  119. #elif BX_PLATFORM_EMSCRIPTEN
  120. emscripten_log(EM_LOG_CONSOLE, "%s", _out);
  121. #else
  122. fputs(_out, stdout);
  123. fflush(stdout);
  124. #endif // BX_PLATFORM_
  125. }
  126. void debugOutput(const StringView& _str)
  127. {
  128. #if BX_CRT_NONE
  129. crt0::debugOutput(_str);
  130. #else
  131. const char* data = _str.getPtr();
  132. int32_t size = _str.getLength();
  133. char temp[4096];
  134. while (0 != size)
  135. {
  136. uint32_t len = uint32_min(sizeof(temp)-1, size);
  137. memCopy(temp, data, len);
  138. temp[len] = '\0';
  139. data += len;
  140. size -= len;
  141. debugOutput(temp);
  142. }
  143. #endif // BX_CRT_NONE
  144. }
  145. void debugPrintfVargs(const char* _format, va_list _argList)
  146. {
  147. char temp[8192];
  148. char* out = temp;
  149. int32_t len = vsnprintf(out, sizeof(temp), _format, _argList);
  150. if ( (int32_t)sizeof(temp) < len)
  151. {
  152. out = (char*)BX_STACK_ALLOC(len+1);
  153. len = vsnprintf(out, len, _format, _argList);
  154. }
  155. out[len] = '\0';
  156. debugOutput(out);
  157. }
  158. void debugPrintf(const char* _format, ...)
  159. {
  160. va_list argList;
  161. va_start(argList, _format);
  162. debugPrintfVargs(_format, argList);
  163. va_end(argList);
  164. }
  165. #define DBG_ADDRESS "%" PRIxPTR
  166. void debugPrintfData(const void* _data, uint32_t _size, const char* _format, ...)
  167. {
  168. #define HEX_DUMP_WIDTH 16
  169. #define HEX_DUMP_SPACE_WIDTH 48
  170. #define HEX_DUMP_FORMAT "%-" BX_STRINGIZE(HEX_DUMP_SPACE_WIDTH) "." BX_STRINGIZE(HEX_DUMP_SPACE_WIDTH) "s"
  171. va_list argList;
  172. va_start(argList, _format);
  173. debugPrintfVargs(_format, argList);
  174. va_end(argList);
  175. debugPrintf("\ndata: " DBG_ADDRESS ", size: %d\n", _data, _size);
  176. if (NULL != _data)
  177. {
  178. const uint8_t* data = (const uint8_t*)_data;
  179. char hex[HEX_DUMP_WIDTH*3+1];
  180. char ascii[HEX_DUMP_WIDTH+1];
  181. uint32_t hexPos = 0;
  182. uint32_t asciiPos = 0;
  183. for (uint32_t ii = 0; ii < _size; ++ii)
  184. {
  185. snprintf(&hex[hexPos], sizeof(hex)-hexPos, "%02x ", data[asciiPos]);
  186. hexPos += 3;
  187. ascii[asciiPos] = isPrint(data[asciiPos]) ? data[asciiPos] : '.';
  188. asciiPos++;
  189. if (HEX_DUMP_WIDTH == asciiPos)
  190. {
  191. ascii[asciiPos] = '\0';
  192. debugPrintf("\t" DBG_ADDRESS "\t" HEX_DUMP_FORMAT "\t%s\n", data, hex, ascii);
  193. data += asciiPos;
  194. hexPos = 0;
  195. asciiPos = 0;
  196. }
  197. }
  198. if (0 != asciiPos)
  199. {
  200. ascii[asciiPos] = '\0';
  201. debugPrintf("\t" DBG_ADDRESS "\t" HEX_DUMP_FORMAT "\t%s\n", data, hex, ascii);
  202. }
  203. }
  204. #undef HEX_DUMP_WIDTH
  205. #undef HEX_DUMP_SPACE_WIDTH
  206. #undef HEX_DUMP_FORMAT
  207. }
  208. class DebugWriter : public WriterI
  209. {
  210. virtual int32_t write(const void* _data, int32_t _size, Error* _err) override
  211. {
  212. BX_UNUSED(_err);
  213. debugOutput(StringView( (const char*)_data, _size) );
  214. return _size;
  215. }
  216. };
  217. WriterI* getDebugOut()
  218. {
  219. static DebugWriter s_debugOut;
  220. return &s_debugOut;
  221. }
  222. #if BX_CONFIG_CALLSTACK_USE_LIB_BACKTRACE
  223. uint32_t getCallStack(uint32_t _skip, uint32_t _max, uintptr_t* _outStack)
  224. {
  225. const uint32_t max = _skip+_max+1;
  226. void** tmp = (void**)BX_STACK_ALLOC(sizeof(uintptr_t)*max);
  227. const uint32_t numFull = backtrace(tmp, max);
  228. const uint32_t skip = min(_skip + 1 /* skip self */, numFull);
  229. const uint32_t num = numFull - skip;
  230. memCopy(_outStack, tmp + skip, sizeof(uintptr_t)*num);
  231. return num;
  232. }
  233. struct StackTraceContext
  234. {
  235. StackTraceContext()
  236. {
  237. state = backtrace_create_state(NULL, 0, NULL, NULL);
  238. }
  239. struct backtrace_state* state;
  240. };
  241. static StackTraceContext s_stCtx;
  242. struct CallbackData
  243. {
  244. StringView resolvedName;
  245. StringView fileName;
  246. int32_t line;
  247. };
  248. static void backtraceSymInfoCb(void* _data, uintptr_t _pc, const char* _symName, uintptr_t _symVal, uintptr_t _symSize)
  249. {
  250. BX_UNUSED(_pc, _symVal);
  251. CallbackData* cbData = (CallbackData*)_data;
  252. cbData->resolvedName.set(_symName, _symSize);
  253. }
  254. static int backtraceFullCb(void* _data, uintptr_t _pc, const char* _fileName, int32_t _lineNo, const char* _function)
  255. {
  256. BX_UNUSED(_pc, _function);
  257. CallbackData* cbData = (CallbackData*)_data;
  258. if (NULL == _fileName)
  259. {
  260. cbData->fileName.set("<Unknown?>");
  261. cbData->line = -1;
  262. }
  263. else
  264. {
  265. cbData->fileName.set(_fileName);
  266. cbData->line = _lineNo;
  267. }
  268. return 1;
  269. }
  270. int32_t writeCallstack(WriterI* _writer, uintptr_t* _stack, uint32_t _num, Error* _err)
  271. {
  272. BX_ERROR_SCOPE(_err);
  273. char demangleBuf[4096];
  274. size_t demangleLen = BX_COUNTOF(demangleBuf);
  275. int32_t total = write(_writer, _err, "Callstack (%d):\n", _num);
  276. constexpr uint32_t kWidth = 40;
  277. total += write(_writer, _err, "\t #: %-*s Line: PC --- Function ---\n", kWidth, "File ---");
  278. CallbackData cbData;
  279. for (uint32_t ii = 0; ii < _num && _err->isOk(); ++ii)
  280. {
  281. backtrace_pcinfo(s_stCtx.state, _stack[ii], backtraceFullCb, NULL, &cbData);
  282. StringView demangledName;
  283. if (1 == backtrace_syminfo(s_stCtx.state, _stack[ii], backtraceSymInfoCb, NULL, &cbData) )
  284. {
  285. demangleLen = BX_COUNTOF(demangleBuf);
  286. int32_t demangleStatus;
  287. abi::__cxa_demangle(cbData.resolvedName.getPtr(), demangleBuf, &demangleLen, &demangleStatus);
  288. if (0 == demangleStatus)
  289. {
  290. demangledName.set(demangleBuf, demangleLen);
  291. }
  292. else
  293. {
  294. demangledName = cbData.resolvedName;
  295. }
  296. }
  297. else
  298. {
  299. demangledName = "???";
  300. }
  301. const StringView fn = strTail(cbData.fileName, kWidth);
  302. total += write(_writer, _err
  303. , "\t%2d: %-*S % 5d: %p %S\n"
  304. , ii
  305. , kWidth
  306. , &fn
  307. , cbData.line
  308. , _stack[ii]
  309. , &demangledName
  310. );
  311. if (0 == strCmp(demangledName, "main", 4) )
  312. {
  313. if (0 != _num-1-ii)
  314. {
  315. total += write(_writer, _err
  316. , "\t... %d more stack frames below 'main'.\n"
  317. , _num-1-ii
  318. );
  319. }
  320. break;
  321. }
  322. }
  323. return total;
  324. }
  325. #else
  326. uint32_t getCallStack(uint32_t _skip, uint32_t _max, uintptr_t* _outStack)
  327. {
  328. BX_UNUSED(_skip, _max, _outStack);
  329. return 0;
  330. }
  331. int32_t writeCallstack(WriterI* _writer, uintptr_t* _stack, uint32_t _num, Error* _err)
  332. {
  333. BX_UNUSED(_writer, _stack, _num, _err);
  334. return 0;
  335. }
  336. #endif // BX_CONFIG_CALLSTACK_*
  337. void debugOutputCallstack(uint32_t _skip)
  338. {
  339. uintptr_t stack[32];
  340. const uint32_t num = getCallStack(_skip + 1 /* skip self */, BX_COUNTOF(stack), stack);
  341. writeCallstack(getDebugOut(), stack, num, ErrorIgnore{});
  342. }
  343. #if BX_CONFIG_EXCEPTION_HANDLING_USE_POSIX_SIGNALS
  344. struct SignalInfo
  345. {
  346. int32_t signalId;
  347. const char* name;
  348. };
  349. static const SignalInfo s_signalInfo[] =
  350. { // Linux
  351. { /* 4 */ SIGILL, "SIGILL - Illegal instruction signal." },
  352. { /* 6 */ SIGABRT, "SIGABRT - Abort signal." },
  353. { /* 8 */ SIGFPE, "SIGFPE - Floating point error signal." },
  354. { /* 11 */ SIGSEGV, "SIGSEGV - Segmentation violation signal." },
  355. };
  356. struct ExceptionHandler
  357. {
  358. ExceptionHandler()
  359. {
  360. BX_TRACE("ExceptionHandler - POSIX");
  361. stack_t stack;
  362. stack.ss_sp = s_stack;
  363. stack.ss_size = sizeof(s_stack);
  364. stack.ss_flags = 0;
  365. sigaltstack(&stack, &m_oldStack);
  366. struct sigaction sa;
  367. sa.sa_handler = NULL;
  368. sa.sa_sigaction = signalActionHandler;
  369. sa.sa_mask = { 0 };
  370. sa.sa_flags = SA_ONSTACK | SA_SIGINFO;
  371. sa.sa_restorer = NULL;
  372. for (uint32_t ii = 0; ii < BX_COUNTOF(s_signalInfo); ++ii)
  373. {
  374. sigaction(s_signalInfo[ii].signalId, &sa, &m_oldSignalAction[ii]);
  375. }
  376. }
  377. ~ExceptionHandler()
  378. {
  379. }
  380. static void signalActionHandler(int32_t _signalId, siginfo_t* _info, void* _context)
  381. {
  382. BX_UNUSED(_context);
  383. const char* name = "Unknown signal?";
  384. for (uint32_t ii = 0; ii < BX_COUNTOF(s_signalInfo); ++ii)
  385. {
  386. const SignalInfo& signalInfo = s_signalInfo[ii];
  387. if (signalInfo.signalId == _signalId)
  388. {
  389. name = signalInfo.name;
  390. break;
  391. }
  392. }
  393. if (assertFunction(Location("Exception Handler", UINT32_MAX), 2
  394. , "%s SIGNAL %d, ERRNO %d, CODE %d"
  395. , name
  396. , _info->si_signo
  397. , _info->si_errno
  398. , _info->si_code
  399. ) )
  400. {
  401. exit(kExitFailure, false);
  402. }
  403. }
  404. static constexpr uint32_t kExceptionStackSize = 64<<10;
  405. static char s_stack[kExceptionStackSize];
  406. stack_t m_oldStack;
  407. struct sigaction m_oldSignalAction[BX_COUNTOF(s_signalInfo)];
  408. };
  409. char ExceptionHandler::s_stack[kExceptionStackSize];
  410. #elif BX_CONFIG_EXCEPTION_HANDLING_USE_WINDOWS_SEH
  411. struct ExceptionInfo
  412. {
  413. uint32_t exceptionCode;
  414. const char* name;
  415. };
  416. static const ExceptionInfo s_exceptionInfo[] =
  417. { // Windows
  418. { /* EXCEPTION_ACCESS_VIOLATION */ 0xc0000005u, "Access violation." },
  419. { /* EXCEPTION_ILLEGAL_INSTRUCTION */ 0xc000001du, "Illegal instruction." },
  420. { /* EXCEPTION_STACK_OVERFLOW */ 0xc00000fdu, "Stack overflow." },
  421. };
  422. struct ExceptionHandler
  423. {
  424. ExceptionHandler()
  425. {
  426. BX_TRACE("ExceptionHandler - Windows SEH");
  427. SetUnhandledExceptionFilter(topLevelExceptionFilter);
  428. }
  429. static uint32_t __stdcall topLevelExceptionFilter(ExceptionPointers* _info)
  430. {
  431. const char* name = "Unknown signal?";
  432. const uint32_t exceptionCode = _info->exceptionRecord->exceptionCode;
  433. for (uint32_t ii = 0; ii < BX_COUNTOF(s_exceptionInfo); ++ii)
  434. {
  435. const ExceptionInfo& signal = s_exceptionInfo[ii];
  436. if (signal.exceptionCode == exceptionCode)
  437. {
  438. name = signal.name;
  439. break;
  440. }
  441. }
  442. if (assertFunction(Location("Exception Handler", UINT32_MAX), 2
  443. , "%s Exception Code %x"
  444. , name
  445. , _info->exceptionRecord->exceptionCode
  446. ) )
  447. {
  448. exit(kExitFailure, false);
  449. }
  450. return 0 /* EXCEPTION_CONTINUE_SEARCH */;
  451. }
  452. };
  453. #else // Noop exception handler
  454. class ExceptionHandler
  455. {
  456. public:
  457. ExceptionHandler()
  458. {
  459. BX_TRACE("ExceptionHandler - Noop");
  460. }
  461. };
  462. #endif // BX_CONFIG_EXCEPTION_HANDLING_*
  463. void installExceptionHandler()
  464. {
  465. static bool s_installed = false;
  466. if (!s_installed)
  467. {
  468. s_installed = true;
  469. static ExceptionHandler s_exceptionHandler;
  470. }
  471. }
  472. } // namespace bx