| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567 |
- /*
- * Copyright 2010-2025 Branimir Karadzic. All rights reserved.
- * License: https://github.com/bkaradzic/bx/blob/master/LICENSE
- */
- #include <bx/debug.h>
- #include <bx/string.h> // isPrint
- #include <bx/readerwriter.h> // WriterI
- #include <bx/os.h> // exit
- #include <inttypes.h> // PRIx*
- #ifndef BX_CONFIG_CALLSTACK_USE_LIB_BACKTRACE
- # define BX_CONFIG_CALLSTACK_USE_LIB_BACKTRACE 0
- #elif BX_CONFIG_CALLSTACK_USE_LIB_BACKTRACE
- # if !BX_PLATFORM_LINUX || !BX_COMPILER_GCC
- # error "libbackrace is only supported on GCC/Linux."
- # endif // BX_PLATFORM_LINUX && BX_COMPILER_GCC
- #endif // BX_CONFIG_CALLSTACK_USE_LIB_BACKTRACE
- #if BX_CONFIG_CALLSTACK_USE_LIB_BACKTRACE
- # include <execinfo.h> // backtrace
- # include <backtrace.h> // backtrace_syminfo
- # include <cxxabi.h> // abi::__cxa_demangle
- #endif // BX_CONFIG_CALLSTACK_*
- #ifndef BX_CONFIG_EXCEPTION_HANDLING_USE_WINDOWS_SEH
- # define BX_CONFIG_EXCEPTION_HANDLING_USE_WINDOWS_SEH BX_PLATFORM_WINDOWS
- #endif // BX_CONFIG_EXCEPTION_HANDLING_USE_WINDOWS_SEH
- #ifndef BX_CONFIG_EXCEPTION_HANDLING_USE_POSIX_SIGNALS
- # define BX_CONFIG_EXCEPTION_HANDLING_USE_POSIX_SIGNALS 0 \
- | (BX_PLATFORM_LINUX && !BX_CRT_NONE)
- #endif // BX_CONFIG_EXCEPTION_HANDLING_USE_POSIX_SIGNALS
- #if BX_CONFIG_EXCEPTION_HANDLING_USE_POSIX_SIGNALS
- # include <signal.h>
- #elif BX_CONFIG_EXCEPTION_HANDLING_USE_WINDOWS_SEH
- struct ExceptionRecord
- {
- uint32_t exceptionCode;
- uint32_t exceptionFlags;
- ExceptionRecord* exceptionRecord;
- uintptr_t exceptionAddress;
- uint32_t numberParameters;
- uintptr_t exceptionInformation[15];
- };
- struct ExceptionPointers
- {
- ExceptionRecord* exceptionRecord;
- void* contextRecord;
- };
- typedef uint32_t (__stdcall* TopLevelExceptionFilterFn)(ExceptionPointers* _exceptionInfo);
- extern "C" __declspec(dllimport) TopLevelExceptionFilterFn __stdcall SetUnhandledExceptionFilter(TopLevelExceptionFilterFn _topLevelExceptionFilter);
- #endif // BX_CONFIG_EXCEPTION_HANDLING_*
- #if BX_CRT_NONE
- # include <bx/crt0.h>
- #elif BX_PLATFORM_ANDROID
- # include <android/log.h>
- #elif BX_PLATFORM_WINDOWS \
- || BX_PLATFORM_WINRT \
- || BX_PLATFORM_XBOXONE
- extern "C" __declspec(dllimport) void __stdcall OutputDebugStringA(const char* _str);
- #elif BX_PLATFORM_IOS || BX_PLATFORM_OSX || BX_PLATFORM_VISIONOS
- # if defined(__OBJC__)
- # import <Foundation/NSObjCRuntime.h>
- # else
- # include <CoreFoundation/CFString.h>
- extern "C" void NSLog(CFStringRef _format, ...);
- # endif // defined(__OBJC__)
- #elif BX_PLATFORM_EMSCRIPTEN
- # include <emscripten/emscripten.h>
- #else
- # include <stdio.h> // fputs, fflush
- #endif // BX_PLATFORM_WINDOWS
- namespace bx
- {
- void debugBreak()
- {
- #if BX_COMPILER_MSVC
- __debugbreak();
- #elif BX_CPU_ARM
- __builtin_trap();
- // asm("bkpt 0");
- #elif BX_CPU_X86 && (BX_COMPILER_GCC || BX_COMPILER_CLANG)
- // NaCl doesn't like int 3:
- // NativeClient: NaCl module load failed: Validation failure. File violates Native Client safety rules.
- __asm__ ("int $3");
- #elif BX_PLATFORM_EMSCRIPTEN
- emscripten_log(0
- | EM_LOG_CONSOLE
- | EM_LOG_ERROR
- | EM_LOG_C_STACK
- | EM_LOG_JS_STACK
- , "debugBreak!"
- );
- // Doing emscripten_debugger() disables asm.js validation due to an emscripten bug
- //emscripten_debugger();
- EM_ASM({ debugger; });
- #else // cross platform implementation
- int* int3 = (int*)3L;
- *int3 = 3;
- #endif // BX
- }
- void debugOutput(const char* _out)
- {
- #if BX_CRT_NONE
- crt0::debugOutput(_out);
- #elif BX_PLATFORM_ANDROID
- # ifndef BX_ANDROID_LOG_TAG
- # define BX_ANDROID_LOG_TAG ""
- # endif // BX_ANDROID_LOG_TAG
- __android_log_write(ANDROID_LOG_DEBUG, BX_ANDROID_LOG_TAG, _out);
- #elif BX_PLATFORM_WINDOWS \
- || BX_PLATFORM_WINRT \
- || BX_PLATFORM_XBOXONE
- OutputDebugStringA(_out);
- #elif BX_PLATFORM_IOS \
- || BX_PLATFORM_OSX \
- || BX_PLATFORM_VISIONOS
- # if defined(__OBJC__)
- NSLog(@"%s", _out);
- # else
- NSLog(__CFStringMakeConstantString("%s"), _out);
- # endif // defined(__OBJC__)
- #elif BX_PLATFORM_EMSCRIPTEN
- emscripten_log(EM_LOG_CONSOLE, "%s", _out);
- #else
- fputs(_out, stdout);
- fflush(stdout);
- #endif // BX_PLATFORM_
- }
- void debugOutput(const StringView& _str)
- {
- #if BX_CRT_NONE
- crt0::debugOutput(_str);
- #else
- const char* data = _str.getPtr();
- int32_t size = _str.getLength();
- char temp[4096];
- while (0 != size)
- {
- uint32_t len = uint32_min(sizeof(temp)-1, size);
- memCopy(temp, data, len);
- temp[len] = '\0';
- data += len;
- size -= len;
- debugOutput(temp);
- }
- #endif // BX_CRT_NONE
- }
- void debugPrintfVargs(const char* _format, va_list _argList)
- {
- char temp[8192];
- char* out = temp;
- int32_t len = vsnprintf(out, sizeof(temp), _format, _argList);
- if ( (int32_t)sizeof(temp) < len)
- {
- out = (char*)BX_STACK_ALLOC(len+1);
- len = vsnprintf(out, len, _format, _argList);
- }
- out[len] = '\0';
- debugOutput(out);
- }
- void debugPrintf(const char* _format, ...)
- {
- va_list argList;
- va_start(argList, _format);
- debugPrintfVargs(_format, argList);
- va_end(argList);
- }
- #define DBG_ADDRESS "%" PRIxPTR
- void debugPrintfData(const void* _data, uint32_t _size, const char* _format, ...)
- {
- #define HEX_DUMP_WIDTH 16
- #define HEX_DUMP_SPACE_WIDTH 48
- #define HEX_DUMP_FORMAT "%-" BX_STRINGIZE(HEX_DUMP_SPACE_WIDTH) "." BX_STRINGIZE(HEX_DUMP_SPACE_WIDTH) "s"
- va_list argList;
- va_start(argList, _format);
- debugPrintfVargs(_format, argList);
- va_end(argList);
- debugPrintf("\ndata: " DBG_ADDRESS ", size: %d\n", _data, _size);
- if (NULL != _data)
- {
- const uint8_t* data = (const uint8_t*)_data;
- char hex[HEX_DUMP_WIDTH*3+1];
- char ascii[HEX_DUMP_WIDTH+1];
- uint32_t hexPos = 0;
- uint32_t asciiPos = 0;
- for (uint32_t ii = 0; ii < _size; ++ii)
- {
- snprintf(&hex[hexPos], sizeof(hex)-hexPos, "%02x ", data[asciiPos]);
- hexPos += 3;
- ascii[asciiPos] = isPrint(data[asciiPos]) ? data[asciiPos] : '.';
- asciiPos++;
- if (HEX_DUMP_WIDTH == asciiPos)
- {
- ascii[asciiPos] = '\0';
- debugPrintf("\t" DBG_ADDRESS "\t" HEX_DUMP_FORMAT "\t%s\n", data, hex, ascii);
- data += asciiPos;
- hexPos = 0;
- asciiPos = 0;
- }
- }
- if (0 != asciiPos)
- {
- ascii[asciiPos] = '\0';
- debugPrintf("\t" DBG_ADDRESS "\t" HEX_DUMP_FORMAT "\t%s\n", data, hex, ascii);
- }
- }
- #undef HEX_DUMP_WIDTH
- #undef HEX_DUMP_SPACE_WIDTH
- #undef HEX_DUMP_FORMAT
- }
- class DebugWriter : public WriterI
- {
- virtual int32_t write(const void* _data, int32_t _size, Error* _err) override
- {
- BX_UNUSED(_err);
- debugOutput(StringView( (const char*)_data, _size) );
- return _size;
- }
- };
- WriterI* getDebugOut()
- {
- static DebugWriter s_debugOut;
- return &s_debugOut;
- }
- #if BX_CONFIG_CALLSTACK_USE_LIB_BACKTRACE
- uint32_t getCallStack(uint32_t _skip, uint32_t _max, uintptr_t* _outStack)
- {
- const uint32_t max = _skip+_max+1;
- void** tmp = (void**)BX_STACK_ALLOC(sizeof(uintptr_t)*max);
- const uint32_t numFull = backtrace(tmp, max);
- const uint32_t skip = min(_skip + 1 /* skip self */, numFull);
- const uint32_t num = numFull - skip;
- memCopy(_outStack, tmp + skip, sizeof(uintptr_t)*num);
- return num;
- }
- struct StackTraceContext
- {
- StackTraceContext()
- {
- state = backtrace_create_state(NULL, 0, NULL, NULL);
- }
- struct backtrace_state* state;
- };
- static StackTraceContext s_stCtx;
- struct CallbackData
- {
- StringView resolvedName;
- StringView fileName;
- int32_t line;
- };
- static void backtraceSymInfoCb(void* _data, uintptr_t _pc, const char* _symName, uintptr_t _symVal, uintptr_t _symSize)
- {
- BX_UNUSED(_pc, _symVal);
- CallbackData* cbData = (CallbackData*)_data;
- cbData->resolvedName.set(_symName, _symSize);
- }
- static int backtraceFullCb(void* _data, uintptr_t _pc, const char* _fileName, int32_t _lineNo, const char* _function)
- {
- BX_UNUSED(_pc, _function);
- CallbackData* cbData = (CallbackData*)_data;
- if (NULL == _fileName)
- {
- cbData->fileName.set("<Unknown?>");
- cbData->line = -1;
- }
- else
- {
- cbData->fileName.set(_fileName);
- cbData->line = _lineNo;
- }
- return 1;
- }
- int32_t writeCallstack(WriterI* _writer, uintptr_t* _stack, uint32_t _num, Error* _err)
- {
- BX_ERROR_SCOPE(_err);
- char demangleBuf[4096];
- size_t demangleLen = BX_COUNTOF(demangleBuf);
- int32_t total = write(_writer, _err, "Callstack (%d):\n", _num);
- constexpr uint32_t kWidth = 40;
- total += write(_writer, _err, "\t #: %-*s Line: PC --- Function ---\n", kWidth, "File ---");
- CallbackData cbData;
- for (uint32_t ii = 0; ii < _num && _err->isOk(); ++ii)
- {
- backtrace_pcinfo(s_stCtx.state, _stack[ii], backtraceFullCb, NULL, &cbData);
- StringView demangledName;
- if (1 == backtrace_syminfo(s_stCtx.state, _stack[ii], backtraceSymInfoCb, NULL, &cbData) )
- {
- demangleLen = BX_COUNTOF(demangleBuf);
- int32_t demangleStatus;
- abi::__cxa_demangle(cbData.resolvedName.getPtr(), demangleBuf, &demangleLen, &demangleStatus);
- if (0 == demangleStatus)
- {
- demangledName.set(demangleBuf, demangleLen);
- }
- else
- {
- demangledName = cbData.resolvedName;
- }
- }
- else
- {
- demangledName = "???";
- }
- const StringView fn = strTail(cbData.fileName, kWidth);
- total += write(_writer, _err
- , "\t%2d: %-*S % 5d: %p %S\n"
- , ii
- , kWidth
- , &fn
- , cbData.line
- , _stack[ii]
- , &demangledName
- );
- if (0 == strCmp(demangledName, "main", 4) )
- {
- if (0 != _num-1-ii)
- {
- total += write(_writer, _err
- , "\t... %d more stack frames below 'main'.\n"
- , _num-1-ii
- );
- }
- break;
- }
- }
- return total;
- }
- #else
- uint32_t getCallStack(uint32_t _skip, uint32_t _max, uintptr_t* _outStack)
- {
- BX_UNUSED(_skip, _max, _outStack);
- return 0;
- }
- int32_t writeCallstack(WriterI* _writer, uintptr_t* _stack, uint32_t _num, Error* _err)
- {
- BX_UNUSED(_writer, _stack, _num, _err);
- return 0;
- }
- #endif // BX_CONFIG_CALLSTACK_*
- void debugOutputCallstack(uint32_t _skip)
- {
- uintptr_t stack[32];
- const uint32_t num = getCallStack(_skip + 1 /* skip self */, BX_COUNTOF(stack), stack);
- writeCallstack(getDebugOut(), stack, num, ErrorIgnore{});
- }
- #if BX_CONFIG_EXCEPTION_HANDLING_USE_POSIX_SIGNALS
- struct SignalInfo
- {
- int32_t signalId;
- const char* name;
- };
- static const SignalInfo s_signalInfo[] =
- { // Linux
- { /* 4 */ SIGILL, "SIGILL - Illegal instruction signal." },
- { /* 6 */ SIGABRT, "SIGABRT - Abort signal." },
- { /* 8 */ SIGFPE, "SIGFPE - Floating point error signal." },
- { /* 11 */ SIGSEGV, "SIGSEGV - Segmentation violation signal." },
- };
- struct ExceptionHandler
- {
- ExceptionHandler()
- {
- BX_TRACE("ExceptionHandler - POSIX");
- stack_t stack;
- stack.ss_sp = s_stack;
- stack.ss_size = sizeof(s_stack);
- stack.ss_flags = 0;
- sigaltstack(&stack, &m_oldStack);
- struct sigaction sa;
- sa.sa_handler = NULL;
- sa.sa_sigaction = signalActionHandler;
- sa.sa_mask = { 0 };
- sa.sa_flags = SA_ONSTACK | SA_SIGINFO;
- sa.sa_restorer = NULL;
- for (uint32_t ii = 0; ii < BX_COUNTOF(s_signalInfo); ++ii)
- {
- sigaction(s_signalInfo[ii].signalId, &sa, &m_oldSignalAction[ii]);
- }
- }
- ~ExceptionHandler()
- {
- }
- static void signalActionHandler(int32_t _signalId, siginfo_t* _info, void* _context)
- {
- BX_UNUSED(_context);
- const char* name = "Unknown signal?";
- for (uint32_t ii = 0; ii < BX_COUNTOF(s_signalInfo); ++ii)
- {
- const SignalInfo& signalInfo = s_signalInfo[ii];
- if (signalInfo.signalId == _signalId)
- {
- name = signalInfo.name;
- break;
- }
- }
- if (assertFunction(Location("Exception Handler", UINT32_MAX), 2
- , "%s SIGNAL %d, ERRNO %d, CODE %d"
- , name
- , _info->si_signo
- , _info->si_errno
- , _info->si_code
- ) )
- {
- exit(kExitFailure, false);
- }
- }
- static constexpr uint32_t kExceptionStackSize = 64<<10;
- static char s_stack[kExceptionStackSize];
- stack_t m_oldStack;
- struct sigaction m_oldSignalAction[BX_COUNTOF(s_signalInfo)];
- };
- char ExceptionHandler::s_stack[kExceptionStackSize];
- #elif BX_CONFIG_EXCEPTION_HANDLING_USE_WINDOWS_SEH
- struct ExceptionInfo
- {
- uint32_t exceptionCode;
- const char* name;
- };
- static const ExceptionInfo s_exceptionInfo[] =
- { // Windows
- { /* EXCEPTION_ACCESS_VIOLATION */ 0xc0000005u, "Access violation." },
- { /* EXCEPTION_ILLEGAL_INSTRUCTION */ 0xc000001du, "Illegal instruction." },
- { /* EXCEPTION_STACK_OVERFLOW */ 0xc00000fdu, "Stack overflow." },
- };
- struct ExceptionHandler
- {
- ExceptionHandler()
- {
- BX_TRACE("ExceptionHandler - Windows SEH");
- SetUnhandledExceptionFilter(topLevelExceptionFilter);
- }
- static uint32_t __stdcall topLevelExceptionFilter(ExceptionPointers* _info)
- {
- const char* name = "Unknown signal?";
- const uint32_t exceptionCode = _info->exceptionRecord->exceptionCode;
- for (uint32_t ii = 0; ii < BX_COUNTOF(s_exceptionInfo); ++ii)
- {
- const ExceptionInfo& signal = s_exceptionInfo[ii];
- if (signal.exceptionCode == exceptionCode)
- {
- name = signal.name;
- break;
- }
- }
- if (assertFunction(Location("Exception Handler", UINT32_MAX), 2
- , "%s Exception Code %x"
- , name
- , _info->exceptionRecord->exceptionCode
- ) )
- {
- exit(kExitFailure, false);
- }
- return 0 /* EXCEPTION_CONTINUE_SEARCH */;
- }
- };
- #else // Noop exception handler
- class ExceptionHandler
- {
- public:
- ExceptionHandler()
- {
- BX_TRACE("ExceptionHandler - Noop");
- }
- };
- #endif // BX_CONFIG_EXCEPTION_HANDLING_*
- void installExceptionHandler()
- {
- static bool s_installed = false;
- if (!s_installed)
- {
- s_installed = true;
- static ExceptionHandler s_exceptionHandler;
- }
- }
- } // namespace bx
|