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