|
@@ -8,6 +8,20 @@
|
|
|
#include <bx/readerwriter.h> // WriterI
|
|
#include <bx/readerwriter.h> // WriterI
|
|
|
#include <inttypes.h> // PRIx*
|
|
#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_*
|
|
|
|
|
+
|
|
|
#if BX_CRT_NONE
|
|
#if BX_CRT_NONE
|
|
|
# include <bx/crt0.h>
|
|
# include <bx/crt0.h>
|
|
|
#elif BX_PLATFORM_ANDROID
|
|
#elif BX_PLATFORM_ANDROID
|
|
@@ -199,4 +213,170 @@ namespace bx
|
|
|
return &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**)alloca(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;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // If offset in UTF-8 string doesn't land on rune, walk back until first byte of rune is reached.
|
|
|
|
|
+ static const char* fixPtrToRune(const char* _strBegin, const char* _curr)
|
|
|
|
|
+ {
|
|
|
|
|
+ for (; _curr > _strBegin && (*_curr & 0xc0) == 0x80; --_curr);
|
|
|
|
|
+
|
|
|
|
|
+ return _curr;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ StringView strTail(const StringView _str, uint32_t _num)
|
|
|
|
|
+ {
|
|
|
|
|
+ return StringView(
|
|
|
|
|
+ fixPtrToRune(_str.getPtr(), _str.getTerm() - min(_num, _str.getLength() ) )
|
|
|
|
|
+ , _str.getTerm()
|
|
|
|
|
+ );
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ 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);
|
|
|
|
|
+
|
|
|
|
|
+ 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 = "???";
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ constexpr uint32_t width = 40;
|
|
|
|
|
+ const StringView fn = strTail(cbData.fileName, width);
|
|
|
|
|
+
|
|
|
|
|
+ total += write(_writer, _err
|
|
|
|
|
+ , "\t%2d: %-*S % 5d: %p %S\n"
|
|
|
|
|
+ , ii
|
|
|
|
|
+ , width
|
|
|
|
|
+ , &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{});
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
} // namespace bx
|
|
} // namespace bx
|