14#if defined(ZTH_CONFIG_ENABLE_BACKTRACE) && !ZTH_CONFIG_ENABLE_BACKTRACE
16#elif defined(ZTH_OS_WINDOWS)
18#elif defined(ZTH_HAVE_LIBUNWIND)
19# define ZTH_BT_LIBUNWIND
20#elif defined(ZTH_OS_MAC) && defined(ZTH_ARCH_ARM64)
23#elif defined(ZTH_HAVE_EXECINFO)
24# define ZTH_BT_BACKTRACE
30# define ZTH_BT_PRINT_NONE
31#elif defined(ZTH_BT_WIN32)
32# define ZTH_BT_PRINT_WIN32
33#elif defined(ZTH_HAVE_LIBBACKTRACE) && !defined(CLANG_TIDY)
34# define ZTH_BT_PRINT_LIBBACKTRACE
35#elif defined(ZTH_HAVE_DL)
36# define ZTH_BT_PRINT_DL
37#elif defined(ZTH_BT_BACKTRACE) || defined(ZTH_HAVE_EXECINFO)
38# define ZTH_BT_PRINT_SYMBOLS
40# define ZTH_BT_PRINT_ADDR
43#ifndef ZTH_BT_PRINT_NONE
44# if (defined(ZTH_OS_WINDOWS) || defined(ZTH_OS_POSIX)) \
45 && (!defined(ZTH_THREADS) || __cplusplus >= 201103L)
46# define ZTH_BT_ADDR2LINE
66static bool bt_win32_sym_init()
68 static bool sym_init =
false;
72 HANDLE process = GetCurrentProcess();
74 SYMOPT_DEFERRED_LOADS | SYMOPT_LOAD_LINES | SYMOPT_UNDNAME
75 | SYMOPT_FAIL_CRITICAL_ERRORS);
77 if(SymInitialize(process,
nullptr, TRUE) == TRUE) {
83 if(GetLastError() == ERROR_INVALID_PARAMETER) {
95 HANDLE process = GetCurrentProcess();
96 if(!bt_win32_sym_init()) {
101 CONTEXT context = {};
102 RtlCaptureContext(&context);
104 STACKFRAME64 stackFrame = {};
105# if defined(ZTH_ARCH_X86_64)
106 DWORD machineType = IMAGE_FILE_MACHINE_AMD64;
107 stackFrame.AddrPC.Offset = context.Rip;
108 stackFrame.AddrFrame.Offset = context.Rbp;
109 stackFrame.AddrStack.Offset = context.Rsp;
110# elif defined(ZTH_ARCH_X86)
111 DWORD machineType = IMAGE_FILE_MACHINE_I386;
112 stackFrame.AddrPC.Offset = context.Eip;
113 stackFrame.AddrFrame.Offset = context.Ebp;
114 stackFrame.AddrStack.Offset = context.Esp;
116# error Unsupported architecture.
118 stackFrame.AddrPC.Mode = AddrModeFlat;
119 stackFrame.AddrFrame.Mode = AddrModeFlat;
120 stackFrame.AddrStack.Mode = AddrModeFlat;
123 while(depth < maxDepth
125 machineType, process, GetCurrentThread(), &stackFrame, &context,
nullptr,
126 SymFunctionTableAccess64, SymGetModuleBase64,
nullptr)) {
128 bt.
bt().push_back((
void*)stackFrame.AddrPC.Offset);
129 if(!stackFrame.AddrFrame.Offset
130 || stackFrame.AddrPC.Offset ==
reinterpret_cast<DWORD64
>(&
context_entry))
143#ifdef ZTH_BT_LIBUNWIND
144# include <libunwind.h>
152 if(unw_getcontext(&uc))
156 unw_init_local(&cursor, &uc);
158 for(
size_t i = 0; i < skip && unw_step(&cursor) > 0; i++)
163 while(unw_step(&cursor) > 0 && depth < maxDepth) {
167 unw_get_reg(&cursor, UNW_REG_SP, &sp);
171 unw_get_reg(&cursor, UNW_REG_IP, &ip);
172 bt.
bt().push_back(
reinterpret_cast<void*
>(ip));
174 if(unw_get_proc_info(&cursor, &pip) == 0
175 && pip.start_ip ==
reinterpret_cast<unw_word_t
>(&
context_entry))
191#ifdef ZTH_BT_BACKTRACE
192# include <execinfo.h>
199 b.resize((
size_t)backtrace(b.data(), (
int)maxDepth));
206 for(; src < b.size(); src++, dst++) {
208 if(!b[dst] || b[dst] ==
reinterpret_cast<void*
>(&
context_entry))
236#ifdef ZTH_BT_PRINT_NONE
237static __attribute__((unused))
void bt_print(
size_t index,
void const* addr,
int color)
251static __attribute__((unused))
void bt_print_addr(
size_t index,
void const* addr,
int color)
254 color,
"%s%-3lu [%p]\n", color >= 0 ?
ZTH_DBG_PREFIX :
"", (unsigned long)index,
258#ifdef ZTH_BT_PRINT_ADDR
259# define bt_print bt_print_addr
268#if defined(ZTH_HAVE_EXECINFO) && !defined(ZTH_BT_PRINT_NONE)
270# include <execinfo.h>
272static __attribute__((unused))
void bt_print_symbols(
size_t index,
void const* addr,
int color)
274 char** symbols = backtrace_symbols((
void*
const*)&addr, 1);
275 char const* sym = symbols ? symbols[0] :
nullptr;
278 bt_print_addr(index, addr, color);
282 (unsigned long)index, sym);
288# define bt_print_symbols bt_print_addr
291#ifdef ZTH_BT_PRINT_SYMBOLS
292# define bt_print bt_print_symbols
301#if defined(ZTH_HAVE_DL) && !defined(ZTH_BT_PRINT_NONE)
305static __attribute__((unused))
void bt_print_dl(
size_t index,
void const* addr,
int color)
308 if(!dladdr(addr, &info))
312 char* demangled = abi::__cxa_demangle(info.dli_sname,
nullptr,
nullptr, &status);
313 if(status == 0 && demangled) {
315 color,
"%s%-3lu [%p] %s(%s+0x%lx) %s\n", color >= 0 ?
ZTH_DBG_PREFIX :
"",
316 (
unsigned long)index, addr, info.dli_fname, info.dli_sname,
317 (
unsigned long)((uintptr_t)addr - (uintptr_t)info.dli_saddr),
318 demangled ? demangled :
"??");
326# define bt_print_dl bt_print_symbols
329#ifdef ZTH_BT_PRINT_DL
330# define bt_print bt_print_dl
339#if defined(ZTH_BT_ADDR2LINE) && !defined(ZTH_BT_PRINT_NONE)
340# if __cplusplus >= 201103L
345# include <sys/types.h>
346# include <sys/wait.h>
350# define ADDR2LINE_BUF_SIZE 1024
352static void bt_print_addr2line_print(
353 size_t index,
void const* addr,
char const* file,
char const* func,
int color)
355 bool has_func = func && *func && func[0] !=
'?';
357 color,
"%s%-3lu [%p] (%s) %s\n", color >= 0 ?
ZTH_DBG_PREFIX :
"",
358 (
unsigned long)index, addr, file && *file ? file :
"??", has_func ? func :
"??");
361static bool bt_addr2line_has_symbol(
char const* func,
char const* file)
363 bool has_func = func && *func && func[0] !=
'?';
364 bool has_file = file && *file && file[0] !=
'?';
365 return has_func || has_file;
368struct bt_addr2line_module_info {
370 uintptr_t runtime_base;
371 uintptr_t preferred_base;
374 bt_addr2line_module_info()
381struct bt_addr2line_process {
383# ifdef ZTH_OS_WINDOWS
393 bt_addr2line_process()
395# ifdef ZTH_OS_WINDOWS
397 , stdin_write(nullptr)
398 , stdout_read(nullptr)
410 bt_addr2line_symbol_map;
412# ifdef ZTH_OS_WINDOWS
414bt_addr2line_close_child_handles(HANDLE out_read, HANDLE out_write, HANDLE in_read, HANDLE in_write)
417 CloseHandle(out_read);
419 CloseHandle(out_write);
421 CloseHandle(in_read);
423 CloseHandle(in_write);
426static void bt_addr2line_stop(bt_addr2line_process& proc)
428 if(proc.stdin_write) {
429 CloseHandle(proc.stdin_write);
430 proc.stdin_write =
nullptr;
432 if(proc.stdout_read) {
433 CloseHandle(proc.stdout_read);
434 proc.stdout_read =
nullptr;
437 CloseHandle(proc.process);
438 proc.process =
nullptr;
440 proc.started =
false;
443static bool bt_addr2line_start(
char const* module, bt_addr2line_process& proc)
448 if(IsDebuggerPresent()) {
450 DWORD n = GetEnvironmentVariableA(
451 "ZTH_ADDR2LINE_UNDER_DEBUG", env, (DWORD)
sizeof(env));
453 && (env[0] ==
'1' || env[0] ==
'y' || env[0] ==
'Y' || env[0] ==
't'
454 || env[0] ==
'T' || env[0] ==
'o' || env[0] ==
'O');
459 char temp_path[MAX_PATH] = {};
460 char const* windows_cwd =
"C:\\";
461 DWORD temp_len = GetTempPathA((DWORD)
sizeof(temp_path), temp_path);
462 if(temp_len > 0 && temp_len <
sizeof(temp_path))
463 windows_cwd = temp_path;
465 SECURITY_ATTRIBUTES sa = {};
466 sa.nLength =
sizeof(sa);
467 sa.bInheritHandle = TRUE;
469 HANDLE child_stdout_read =
nullptr;
470 HANDLE child_stdout_write =
nullptr;
471 if(!CreatePipe(&child_stdout_read, &child_stdout_write, &sa, 0))
473 if(!SetHandleInformation(child_stdout_read, HANDLE_FLAG_INHERIT, 0)) {
474 bt_addr2line_close_child_handles(
475 child_stdout_read, child_stdout_write,
nullptr,
nullptr);
479 HANDLE child_stdin_read =
nullptr;
480 HANDLE child_stdin_write =
nullptr;
481 if(!CreatePipe(&child_stdin_read, &child_stdin_write, &sa, 0)) {
482 bt_addr2line_close_child_handles(
483 child_stdout_read, child_stdout_write,
nullptr,
nullptr);
486 if(!SetHandleInformation(child_stdin_write, HANDLE_FLAG_INHERIT, 0)) {
487 bt_addr2line_close_child_handles(
488 child_stdout_read, child_stdout_write, child_stdin_read, child_stdin_write);
492 STARTUPINFOA si = {};
494 si.dwFlags = STARTF_USESTDHANDLES;
495 si.hStdInput = child_stdin_read;
496 si.hStdOutput = child_stdout_write;
499 si.hStdError = child_stdout_write;
501 PROCESS_INFORMATION pi = {};
504 cmd_line.push_back(
'\0');
505 bool started = CreateProcessA(
506 nullptr, &cmd_line[0],
nullptr,
nullptr, TRUE, CREATE_NO_WINDOW,
507 nullptr, windows_cwd, &si, &pi)
513 shell_cmd_line.push_back(
'\0');
514 started = CreateProcessA(
515 nullptr, &shell_cmd_line[0],
nullptr,
nullptr, TRUE,
516 CREATE_NO_WINDOW,
nullptr, windows_cwd, &si, &pi)
521 bt_addr2line_close_child_handles(
522 child_stdout_read, child_stdout_write, child_stdin_read, child_stdin_write);
526 CloseHandle(pi.hThread);
527 CloseHandle(child_stdout_write);
528 CloseHandle(child_stdin_read);
530 proc.process = pi.hProcess;
531 proc.stdin_write = child_stdin_write;
532 proc.stdout_read = child_stdout_read;
537static bool bt_addr2line_write_all(bt_addr2line_process& proc,
char const* data,
size_t len)
540 while(total < (DWORD)len) {
543 proc.stdin_write, data + total, (DWORD)(len - total), &written,
nullptr)
551static bool bt_addr2line_read_line(bt_addr2line_process& proc,
char (&buf)[ADDR2LINE_BUF_SIZE])
554 while(len + 1 <
sizeof(buf)) {
557 if(!ReadFile(proc.stdout_read, &ch, 1, &read,
nullptr) || read == 0)
559 if(ch ==
'\n' || ch ==
'\r') {
570static void bt_addr2line_stop(bt_addr2line_process& proc)
572 if(proc.stdin_write >= 0) {
573 close(proc.stdin_write);
574 proc.stdin_write = -1;
576 if(proc.stdout_read >= 0) {
577 close(proc.stdout_read);
578 proc.stdout_read = -1;
582 (void)waitpid(proc.pid, &status, WNOHANG);
585 proc.started =
false;
588static bool bt_addr2line_start(
char const* module, bt_addr2line_process& proc)
590 int in_pipe[2] = {-1, -1};
591 int out_pipe[2] = {-1, -1};
592 if(pipe(in_pipe) || pipe(out_pipe)) {
606 dup2(in_pipe[0], STDIN_FILENO);
607 dup2(out_pipe[1], STDOUT_FILENO);
612 execlp(
"addr2line",
"addr2line",
"-e", module,
"-C",
"-f", (
char*)
nullptr);
626 proc.stdin_write = in_pipe[1];
627 proc.stdout_read = out_pipe[0];
632static bool bt_addr2line_write_all(bt_addr2line_process& proc,
char const* data,
size_t len)
635 ssize_t written =
write(proc.stdin_write, data, len);
639 len -= (size_t)written;
644static bool bt_addr2line_read_line(bt_addr2line_process& proc,
char (&buf)[ADDR2LINE_BUF_SIZE])
647 while(len + 1 <
sizeof(buf)) {
650 ssize_t r =
read(proc.stdout_read, &ch, 1);
653 if(ch ==
'\n' || ch ==
'\r') {
665static bool bt_addr2line_query(
666 char const* module, uintptr_t rel,
char (&func)[ADDR2LINE_BUF_SIZE],
667 char (&file)[ADDR2LINE_BUF_SIZE])
669 static bt_addr2line_process_map processes;
670 bt_addr2line_process& proc = processes[
zth::string(module)];
672 if(!proc.started && !bt_addr2line_start(module, proc))
676 int query_len = snprintf(query,
sizeof(query),
"0x%llx\n", (
unsigned long long)rel);
680 if(!bt_addr2line_write_all(proc, query, (
size_t)query_len)
681 || !bt_addr2line_read_line(proc, func) || !bt_addr2line_read_line(proc, file)) {
682 bt_addr2line_stop(proc);
683 if(!bt_addr2line_start(module, proc))
685 if(!bt_addr2line_write_all(proc, query, (
size_t)query_len)
686 || !bt_addr2line_read_line(proc, func) || !bt_addr2line_read_line(proc, file)) {
687 bt_addr2line_stop(proc);
695# ifdef ZTH_OS_WINDOWS
696static bool bt_get_preferred_base_from_file(HMODULE hmod, uintptr_t& preferred_base)
700 wchar_t module_path[MAX_PATH] = {};
701 if(!GetModuleFileNameW(
702 hmod, module_path, (DWORD)(
sizeof(module_path) /
sizeof(module_path[0]))))
705 HANDLE file = CreateFileW(
706 module_path, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_DELETE,
nullptr,
707 OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL,
nullptr);
708 if(file == INVALID_HANDLE_VALUE)
711 IMAGE_DOS_HEADER dos = {};
713 if(!ReadFile(file, &dos,
sizeof(dos), &bytes,
nullptr) || bytes !=
sizeof(dos)
714 || dos.e_magic != IMAGE_DOS_SIGNATURE) {
719 LARGE_INTEGER li = {};
720 li.QuadPart = dos.e_lfanew;
721 if(!SetFilePointerEx(file, li,
nullptr, FILE_BEGIN)) {
727 if(!ReadFile(file, &signature,
sizeof(signature), &bytes,
nullptr)
728 || bytes !=
sizeof(signature) || signature != IMAGE_NT_SIGNATURE) {
733 IMAGE_FILE_HEADER file_header = {};
734 if(!ReadFile(file, &file_header,
sizeof(file_header), &bytes,
nullptr)
735 || bytes !=
sizeof(file_header)) {
740 LARGE_INTEGER opt_header_pos = {};
741 if(!SetFilePointerEx(file, {}, &opt_header_pos, FILE_CURRENT)) {
747 if(!ReadFile(file, &magic,
sizeof(magic), &bytes,
nullptr) || bytes !=
sizeof(magic)) {
752 if(file_header.SizeOfOptionalHeader == 0) {
757 if(magic == IMAGE_NT_OPTIONAL_HDR64_MAGIC) {
758 IMAGE_OPTIONAL_HEADER64 opt = {};
759 if(!SetFilePointerEx(file, opt_header_pos,
nullptr, FILE_BEGIN)
760 || !ReadFile(file, &opt,
sizeof(opt), &bytes,
nullptr) || bytes !=
sizeof(opt)) {
764 preferred_base = (uintptr_t)opt.ImageBase;
765 }
else if(magic == IMAGE_NT_OPTIONAL_HDR32_MAGIC) {
766 IMAGE_OPTIONAL_HEADER32 opt = {};
767 if(!SetFilePointerEx(file, opt_header_pos,
nullptr, FILE_BEGIN)
768 || !ReadFile(file, &opt,
sizeof(opt), &bytes,
nullptr) || bytes !=
sizeof(opt)) {
772 preferred_base = (uintptr_t)opt.ImageBase;
779 return preferred_base != 0;
782static void bt_addr2line_resolve_module(
void const* addr, bt_addr2line_module_info& info)
784 static bt_addr2line_module_map module_cache;
787 info.runtime_base = 0;
788 info.preferred_base = 0;
791 HMODULE hmod =
nullptr;
792 if(GetModuleHandleExA(
793 GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS
794 | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT,
795 reinterpret_cast<LPCSTR
>(addr), &hmod)
797 uintptr_t runtime_base =
reinterpret_cast<uintptr_t
>(hmod);
798 bt_addr2line_module_map::iterator it = module_cache.find(runtime_base);
799 if(it != module_cache.end()) {
804 info.runtime_base = runtime_base;
805 info.base = runtime_base;
806 (void)bt_get_preferred_base_from_file(hmod, info.preferred_base);
807 if(!info.preferred_base) {
808 if(PIMAGE_NT_HEADERS nt = ImageNtHeader(hmod))
809 info.preferred_base =
810 static_cast<uintptr_t
>(nt->OptionalHeader.ImageBase);
812 info.module.resize(MAX_PATH);
814 GetModuleFileNameA(hmod, info.module.data(), (DWORD)info.module.size()));
815 module_cache[runtime_base] = info;
819 if(info.module.empty()) {
820 info.module.resize(MAX_PATH);
822 GetModuleFileNameA(
nullptr, info.module.data(), (DWORD)info.module.size()));
826static uintptr_t bt_addr2line_relocate_pc(uintptr_t pc, bt_addr2line_module_info
const& info)
828 if(info.runtime_base && info.preferred_base && pc >= info.runtime_base)
829 return pc - info.runtime_base + info.preferred_base;
830 if(info.base && pc >= info.base)
831 return pc - info.base;
835static void bt_addr2line_resolve_module(
void const* addr, bt_addr2line_module_info& info)
838 info.runtime_base = 0;
839 info.preferred_base = 0;
843 static bt_addr2line_module_map module_cache;
845 Dl_info dl_info = {};
846 if(dladdr(addr, &dl_info) && dl_info.dli_fbase && dl_info.dli_fname) {
847 uintptr_t base =
reinterpret_cast<uintptr_t
>(dl_info.dli_fbase);
848 bt_addr2line_module_map::iterator it = module_cache.find(base);
849 if(it != module_cache.end()) {
855 info.module = dl_info.dli_fname;
856 module_cache[base] = info;
863 if(info.module.empty())
864 info.module =
zth::format(
"/proc/%u/exe", (
unsigned)getpid());
868static uintptr_t bt_addr2line_relocate_pc(uintptr_t pc, bt_addr2line_module_info
const& info)
870 if(info.base && pc >= info.base)
871 return pc - info.base;
876static void bt_print_addr2line_unsafe(
size_t index,
void const* addr,
int color)
878 static bt_addr2line_symbol_map cache;
879 bt_addr2line_symbol_map::iterator it = cache.find(addr);
880 if(it != cache.end()) {
881 if(bt_addr2line_has_symbol(it->second.second.c_str(), it->second.first.c_str())) {
882 bt_print_addr2line_print(
883 index, addr, it->second.first.c_str(), it->second.second.c_str(),
891 uintptr_t pc =
reinterpret_cast<uintptr_t
>(addr);
892 bt_addr2line_module_info info = {};
893 bt_addr2line_resolve_module(addr, info);
894 uintptr_t rel = bt_addr2line_relocate_pc(pc, info);
896 static char func[ADDR2LINE_BUF_SIZE];
897 static char file[ADDR2LINE_BUF_SIZE];
898 if(!bt_addr2line_query(info.module.c_str(), rel, func, file)) {
904 if(bt_addr2line_has_symbol(func, file)) {
906 bt_print_addr2line_print(index, addr, file, func, color);
913static __attribute__((unused))
void bt_print_addr2line(
size_t index,
void const* addr,
int color)
916# if __cplusplus < 201103L
922 static std::mutex mtx;
923 std::lock_guard<std::mutex> lock(mtx);
924 bt_print_addr2line_unsafe(index, addr, color);
927 bt_print_addr2line_unsafe(index, addr, color);
931# define bt_print_addr2line bt_print_dl
940#if defined(ZTH_HAVE_LIBBACKTRACE) && !defined(CLANG_TIDY) && !defined(ZTH_BT_PRINT_NONE)
947struct bt_print_cb_data {
954bt_print_cb(
void* data, uintptr_t pc,
const char* filename,
int lineno,
const char* function)
956 bt_print_cb_data* cbdata =
static_cast<bt_print_cb_data*
>(data);
958 if(!filename || !*filename)
962 char* demangled = abi::__cxa_demangle(function,
nullptr,
nullptr, &status);
963 char const* sym = demangled ? demangled : function;
966 cbdata->color,
"%s%-3lu [%p] (%s:%d) %s\n",
967 cbdata->color >= 0 ?
ZTH_DBG_PREFIX :
"", (unsigned long)cbdata->index, (void*)pc,
968 filename, lineno, sym ? sym :
"??");
977 static backtrace_state*
const BT_STATE_ERROR = (backtrace_state*)-1;
980 bt_state = backtrace_create_state(
983 bt_state = BT_STATE_ERROR;
986 if(bt_state == BT_STATE_ERROR) {
991 bt_print_cb_data data = {index, color,
false};
993 bt_state,
reinterpret_cast<uintptr_t
>(addr), bt_print_cb,
nullptr, &data)
998# define bt_print_libbacktrace bt_print_addr2line
1001#ifdef ZTH_BT_PRINT_LIBBACKTRACE
1002# define bt_print bt_print_libbacktrace
1011#ifdef ZTH_BT_PRINT_WIN32
1012static void bt_print(
size_t index,
void const* addr,
int color)
1014 HANDLE process = GetCurrentProcess();
1015 if(!bt_win32_sym_init()) {
1020 unsigned char sym_buf[
sizeof(SYMBOL_INFO) + MAX_SYM_NAME] = {};
1021 SYMBOL_INFO* sym =
reinterpret_cast<SYMBOL_INFO*
>(sym_buf);
1022 sym->SizeOfStruct =
sizeof(SYMBOL_INFO);
1023 sym->MaxNameLen = MAX_SYM_NAME;
1025 DWORD64 addr64 =
reinterpret_cast<DWORD64
>(addr);
1026 DWORD64 sym_displacement = 0;
1027 if(!SymFromAddr(process, addr64, &sym_displacement, sym)) {
1032 char demangled_name[MAX_SYM_NAME] = {};
1033 char const* name = (sym->NameLen > 0 && sym->Name[0]) ? sym->Name :
"??";
1034 char* demangled_name_gnu =
nullptr;
1035 if(name !=
nullptr && name[0] !=
'\0' && name[0] !=
'?') {
1036 DWORD demangled_len = UnDecorateSymbolName(
1037 name, demangled_name, (DWORD)
sizeof(demangled_name), UNDNAME_COMPLETE);
1038 if(demangled_len > 0 && demangled_name[0] !=
'\0') {
1039 name = demangled_name;
1041 int demangle_status = -1;
1042 demangled_name_gnu =
1043 abi::__cxa_demangle(name,
nullptr,
nullptr, &demangle_status);
1044 if(demangle_status == 0 && demangled_name_gnu
1045 && demangled_name_gnu[0] !=
'\0')
1046 name = demangled_name_gnu;
1050 IMAGEHLP_LINE64 line = {};
1051 line.SizeOfStruct =
sizeof(IMAGEHLP_LINE64);
1052 DWORD line_displacement = 0;
1053 if(SymGetLineFromAddr64(process, addr64, &line_displacement, &line)) {
1055 color,
"%s%-3lu [%p] (%s:%lu) %s+0x%llx\n",
1057 line.FileName ? line.FileName :
"??", (
unsigned long)line.LineNumber, name,
1058 (
unsigned long long)sym_displacement);
1063 free(demangled_name_gnu);
1076Backtrace::Backtrace(
size_t skip,
size_t maxDepth) noexcept
1085 m_fiberId = m_fiber ? m_fiber->
id() : 0;
1088 bt_capture(*
this, skip, maxDepth);
1097void Backtrace::printPartial(
size_t start, ssize_t end,
int color)
const
1102 if(-end > (ssize_t)
bt().size())
1104 end = (ssize_t)
bt().size() + end;
1106 if(start > (
size_t)end)
1109 for(
size_t i = start; i <= (size_t)end; i++) {
1110 void const* addr =
bt()[i];
1114 bt_print(i, addr, color);
1121void Backtrace::print(
int color)
const
1127 color,
"%sBacktrace of fiber %p #%" PRIu64
":\n", color >= 0 ?
ZTH_DBG_PREFIX :
"",
1128 m_fiber, m_fiberId);
1138void Backtrace::printDelta(
Backtrace const& other,
int color)
const
1141 if(other.
t0() >
t0()) {
1148 if(
bt().empty() && other.
bt().empty()) {
1151 "%sExecution from fiber %p #%s snapshot to %p #%s snapshot took %s\n",
1170 Backtrace::bt_type
const& this_bt =
bt();
1171 Backtrace::bt_type
const& other_bt = other.
bt();
1172 size_t max_common = std::min(this_bt.size(), other_bt.size());
1173 while((
size_t)common < max_common
1174 && this_bt[this_bt.size() - 1 - (
size_t)common]
1175 == other_bt[other_bt.size() - 1 - (
size_t)common])
1180 color,
"%sExecution from fiber %p #%s:\n", color >= 0 ?
ZTH_DBG_PREFIX :
"",
#define bt_print_addr2line
void context_entry(zth::Context *context)
Entry point of the fiber.
#define bt_print_libbacktrace
static safe_ptr< singleton_type >::type instance() noexcept
Return the only instance of T within this thread.
Convenient wrapper around struct timespec that contains a time interval.
uint64_t id() const noexcept
The class that manages the fibers within this thread.
Fiber * currentFiber() const noexcept
char const * c_str() const
bt_type const & bt() const noexcept
void print(int color=-1) const
vector_type< void * >::type bt_type
bool truncated() const noexcept
void printDelta(Backtrace const &other, int color=-1) const
uint64_t fiberId() const noexcept
void printPartial(size_t start, ssize_t end=-1, int color=-1) const
Timestamp const & t1() const noexcept
Timestamp const & t0() const noexcept
void context_entry(zth::Context *context) noexcept
Entry point of the fiber.
int execlp(char const *file, char const *arg,...)
Start an external program.
ssize_t read(int fd, void *buf, size_t count)
Like normal read(), but forwards the poll() to the zth::Waiter in case it would block.
ssize_t write(int fd, void const *buf, size_t count)
Like normal write(), but forwards the poll() to the zth::Waiter in case it would block.
void log_color(int color, char const *fmt,...)
Logs a given printf()-like formatted string using an ANSI color code.
std::basic_string< char, std::char_traits< char >, Config::Allocator< char >::type > string
std::string type using Config::Allocator::type.
#define ZTH_TLS_DEFINE(type, var, init)
cow_string str(T value)
Returns an zth::string representation of the given value.
string format(char const *fmt,...)
Format like sprintf(), but save the result in an zth::string.
static bool const EnableThreads
Add (Worker) thread support when true.
std::map type using Config::Allocator::type.
std::map< Key, T, Compare, typename Config::Allocator< std::pair< const Key, T > >::type > type
#define ZTH_DBG_PREFIX
Prefix for every zth_dbg() call.