kit

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

backtrace.c (1971B)


      1 /*
      2  * __kit_backtrace -- freestanding call-stack capture via the frame-pointer
      3  * chain. See rt/include/kit/backtrace.h for the contract and doc/plan/
      4  * BACKTRACE.md for the design.
      5  *
      6  * kit keeps a frame pointer on every backend and never omits it, so each
      7  * prologue stores {saved_fp, saved_ra} and anchors the frame pointer at the
      8  * record. The layout is uniform across all kit targets:
      9  *
     10  *     fp[0] = caller's frame pointer   (saved fp)
     11  *     fp[1] = return address           (saved ra)
     12  *
     13  * Indexing a void** scales by the target pointer width, so the same two
     14  * indices are correct on 32- and 64-bit targets alike — no per-arch offset
     15  * table, no #ifdef cascade (per the RUNTIME.md "no target-dispatch ifdef"
     16  * rule). __builtin_frame_address(0) marks __kit_backtrace as reading its own
     17  * frame, which forces it to keep a valid record (it never degrades to a
     18  * frameless leaf), so the seed frame is always walkable.
     19  */
     20 #include <kit/backtrace.h>
     21 
     22 int __kit_backtrace(void** buf, int max, int skip) {
     23   void** fp;
     24   int n = 0;
     25   if (!buf || max <= 0) return 0;
     26   if (skip < 0) skip = 0;
     27 
     28   fp = (void**)__builtin_frame_address(0);
     29   while (fp) {
     30     void** next = (void**)fp[0]; /* saved caller frame pointer */
     31     void* ra = fp[1];            /* saved return address       */
     32 
     33     /* A real frame always has a non-null return address; a null one is the
     34      * synthetic stack origin (_start / runtime entry zeroes the record), so
     35      * stop without recording it. */
     36     if (!ra) break;
     37 
     38     if (skip > 0) {
     39       --skip;
     40     } else {
     41       if (n >= max) break;
     42       buf[n++] = ra;
     43     }
     44 
     45     /* The stack grows down, so a valid caller frame sits strictly above this
     46      * one. A null, non-increasing, or misaligned link is the chain terminator
     47      * (or garbage) — stop, which also bounds a runaway walk. */
     48     if (next <= fp) break;
     49     if (((unsigned long)(void*)next & (sizeof(void*) - 1u)) != 0u) break;
     50     fp = next;
     51   }
     52   return n;
     53 }