Zth (libzth)
Loading...
Searching...
No Matches
backtrace.cpp
Go to the documentation of this file.
1/*
2 * SPDX-FileCopyrightText: 2019-2026 Jochem Rutgers
3 *
4 * SPDX-License-Identifier: MPL-2.0
5 */
6
7#define UNW_LOCAL_ONLY
8
9#include <libzth/backtrace.h>
10
11#include <libzth/context.h>
12#include <libzth/worker.h>
13
14#if defined(ZTH_CONFIG_ENABLE_BACKTRACE) && !ZTH_CONFIG_ENABLE_BACKTRACE
15# define ZTH_BT_NONE
16#elif defined(ZTH_OS_WINDOWS)
17# define ZTH_BT_WIN32
18#elif defined(ZTH_HAVE_LIBUNWIND)
19# define ZTH_BT_LIBUNWIND
20#elif defined(ZTH_OS_MAC) && defined(ZTH_ARCH_ARM64)
21// macOS on ARM64 does not seem to support backtrace() in combination with ucontext.
22# define ZTH_BT_NONE
23#elif defined(ZTH_HAVE_EXECINFO)
24# define ZTH_BT_BACKTRACE
25#else
26# define ZTH_BT_NONE
27#endif
28
29#ifdef ZTH_BT_NONE
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
39#else
40# define ZTH_BT_PRINT_ADDR
41#endif
42
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
47# endif
48#endif
49
50extern "C" void context_entry(zth::Context* context);
51
52
53
55// Implement using Windows API
56//
57
58#ifdef ZTH_BT_WIN32
59# include <windows.h>
60
61# include <dbghelp.h>
62# if defined(__GNUG__)
63# include <cxxabi.h>
64# endif
65
66static bool bt_win32_sym_init()
67{
68 static bool sym_init = false;
69 if(sym_init)
70 return true;
71
72 HANDLE process = GetCurrentProcess();
73 SymSetOptions(
74 SYMOPT_DEFERRED_LOADS | SYMOPT_LOAD_LINES | SYMOPT_UNDNAME
75 | SYMOPT_FAIL_CRITICAL_ERRORS);
76
77 if(SymInitialize(process, nullptr, TRUE) == TRUE) {
78 sym_init = true;
79 return true;
80 }
81
82 // DbgHelp was likely initialized elsewhere in-process already.
83 if(GetLastError() == ERROR_INVALID_PARAMETER) {
84 sym_init = true;
85 return true;
86 }
87
88 return false;
89}
90
91static void bt_capture(zth::impl::Backtrace& bt, size_t skip, size_t maxDepth)
92{
93 bt.bt().clear();
94
95 HANDLE process = GetCurrentProcess();
96 if(!bt_win32_sym_init()) {
97 bt.truncated(true);
98 return;
99 }
100
101 CONTEXT context = {};
102 RtlCaptureContext(&context);
103
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;
115# else
116# error Unsupported architecture.
117# endif
118 stackFrame.AddrPC.Mode = AddrModeFlat;
119 stackFrame.AddrFrame.Mode = AddrModeFlat;
120 stackFrame.AddrStack.Mode = AddrModeFlat;
121
122 size_t depth = 0;
123 while(depth < maxDepth
124 && StackWalk64(
125 machineType, process, GetCurrentThread(), &stackFrame, &context, nullptr,
126 SymFunctionTableAccess64, SymGetModuleBase64, nullptr)) {
127 if(depth >= skip)
128 bt.bt().push_back((void*)stackFrame.AddrPC.Offset);
129 if(!stackFrame.AddrFrame.Offset
130 || stackFrame.AddrPC.Offset == reinterpret_cast<DWORD64>(&context_entry))
131 break;
132 depth++;
133 }
134}
135#endif // ZTH_BT_WIN32
136
137
138
140// Implement with libunwind
141//
142
143#ifdef ZTH_BT_LIBUNWIND
144# include <libunwind.h>
145
146static void bt_capture(zth::impl::Backtrace& bt, size_t skip, size_t maxDepth)
147{
148 bt.bt().clear();
149
150 unw_context_t uc;
151
152 if(unw_getcontext(&uc))
153 return;
154
155 unw_cursor_t cursor;
156 unw_init_local(&cursor, &uc);
157 size_t depth = 0;
158 for(size_t i = 0; i < skip && unw_step(&cursor) > 0; i++)
159 ;
160
161 unw_proc_info_t pip;
162
163 while(unw_step(&cursor) > 0 && depth < maxDepth) {
164 // cppcheck-suppress knownConditionTrueFalse
165 if(depth == 0) {
166 unw_word_t sp = 0;
167 unw_get_reg(&cursor, UNW_REG_SP, &sp);
168 }
169
170 unw_word_t ip = 0;
171 unw_get_reg(&cursor, UNW_REG_IP, &ip);
172 bt.bt().push_back(reinterpret_cast<void*>(ip)); // NOLINT
173
174 if(unw_get_proc_info(&cursor, &pip) == 0
175 && pip.start_ip == reinterpret_cast<unw_word_t>(&context_entry))
176 // Stop here, as we might get segfaults when passing the context_entry
177 // functions.
178 break;
179 }
180
181 bt.truncated(depth == maxDepth);
182}
183#endif // ZTH_BT_LIBUNWIND
184
185
186
188// Implement with backtrace()
189//
190
191#ifdef ZTH_BT_BACKTRACE
192# include <execinfo.h>
193
194static void bt_capture(zth::impl::Backtrace& bt, size_t skip, size_t maxDepth)
195{
197
198 b.resize(maxDepth);
199 b.resize((size_t)backtrace(b.data(), (int)maxDepth));
200 // NOLINTNEXTLINE(cppcoreguidelines-prefer-member-initializer)
201 bt.truncated(b.size() == maxDepth);
202
203 size_t dst = 0;
204 size_t src = skip;
205
206 for(; src < b.size(); src++, dst++) {
207 b[dst] = b[src];
208 if(!b[dst] || b[dst] == reinterpret_cast<void*>(&context_entry))
209 break;
210 }
211
212 b.resize(dst);
213}
214#endif // ZTH_BT_BACKTRACE
215
216
218// No support for capturing backtraces.
219//
220
221#ifdef ZTH_BT_NONE
222static void bt_capture(zth::impl::Backtrace& bt, size_t skip, size_t maxDepth)
223{
224 (void)skip;
225 (void)maxDepth;
226 bt.truncated(true);
227}
228#endif // ZTH_BT_NONE
229
230
231
233// No printing.
234//
235
236#ifdef ZTH_BT_PRINT_NONE
237static __attribute__((unused)) void bt_print(size_t index, void const* addr, int color)
238{
239 (void)index;
240 (void)addr;
241 (void)color;
242}
243#endif // ZTH_BT_PRINT_NONE
244
245
246
248// Print plain addresses
249//
250
251static __attribute__((unused)) void bt_print_addr(size_t index, void const* addr, int color)
252{
254 color, "%s%-3lu [%p]\n", color >= 0 ? ZTH_DBG_PREFIX : "", (unsigned long)index,
255 addr);
256}
257
258#ifdef ZTH_BT_PRINT_ADDR
259# define bt_print bt_print_addr
260#endif // ZTH_BT_PRINT_ADDR
261
262
263
265// Print basic symbols
266//
267
268#if defined(ZTH_HAVE_EXECINFO) && !defined(ZTH_BT_PRINT_NONE)
269// NOLINTNEXTLINE(readability-duplicate-include)
270# include <execinfo.h>
271
272static __attribute__((unused)) void bt_print_symbols(size_t index, void const* addr, int color)
273{
274 char** symbols = backtrace_symbols((void* const*)&addr, 1); // NOLINT
275 char const* sym = symbols ? symbols[0] : nullptr;
276
277 if(!sym || !*sym) {
278 bt_print_addr(index, addr, color);
279 } else {
281 color, "%s%-3lu %s\n", color >= 0 ? ZTH_DBG_PREFIX : "",
282 (unsigned long)index, sym);
283 }
284
285 free(symbols); // NOLINT
286}
287#else
288# define bt_print_symbols bt_print_addr
289#endif // ZTH_HAVE_EXECINFO
290
291#ifdef ZTH_BT_PRINT_SYMBOLS
292# define bt_print bt_print_symbols
293#endif // ZTH_BT_PRINT_SYMBOLS
294
295
296
298// Print symbols with dladdr()
299//
300
301#if defined(ZTH_HAVE_DL) && !defined(ZTH_BT_PRINT_NONE)
302# include <cxxabi.h>
303# include <dlfcn.h>
304
305static __attribute__((unused)) void bt_print_dl(size_t index, void const* addr, int color)
306{
307 Dl_info info = {};
308 if(!dladdr(addr, &info))
309 return;
310
311 int status = -1;
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), // NOLINT
318 demangled ? demangled : "??");
319 } else {
320 bt_print_symbols(index, addr, color);
321 }
322
323 free(demangled); // NOLINT
324}
325#else
326# define bt_print_dl bt_print_symbols
327#endif // ZTH_HAVE_DL
328
329#ifdef ZTH_BT_PRINT_DL
330# define bt_print bt_print_dl
331#endif // ZTH_BT_PRINT_DL
332
333
334
336// Use addr2line to print symbols
337//
338
339#if defined(ZTH_BT_ADDR2LINE) && !defined(ZTH_BT_PRINT_NONE)
340# if __cplusplus >= 201103L
341# include <mutex>
342# endif
343# ifdef ZTH_OS_POSIX
344# include <csignal>
345# include <sys/types.h>
346# include <sys/wait.h>
347# include <unistd.h>
348# endif
349
350# define ADDR2LINE_BUF_SIZE 1024
351
352static void bt_print_addr2line_print(
353 size_t index, void const* addr, char const* file, char const* func, int color)
354{
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 : "??");
359}
360
361static bool bt_addr2line_has_symbol(char const* func, char const* file)
362{
363 bool has_func = func && *func && func[0] != '?';
364 bool has_file = file && *file && file[0] != '?';
365 return has_func || has_file;
366}
367
368struct bt_addr2line_module_info {
369 uintptr_t base;
370 uintptr_t runtime_base;
371 uintptr_t preferred_base;
372 zth::string module;
373
374 bt_addr2line_module_info()
375 : base(0)
376 , runtime_base(0)
377 , preferred_base(0)
378 {}
379};
380
381struct bt_addr2line_process {
382 bool started;
383# ifdef ZTH_OS_WINDOWS
384 HANDLE process;
385 HANDLE stdin_write;
386 HANDLE stdout_read;
387# else
388 pid_t pid;
389 int stdin_write;
390 int stdout_read;
391# endif
392
393 bt_addr2line_process()
394 : started(false)
395# ifdef ZTH_OS_WINDOWS
396 , process(nullptr)
397 , stdin_write(nullptr)
398 , stdout_read(nullptr)
399# else
400 , pid(-1)
401 , stdin_write(-1)
402 , stdout_read(-1)
403# endif
404 {}
405};
406
407typedef zth::map_type<zth::string, bt_addr2line_process>::type bt_addr2line_process_map;
410 bt_addr2line_symbol_map;
411
412# ifdef ZTH_OS_WINDOWS
413static void
414bt_addr2line_close_child_handles(HANDLE out_read, HANDLE out_write, HANDLE in_read, HANDLE in_write)
415{
416 if(out_read)
417 CloseHandle(out_read);
418 if(out_write)
419 CloseHandle(out_write);
420 if(in_read)
421 CloseHandle(in_read);
422 if(in_write)
423 CloseHandle(in_write);
424}
425
426static void bt_addr2line_stop(bt_addr2line_process& proc)
427{
428 if(proc.stdin_write) {
429 CloseHandle(proc.stdin_write);
430 proc.stdin_write = nullptr;
431 }
432 if(proc.stdout_read) {
433 CloseHandle(proc.stdout_read);
434 proc.stdout_read = nullptr;
435 }
436 if(proc.process) {
437 CloseHandle(proc.process);
438 proc.process = nullptr;
439 }
440 proc.started = false;
441}
442
443static bool bt_addr2line_start(char const* module, bt_addr2line_process& proc)
444{
445 // Under Windows debuggers we can hit noisy first-chance faults inside CreateProcessA even
446 // when execution would continue. Skip external addr2line in that case and let callers fall
447 // back to the native symbol resolution paths.
448 if(IsDebuggerPresent()) {
449 char env[16] = {};
450 DWORD n = GetEnvironmentVariableA(
451 "ZTH_ADDR2LINE_UNDER_DEBUG", env, (DWORD)sizeof(env));
452 bool allow = n > 0
453 && (env[0] == '1' || env[0] == 'y' || env[0] == 'Y' || env[0] == 't'
454 || env[0] == 'T' || env[0] == 'o' || env[0] == 'O');
455 if(!allow)
456 return false;
457 }
458
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;
464
465 SECURITY_ATTRIBUTES sa = {};
466 sa.nLength = sizeof(sa);
467 sa.bInheritHandle = TRUE;
468
469 HANDLE child_stdout_read = nullptr;
470 HANDLE child_stdout_write = nullptr;
471 if(!CreatePipe(&child_stdout_read, &child_stdout_write, &sa, 0))
472 return false;
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);
476 return false;
477 }
478
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);
484 return false;
485 }
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);
489 return false;
490 }
491
492 STARTUPINFOA si = {};
493 si.cb = sizeof(si);
494 si.dwFlags = STARTF_USESTDHANDLES;
495 si.hStdInput = child_stdin_read;
496 si.hStdOutput = child_stdout_write;
497 // Use the known inheritable pipe handle instead of inheriting stderr from the parent.
498 // This avoids debugger-only first-chance faults when parent stderr is unavailable.
499 si.hStdError = child_stdout_write;
500
501 PROCESS_INFORMATION pi = {};
502 zth::string cmd = zth::string("addr2line -e \"") + module + "\" -C -f";
503 zth::string cmd_line = cmd;
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)
508 == TRUE;
509
510 if(!started) {
511 zth::string shell_cmd = zth::string("cmd.exe /c ") + cmd;
512 zth::string shell_cmd_line = shell_cmd;
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)
517 == TRUE;
518 }
519
520 if(!started) {
521 bt_addr2line_close_child_handles(
522 child_stdout_read, child_stdout_write, child_stdin_read, child_stdin_write);
523 return false;
524 }
525
526 CloseHandle(pi.hThread);
527 CloseHandle(child_stdout_write);
528 CloseHandle(child_stdin_read);
529
530 proc.process = pi.hProcess;
531 proc.stdin_write = child_stdin_write;
532 proc.stdout_read = child_stdout_read;
533 proc.started = true;
534 return true;
535}
536
537static bool bt_addr2line_write_all(bt_addr2line_process& proc, char const* data, size_t len)
538{
539 DWORD total = 0;
540 while(total < (DWORD)len) {
541 DWORD written = 0;
542 if(!WriteFile(
543 proc.stdin_write, data + total, (DWORD)(len - total), &written, nullptr)
544 || written == 0)
545 return false;
546 total += written;
547 }
548 return true;
549}
550
551static bool bt_addr2line_read_line(bt_addr2line_process& proc, char (&buf)[ADDR2LINE_BUF_SIZE])
552{
553 size_t len = 0;
554 while(len + 1 < sizeof(buf)) {
555 char ch = '\0';
556 DWORD read = 0;
557 if(!ReadFile(proc.stdout_read, &ch, 1, &read, nullptr) || read == 0)
558 break;
559 if(ch == '\n' || ch == '\r') {
560 if(len == 0)
561 continue;
562 break;
563 }
564 buf[len++] = ch;
565 }
566 buf[len] = '\0';
567 return len > 0;
568}
569# else // !ZTH_OS_WINDOWS
570static void bt_addr2line_stop(bt_addr2line_process& proc)
571{
572 if(proc.stdin_write >= 0) {
573 close(proc.stdin_write);
574 proc.stdin_write = -1;
575 }
576 if(proc.stdout_read >= 0) {
577 close(proc.stdout_read);
578 proc.stdout_read = -1;
579 }
580 if(proc.pid > 0) {
581 int status = 0;
582 (void)waitpid(proc.pid, &status, WNOHANG);
583 proc.pid = -1;
584 }
585 proc.started = false;
586}
587
588static bool bt_addr2line_start(char const* module, bt_addr2line_process& proc)
589{
590 int in_pipe[2] = {-1, -1};
591 int out_pipe[2] = {-1, -1};
592 if(pipe(in_pipe) || pipe(out_pipe)) {
593 if(in_pipe[0] >= 0)
594 close(in_pipe[0]);
595 if(in_pipe[1] >= 0)
596 close(in_pipe[1]);
597 if(out_pipe[0] >= 0)
598 close(out_pipe[0]);
599 if(out_pipe[1] >= 0)
600 close(out_pipe[1]);
601 return false;
602 }
603
604 pid_t pid = fork();
605 if(pid == 0) {
606 dup2(in_pipe[0], STDIN_FILENO);
607 dup2(out_pipe[1], STDOUT_FILENO);
608 close(in_pipe[0]);
609 close(in_pipe[1]);
610 close(out_pipe[0]);
611 close(out_pipe[1]);
612 execlp("addr2line", "addr2line", "-e", module, "-C", "-f", (char*)nullptr);
613 _exit(127);
614 }
615
616 close(in_pipe[0]);
617 close(out_pipe[1]);
618
619 if(pid < 0) {
620 close(in_pipe[1]);
621 close(out_pipe[0]);
622 return false;
623 }
624
625 proc.pid = pid;
626 proc.stdin_write = in_pipe[1];
627 proc.stdout_read = out_pipe[0];
628 proc.started = true;
629 return true;
630}
631
632static bool bt_addr2line_write_all(bt_addr2line_process& proc, char const* data, size_t len)
633{
634 while(len > 0) {
635 ssize_t written = write(proc.stdin_write, data, len);
636 if(written <= 0)
637 return false;
638 data += written;
639 len -= (size_t)written;
640 }
641 return true;
642}
643
644static bool bt_addr2line_read_line(bt_addr2line_process& proc, char (&buf)[ADDR2LINE_BUF_SIZE])
645{
646 size_t len = 0;
647 while(len + 1 < sizeof(buf)) {
648 char ch = '\0';
649 // NOLINTNEXTLINE(clang-analyzer-unix.BlockInCriticalSection)
650 ssize_t r = read(proc.stdout_read, &ch, 1);
651 if(r <= 0)
652 break;
653 if(ch == '\n' || ch == '\r') {
654 if(len == 0)
655 continue;
656 break;
657 }
658 buf[len++] = ch;
659 }
660 buf[len] = '\0';
661 return len > 0;
662}
663# endif // !ZTH_OS_WINDOWS
664
665static bool bt_addr2line_query(
666 char const* module, uintptr_t rel, char (&func)[ADDR2LINE_BUF_SIZE],
667 char (&file)[ADDR2LINE_BUF_SIZE])
668{
669 static bt_addr2line_process_map processes;
670 bt_addr2line_process& proc = processes[zth::string(module)];
671
672 if(!proc.started && !bt_addr2line_start(module, proc))
673 return false;
674
675 char query[64];
676 int query_len = snprintf(query, sizeof(query), "0x%llx\n", (unsigned long long)rel);
677 if(query_len <= 0)
678 return false;
679
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))
684 return false;
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);
688 return false;
689 }
690 }
691
692 return true;
693}
694
695# ifdef ZTH_OS_WINDOWS
696static bool bt_get_preferred_base_from_file(HMODULE hmod, uintptr_t& preferred_base)
697{
698 preferred_base = 0;
699
700 wchar_t module_path[MAX_PATH] = {};
701 if(!GetModuleFileNameW(
702 hmod, module_path, (DWORD)(sizeof(module_path) / sizeof(module_path[0]))))
703 return false;
704
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)
709 return false;
710
711 IMAGE_DOS_HEADER dos = {};
712 DWORD bytes = 0;
713 if(!ReadFile(file, &dos, sizeof(dos), &bytes, nullptr) || bytes != sizeof(dos)
714 || dos.e_magic != IMAGE_DOS_SIGNATURE) {
715 CloseHandle(file);
716 return false;
717 }
718
719 LARGE_INTEGER li = {};
720 li.QuadPart = dos.e_lfanew;
721 if(!SetFilePointerEx(file, li, nullptr, FILE_BEGIN)) {
722 CloseHandle(file);
723 return false;
724 }
725
726 DWORD signature = 0;
727 if(!ReadFile(file, &signature, sizeof(signature), &bytes, nullptr)
728 || bytes != sizeof(signature) || signature != IMAGE_NT_SIGNATURE) {
729 CloseHandle(file);
730 return false;
731 }
732
733 IMAGE_FILE_HEADER file_header = {};
734 if(!ReadFile(file, &file_header, sizeof(file_header), &bytes, nullptr)
735 || bytes != sizeof(file_header)) {
736 CloseHandle(file);
737 return false;
738 }
739
740 LARGE_INTEGER opt_header_pos = {};
741 if(!SetFilePointerEx(file, {}, &opt_header_pos, FILE_CURRENT)) {
742 CloseHandle(file);
743 return false;
744 }
745
746 WORD magic = 0;
747 if(!ReadFile(file, &magic, sizeof(magic), &bytes, nullptr) || bytes != sizeof(magic)) {
748 CloseHandle(file);
749 return false;
750 }
751
752 if(file_header.SizeOfOptionalHeader == 0) {
753 CloseHandle(file);
754 return false;
755 }
756
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)) {
761 CloseHandle(file);
762 return false;
763 }
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)) {
769 CloseHandle(file);
770 return false;
771 }
772 preferred_base = (uintptr_t)opt.ImageBase;
773 } else {
774 CloseHandle(file);
775 return false;
776 }
777
778 CloseHandle(file);
779 return preferred_base != 0;
780}
781
782static void bt_addr2line_resolve_module(void const* addr, bt_addr2line_module_info& info)
783{
784 static bt_addr2line_module_map module_cache;
785
786 info.base = 0;
787 info.runtime_base = 0;
788 info.preferred_base = 0;
789 info.module.clear();
790
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)
796 && 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()) {
800 info = it->second;
801 return;
802 }
803
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);
811 }
812 info.module.resize(MAX_PATH);
813 info.module.resize(
814 GetModuleFileNameA(hmod, info.module.data(), (DWORD)info.module.size()));
815 module_cache[runtime_base] = info;
816 return;
817 }
818
819 if(info.module.empty()) {
820 info.module.resize(MAX_PATH);
821 info.module.resize(
822 GetModuleFileNameA(nullptr, info.module.data(), (DWORD)info.module.size()));
823 }
824}
825
826static uintptr_t bt_addr2line_relocate_pc(uintptr_t pc, bt_addr2line_module_info const& info)
827{
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;
832 return pc;
833}
834# else // !ZTH_OS_WINDOWS
835static void bt_addr2line_resolve_module(void const* addr, bt_addr2line_module_info& info)
836{
837 info.base = 0;
838 info.runtime_base = 0;
839 info.preferred_base = 0;
840 info.module.clear();
841
842# ifdef ZTH_HAVE_DL
843 static bt_addr2line_module_map module_cache;
844
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()) {
850 info = it->second;
851 return;
852 }
853
854 info.base = base;
855 info.module = dl_info.dli_fname;
856 module_cache[base] = info;
857 return;
858 }
859# endif // ZTH_HAVE_DL
860
861# ifdef ZTH_OS_POSIX
862 // cppcheck-suppress knownConditionTrueFalse
863 if(info.module.empty())
864 info.module = zth::format("/proc/%u/exe", (unsigned)getpid());
865# endif // ZTH_OS_POSIX
866}
867
868static uintptr_t bt_addr2line_relocate_pc(uintptr_t pc, bt_addr2line_module_info const& info)
869{
870 if(info.base && pc >= info.base)
871 return pc - info.base;
872 return pc;
873}
874# endif // !ZTH_OS_WINDOWS
875
876static void bt_print_addr2line_unsafe(size_t index, void const* addr, int color)
877{
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(),
884 color);
885 } else {
886 bt_print_dl(index, addr, color);
887 }
888 return;
889 }
890
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);
895
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)) {
899 cache[addr] = std::make_pair(zth::string(), zth::string());
900 bt_print_dl(index, addr, color);
901 return;
902 }
903
904 if(bt_addr2line_has_symbol(func, file)) {
905 cache[addr] = std::make_pair(zth::string(file), zth::string(func));
906 bt_print_addr2line_print(index, addr, file, func, color);
907 } else {
908 cache[addr] = std::make_pair(zth::string(), zth::string());
909 bt_print_dl(index, addr, color);
910 }
911}
912
913static __attribute__((unused)) void bt_print_addr2line(size_t index, void const* addr, int color)
914{
916# if __cplusplus < 201103L
917 // No mutex support.
918 return;
919# else // C++11
920
921 // addr2line is not thread-safe, so we need to serialize calls to it.
922 static std::mutex mtx;
923 std::lock_guard<std::mutex> lock(mtx);
924 bt_print_addr2line_unsafe(index, addr, color);
925# endif // < C++11
926 } else {
927 bt_print_addr2line_unsafe(index, addr, color);
928 }
929}
930#else // !ZTH_BT_ADDR2LINE
931# define bt_print_addr2line bt_print_dl
932#endif // ZTH_BT_ADDR2LINE
933
934
935
937// Print symbols with libbacktrace
938//
939
940#if defined(ZTH_HAVE_LIBBACKTRACE) && !defined(CLANG_TIDY) && !defined(ZTH_BT_PRINT_NONE)
941# include <backtrace.h>
942// NOLINTNEXTLINE(readability-duplicate-include)
943# include <cxxabi.h>
944
945ZTH_TLS_DEFINE(backtrace_state*, bt_state, nullptr);
946
947struct bt_print_cb_data {
948 size_t index;
949 int color;
950 bool ok;
951};
952
953static int
954bt_print_cb(void* data, uintptr_t pc, const char* filename, int lineno, const char* function)
955{
956 bt_print_cb_data* cbdata = static_cast<bt_print_cb_data*>(data);
957
958 if(!filename || !*filename)
959 return 1;
960
961 int status = -1;
962 char* demangled = abi::__cxa_demangle(function, nullptr, nullptr, &status);
963 char const* sym = demangled ? demangled : function;
964
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 : "??");
969
970 free(demangled); // NOLINT
971 cbdata->ok = true;
972 return 0;
973}
974
975static void bt_print_libbacktrace(size_t index, void const* addr, int color)
976{
977 static backtrace_state* const BT_STATE_ERROR = (backtrace_state*)-1;
978
979 if(!bt_state) {
980 bt_state = backtrace_create_state(
981 nullptr, (int)zth::Config::EnableThreads, nullptr, nullptr);
982 if(!bt_state)
983 bt_state = BT_STATE_ERROR;
984 }
985
986 if(bt_state == BT_STATE_ERROR) {
987 bt_print_addr2line(index, addr, color);
988 return;
989 }
990
991 bt_print_cb_data data = {index, color, false};
992 if(backtrace_pcinfo(
993 bt_state, reinterpret_cast<uintptr_t>(addr), bt_print_cb, nullptr, &data)
994 || !data.ok)
995 bt_print_addr2line(index, addr, color);
996}
997#else // !ZTH_HAVE_LIBBACKTRACE
998# define bt_print_libbacktrace bt_print_addr2line
999#endif // !ZTH_HAVE_LIBBACKTRACE
1000
1001#ifdef ZTH_BT_PRINT_LIBBACKTRACE
1002# define bt_print bt_print_libbacktrace
1003#endif // ZTH_BT_PRINT_LIBBACKTRACE
1004
1005
1006
1008// Print symbols with Windows API
1009//
1010
1011#ifdef ZTH_BT_PRINT_WIN32
1012static void bt_print(size_t index, void const* addr, int color)
1013{
1014 HANDLE process = GetCurrentProcess();
1015 if(!bt_win32_sym_init()) {
1016 bt_print_libbacktrace(index, addr, color);
1017 return;
1018 }
1019
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;
1024
1025 DWORD64 addr64 = reinterpret_cast<DWORD64>(addr);
1026 DWORD64 sym_displacement = 0;
1027 if(!SymFromAddr(process, addr64, &sym_displacement, sym)) {
1028 bt_print_libbacktrace(index, addr, color);
1029 return;
1030 }
1031
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;
1040 } else {
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;
1047 }
1048 }
1049
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",
1056 color >= 0 ? ZTH_DBG_PREFIX : "", (unsigned long)index, addr,
1057 line.FileName ? line.FileName : "??", (unsigned long)line.LineNumber, name,
1058 (unsigned long long)sym_displacement);
1059 } else {
1060 bt_print_libbacktrace(index, addr, color);
1061 }
1062
1063 free(demangled_name_gnu); // NOLINT
1064}
1065#endif // ZTH_BT_PRINT_WIN32
1066
1067
1069// Backtrace wrapper
1070//
1071
1072namespace zth {
1073namespace impl {
1074
1075// NOLINTNEXTLINE(bugprone-easily-swappable-parameters)
1076Backtrace::Backtrace(size_t skip, size_t maxDepth) noexcept
1077 : m_t0(Timestamp::now())
1078 , m_fiber()
1079 , m_fiberId()
1080 , m_truncated()
1081{
1082 Worker const* worker = Worker::instance();
1083 m_fiber = worker ? worker->currentFiber() : nullptr;
1084 // NOLINTNEXTLINE(cppcoreguidelines-prefer-member-initializer)
1085 m_fiberId = m_fiber ? m_fiber->id() : 0;
1086
1087 try {
1088 bt_capture(*this, skip, maxDepth);
1089 } catch(...) {
1090 bt().clear();
1091 truncated(true);
1092 }
1093
1094 m_t1 = Timestamp::now();
1095}
1096
1097void Backtrace::printPartial(size_t start, ssize_t end, int color) const
1098{
1099 if(bt().empty())
1100 return;
1101 if(end < 0) {
1102 if(-end > (ssize_t)bt().size())
1103 return;
1104 end = (ssize_t)bt().size() + end;
1105 }
1106 if(start > (size_t)end)
1107 return;
1108
1109 for(size_t i = start; i <= (size_t)end; i++) {
1110 void const* addr = bt()[i];
1111 if(!addr)
1112 break;
1113
1114 bt_print(i, addr, color);
1115
1116 if(addr == reinterpret_cast<void*>(&context_entry))
1117 break;
1118 }
1119}
1120
1121void Backtrace::print(int color) const
1122{
1123 if(bt().empty())
1124 return;
1125
1126 log_color(
1127 color, "%sBacktrace of fiber %p #%" PRIu64 ":\n", color >= 0 ? ZTH_DBG_PREFIX : "",
1128 m_fiber, m_fiberId);
1129
1130 printPartial(0, (ssize_t)bt().size() - 1, color);
1131
1132 if(truncated())
1133 log_color(color, "%s<truncated>\n", color >= 0 ? ZTH_DBG_PREFIX : "");
1134 else
1135 log_color(color, "%s<end>\n", color >= 0 ? ZTH_DBG_PREFIX : "");
1136}
1137
1138void Backtrace::printDelta(Backtrace const& other, int color) const
1139{
1140 // Make sure other was first.
1141 if(other.t0() > t0()) {
1142 other.printDelta(*this, color);
1143 return;
1144 }
1145
1146 TimeInterval dt = t0() - other.t1();
1147
1148 if(bt().empty() && other.bt().empty()) {
1149 log_color(
1150 color,
1151 "%sExecution from fiber %p #%s snapshot to %p #%s snapshot took %s\n",
1152 color >= 0 ? ZTH_DBG_PREFIX : "", other.m_fiber,
1153 str(other.m_fiberId).c_str(), m_fiber, str(m_fiberId).c_str(),
1154 dt.str().c_str());
1155 return;
1156 }
1157
1158 if(other.fiberId() != fiberId() || other.truncated() || truncated()) {
1159 log_color(color, "%sExecuted from:\n", color >= 0 ? ZTH_DBG_PREFIX : "");
1160 other.print(color);
1161 log_color(color, "%sto:\n", color >= 0 ? ZTH_DBG_PREFIX : "");
1162 print(color);
1163 log_color(color, "%stook %s\n", color >= 0 ? ZTH_DBG_PREFIX : "", dt.str().c_str());
1164 return;
1165 }
1166
1167 // Find common base
1168 ssize_t common = 0;
1169 {
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])
1176 common++;
1177 }
1178
1179 log_color(
1180 color, "%sExecution from fiber %p #%s:\n", color >= 0 ? ZTH_DBG_PREFIX : "",
1181 m_fiber, str(m_fiberId).c_str());
1182 other.printPartial(0, -common - 1, color);
1183 log_color(color, "%sto:\n", color >= 0 ? ZTH_DBG_PREFIX : "");
1184 printPartial(0, -common - 1, color);
1185 if(common > 0) {
1186 log_color(color, "%shaving in common:\n", color >= 0 ? ZTH_DBG_PREFIX : "");
1187 other.printPartial(other.bt().size() - (size_t)common, color);
1188 }
1189 log_color(color, "%stook %s\n", color >= 0 ? ZTH_DBG_PREFIX : "", dt.str().c_str());
1190}
1191
1192} // namespace impl
1193} // namespace zth
#define bt_print_addr2line
void context_entry(zth::Context *context)
Entry point of the fiber.
Definition context.cpp:164
#define bt_print_libbacktrace
#define bt_print_symbols
#define bt_print_dl
static safe_ptr< singleton_type >::type instance() noexcept
Return the only instance of T within this thread.
Definition util.h:1196
Convenient wrapper around struct timespec that contains a time interval.
Definition time.h:82
string str() const
Definition time.h:488
static Timestamp now()
Definition time.h:656
uint64_t id() const noexcept
Definition util.h:936
The class that manages the fibers within this thread.
Definition worker.h:35
Fiber * currentFiber() const noexcept
Definition worker.h:103
char const * c_str() const
Definition util.h:411
Save a backtrace.
Definition backtrace.h:92
bt_type const & bt() const noexcept
Definition backtrace.h:109
void print(int color=-1) const
vector_type< void * >::type bt_type
Definition backtrace.h:95
bool truncated() const noexcept
Definition backtrace.h:119
void printDelta(Backtrace const &other, int color=-1) const
uint64_t fiberId() const noexcept
Definition backtrace.h:104
void printPartial(size_t start, ssize_t end=-1, int color=-1) const
Timestamp const & t1() const noexcept
Definition backtrace.h:134
Timestamp const & t0() const noexcept
Definition backtrace.h:129
void context_entry(zth::Context *context) noexcept
Entry point of the fiber.
Definition context.cpp:164
int execlp(char const *file, char const *arg,...)
Start an external program.
Definition worker.cpp:103
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.
Definition io.cpp:24
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.
Definition io.cpp:52
void log_color(int color, char const *fmt,...)
Logs a given printf()-like formatted string using an ANSI color code.
Definition util.h:292
std::basic_string< char, std::char_traits< char >, Config::Allocator< char >::type > string
std::string type using Config::Allocator::type.
Definition util.h:330
#define ZTH_TLS_DEFINE(type, var, init)
Definition macros.h:101
cow_string str(T value)
Returns an zth::string representation of the given value.
Definition util.h:512
string format(char const *fmt,...)
Format like sprintf(), but save the result in an zth::string.
Definition util.h:498
static bool const EnableThreads
Add (Worker) thread support when true.
Definition config.h:106
std::map type using Config::Allocator::type.
Definition allocator.h:203
std::map< Key, T, Compare, typename Config::Allocator< std::pair< const Key, T > >::type > type
Definition allocator.h:206
#define ZTH_DBG_PREFIX
Prefix for every zth_dbg() call.
Definition util.h:182