backtrace.c (4888B)
1 #include "backtrace.h" 2 3 #include <kit/dbg.h> 4 #include <stdarg.h> 5 #include <stdio.h> 6 7 /* Frame-pointer register (DWARF index) and pointer width per arch. Only the 8 * native run/dbg targets are walkable; wasm and the 32-bit x86/arm hosts kit 9 * does not self-host on return "unsupported". The FP layout is uniform, so a 10 * single index pair drives the walk on every supported arch. */ 11 int driver_bt_fp_dwarf_reg(KitArchKind arch) { 12 switch (arch) { 13 case KIT_ARCH_ARM_64: 14 return 29; /* x29 */ 15 case KIT_ARCH_X86_64: 16 return 6; /* rbp (System V DWARF numbering) */ 17 case KIT_ARCH_RV32: 18 case KIT_ARCH_RV64: 19 return 8; /* s0 / fp */ 20 default: 21 return -1; 22 } 23 } 24 25 int driver_bt_ptr_size(KitArchKind arch) { 26 switch (arch) { 27 case KIT_ARCH_ARM_64: 28 case KIT_ARCH_X86_64: 29 case KIT_ARCH_RV64: 30 return 8; 31 case KIT_ARCH_RV32: 32 return 4; 33 default: 34 return 0; 35 } 36 } 37 38 int driver_bt_fp_step(KitArchKind arch, KitDwarfReadMemFn read, void* read_user, 39 uint64_t fp, uint64_t* ra_out, uint64_t* next_fp_out) { 40 int ptr = driver_bt_ptr_size(arch); 41 uint64_t ra = 0; /* zero-init: a `ptr`-byte read leaves the high bytes 0 */ 42 uint64_t nfp = 0; /* (hosts are little-endian) */ 43 uint64_t align; 44 45 if (ptr <= 0 || !read || fp == 0) return 0; 46 align = (uint64_t)ptr - 1u; 47 if (fp & align) return 0; /* misaligned current frame */ 48 49 /* fp[1] = saved return address, fp[0] = caller frame pointer. */ 50 if (read(read_user, fp + (uint64_t)ptr, &ra, (size_t)ptr) != KIT_OK) return 0; 51 if (read(read_user, fp, &nfp, (size_t)ptr) != KIT_OK) return 0; 52 53 if (ra == 0) return 0; /* synthetic stack origin */ 54 if (nfp <= fp) return 0; /* stack grows down: caller frame sits above */ 55 if (nfp & align) return 0; /* misaligned link — chain terminator/garbage */ 56 57 *ra_out = ra; 58 *next_fp_out = nfp; 59 return 1; 60 } 61 62 /* Bounded append into a fixed line buffer; silently truncates past the end. */ 63 static void bt_appendf(char* buf, size_t cap, size_t* len, const char* fmt, 64 ...) { 65 va_list ap; 66 int n; 67 if (*len >= cap) return; 68 va_start(ap, fmt); 69 n = vsnprintf(buf + *len, cap - *len, fmt, ap); 70 va_end(ap); 71 if (n < 0) return; 72 *len += (size_t)n; 73 if (*len >= cap) *len = cap - 1; /* clamp to keep the NUL terminator */ 74 } 75 76 /* Render one frame line: "#N 0xPC [<sym+off>] [at file:line[:col]]". 77 * 78 * Symbolization (jit symbol + DWARF func/line) comes from the public 79 * kit_dbg_symbolize_pc; this function only owns the line formatting. For a PC 80 * that doesn't translate into the kit image (libc/dyld trampolines) the frame 81 * carries in_image=false and prints bare, rather than mis-attributing it to the 82 * nearest symbol with a giant offset. */ 83 static void bt_render_frame(const DriverBtCtx* ctx, int level, uint64_t pc) { 84 char line[1024]; 85 size_t len = 0; 86 KitSlice name = KIT_SLICE_NULL; 87 uint64_t off = 0; 88 KitSlice file = KIT_SLICE_NULL; 89 uint32_t srcline = 0, col = 0; 90 int have = 0; 91 92 line[0] = '\0'; 93 bt_appendf(line, sizeof line, &len, "#%-2d 0x%llx", level, 94 (unsigned long long)pc); 95 96 if (ctx->jit) { 97 KitDebugFrame f; 98 if (kit_dbg_symbolize_pc(ctx->jit, ctx->dwarf, pc, &f) == KIT_OK && 99 f.in_image) { 100 name = f.sym.s ? f.sym : f.func; 101 off = f.sym.s ? f.sym_offset : f.func_offset; 102 file = f.file; 103 srcline = f.line; 104 col = f.col; 105 have = 1; 106 } 107 } else if (ctx->dwarf) { 108 /* No JIT image: treat the PC as already image-relative and consult DWARF 109 * directly. */ 110 KitSlice fn; 111 uint64_t lo = 0, hi = 0; 112 if (kit_dwarf_func_at(ctx->dwarf, pc, &fn, &lo, &hi) == KIT_OK && fn.s) { 113 name = fn; 114 off = (pc >= lo) ? (pc - lo) : 0; 115 } 116 (void)kit_dwarf_addr_to_line(ctx->dwarf, pc, &file, &srcline, &col); 117 have = 1; 118 } 119 120 if (have && name.s) { 121 if (off) 122 bt_appendf(line, sizeof line, &len, " <%.*s+0x%llx>", (int)name.len, 123 name.s, (unsigned long long)off); 124 else 125 bt_appendf(line, sizeof line, &len, " <%.*s>", (int)name.len, name.s); 126 } 127 if (have && file.s) { 128 bt_appendf(line, sizeof line, &len, " at %.*s:%u", (int)file.len, file.s, 129 srcline); 130 if (col) bt_appendf(line, sizeof line, &len, ":%u", col); 131 } 132 133 ctx->emit(ctx->emit_user, line); 134 } 135 136 void driver_backtrace_print_pcs(const DriverBtCtx* ctx, const uint64_t* pcs, 137 int n) { 138 int i; 139 if (!ctx || !ctx->emit || !pcs) return; 140 for (i = 0; i < n; ++i) { 141 /* Stop at the kit-image boundary: once the chain leaves into the host 142 * runtime trampoline / libc startup, the frames are unsymbolizable and 143 * their count is host-dependent. Frame #0 (the fault PC) always prints. */ 144 if (i > 0 && ctx->jit && kit_jit_runtime_to_image(ctx->jit, pcs[i]) == 0) 145 break; 146 bt_render_frame(ctx, i, pcs[i]); 147 } 148 }