kit

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

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 }