kit

kit
git clone https://git.ryansepassi.com/git/kit.git
Log | Files | Refs | README

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 }