kit

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

dbg.h (7368B)


      1 #ifndef KIT_DBG_INTERNAL_H
      2 #define KIT_DBG_INTERNAL_H
      3 
      4 /* Internal contracts for src/dbg/. The public KitDebugSession entries are
      5  * defined in session.c on top of these primitives; bp.c, step.c, mem.c, and
      6  * displaced.c own the target-independent session machinery. Per-arch debug
      7  * behavior is reached through ArchImpl.dbg. */
      8 
      9 #include <kit/dbg.h>
     10 #include <kit/dwarf.h>
     11 #include <kit/jit.h>
     12 
     13 #include "arch/arch.h"
     14 #include "core/arena.h"
     15 #include "core/core.h"
     16 
     17 #define DBG_SCRATCH_PAGE_SIZE 4096u
     18 #define DBG_BP_MAX_INSN_LEN ARCH_DBG_MAX_TRAP_BYTES
     19 #define DBG_BP_ID_INTERNAL_BASE 0x80000000u
     20 #define DBG_DISPLACED_SLOT_BYTES 64u
     21 
     22 /* Bridge into link_jit.c so the session can validate addresses and pick the
     23  * arch lifter without dragging LinkImage internals into src/dbg/. */
     24 int kit_jit_image_contains(KitJit*, uint64_t runtime_addr);
     25 KitArchKind kit_jit_image_arch(KitJit*);
     26 Compiler* kit_jit_compiler(KitJit*);
     27 /* The JIT image owns the execmem used to map its code/data; the debugger's
     28  * displaced-step scratch is allocated from the same pool. Implemented in
     29  * src/link/link_jit.c. */
     30 const KitExecMem* kit_jit_image_execmem(KitJit*);
     31 
     32 /* ---- breakpoint table ------------------------------------------------ */
     33 
     34 typedef struct DbgBp {
     35   uint64_t addr;
     36   u8 saved[DBG_BP_MAX_INSN_LEN];
     37   u32 saved_len;
     38   u32 refcount;
     39   u32 user_id; /* public handle returned to caller; 0 = unused slot */
     40   u8 enabled;
     41   u8 internal; /* set when the entry was armed by step.c */
     42   u16 pad;
     43   uint64_t hit_count;
     44   uint64_t skip_count;
     45   uint64_t max_hits;
     46   int (*condition)(void*, const KitUnwindFrame*);
     47   void* condition_user;
     48 } DbgBp;
     49 
     50 typedef struct DbgBpTable {
     51   DbgBp* slots;
     52   u32 cap;
     53   u32 count;
     54   u32 next_user_id;     /* monotonic, starts at 1 */
     55   u32 next_internal_id; /* monotonic, starts at DBG_BP_ID_INTERNAL_BASE */
     56 } DbgBpTable;
     57 
     58 struct KitDebugSession; /* fwd */
     59 
     60 void dbg_bp_init(struct KitDebugSession*);
     61 void dbg_bp_fini(struct KitDebugSession*);
     62 
     63 /* set/clear with the user-facing handle space. The internal variants are
     64  * used by step.c for one-shot temporaries. */
     65 KitStatus dbg_bp_set(struct KitDebugSession*, uint64_t addr, u32* id_out);
     66 KitStatus dbg_bp_set_spec(struct KitDebugSession*, const KitBreakpointSpec*,
     67                           u32* id_out);
     68 KitStatus dbg_bp_set_internal(struct KitDebugSession*, uint64_t addr,
     69                               u32* id_out);
     70 KitStatus dbg_bp_clear(struct KitDebugSession*, u32 id);
     71 
     72 /* Lookup at a PC. Returns the slot index + 1 (so 0 means "not patched");
     73  * the caller uses dbg_bp_at_index to fetch the entry. */
     74 u32 dbg_bp_lookup_index(struct KitDebugSession*, uint64_t addr);
     75 DbgBp* dbg_bp_at_index(struct KitDebugSession*, u32 idx);
     76 
     77 /* Memory-read fixup: if [addr, addr+n) overlaps any patched bp, write the
     78  * original bytes back into `buf` at the right offsets. */
     79 void dbg_bp_unpatch_read(struct KitDebugSession*, uint64_t addr, void* buf,
     80                          size_t n);
     81 
     82 /* ---- memory --------------------------------------------------------- */
     83 KitStatus dbg_mem_read(struct KitDebugSession*, uint64_t addr, void* dst,
     84                        size_t n);
     85 KitStatus dbg_mem_write(struct KitDebugSession*, uint64_t addr, const void* src,
     86                         size_t n);
     87 
     88 /* ---- displaced step ------------------------------------------------- */
     89 /* The session owns a single executable scratch region. The per-arch debug
     90  * vtable writes a fixed-up copy of the original insn plus a trap sentinel
     91  * into it; the worker is then resumed with PC pointing at the scratch entry.
     92  * The sentinel is patched into the bp table so the fault classifier recognizes
     93  * it and translates the stop back into MODE_DONE. */
     94 typedef struct DbgDisplaced {
     95   KitExecMemRegion region;
     96   int valid;
     97   uint64_t orig_pc;   /* original user PC of the insn being stepped */
     98   uint64_t return_pc; /* PC the shim's trap sentinel lives at */
     99   uint64_t fallthrough_pc;
    100   uint32_t internal_bp; /* id of the one-shot bp at return_pc */
    101 } DbgDisplaced;
    102 
    103 KitStatus dbg_displaced_init(struct KitDebugSession*);
    104 void dbg_displaced_fini(struct KitDebugSession*);
    105 
    106 /* Prepare an out-of-line single-step at `insn_pc`. Sets *new_pc to the
    107  * scratch entry the worker should branch to; arms an internal bp on the
    108  * shim's trap sentinel. Returns KIT_OK on success, KIT_UNSUPPORTED if the
    109  * insn family is not supported. */
    110 KitStatus dbg_displaced_prepare(struct KitDebugSession*, uint64_t insn_pc,
    111                                 uint64_t* new_pc);
    112 /* After the shim trap fires, finalize: clear the internal bp, restore the
    113  * user-visible PC to the decoded fallthrough PC (or leave a branch target
    114  * captured by the shim alone). */
    115 void dbg_displaced_finalize(struct KitDebugSession*);
    116 KitStatus dbg_arch_decode_insn(struct KitDebugSession*, uint64_t pc,
    117                                ArchDbgInsn* out);
    118 
    119 /* ---- step state machine --------------------------------------------- */
    120 KitStatus dbg_step_resume(struct KitDebugSession*, KitResumeMode mode);
    121 
    122 /* ---- session state -------------------------------------------------- */
    123 typedef enum DbgSessionState {
    124   DBG_STATE_IDLE = 0,    /* no worker call in flight */
    125   DBG_STATE_RUNNING = 1, /* worker has been signaled to run */
    126   DBG_STATE_STOPPED = 2, /* worker is parked, REPL may inspect */
    127   DBG_STATE_EXITED = 3,  /* worker entry returned */
    128 } DbgSessionState;
    129 
    130 struct KitDebugSession {
    131   KitJit* jit;
    132   Compiler* c;
    133   Heap* heap;
    134   const KitDbgOs* os;
    135   /* Borrowed from the KitJit. Used to allocate displaced-step scratch
    136    * (and only that — the JIT image itself was already mapped at link
    137    * time). NULL if the host's JitHost did not supply an execmem. */
    138   const KitExecMem* execmem;
    139   const ArchImpl* arch_impl;
    140   const ArchDbgOps* arch_dbg;
    141 
    142   /* worker thread + event handshake */
    143   void* worker;
    144   void* ev_resume;
    145   void* ev_stop;
    146   DbgSessionState state;
    147   u8 worker_alive;
    148   u8 worker_should_exit;
    149   u8 pad0[2];
    150 
    151   /* entry args set by _call */
    152   void* entry;
    153   KitEntryKind entry_kind;
    154   int entry_argc;
    155   char** entry_argv;
    156   uint64_t entry_u64_args[8];
    157   uint32_t entry_u64_nargs;
    158   uint64_t entry_u64_ret;
    159 
    160   /* current stop slot (filled by the fault handler / worker exit path) */
    161   KitStopInfo stop;
    162 
    163   /* pending resume directive (set by REPL before signaling ev_resume) */
    164   KitResumeMode pending_mode;
    165   uint64_t pending_pc_override; /* nonzero → write before resume */
    166   u8 pending_has_pc;
    167   u8 pending_step_pending; /* MODE_STEP_INSN in progress via displaced */
    168   u8 pad1[2];
    169 
    170   /* breakpoint table */
    171   DbgBpTable bps;
    172 
    173   /* displaced-step scratch */
    174   DbgDisplaced displaced;
    175 
    176   /* optional DWARF binding (caller-owned; needed for source-level steps) */
    177   KitDebugInfo* dwarf;
    178 
    179   /* Stop-epoch scratch for the symbolic layer (symbolic.c): KitDebugValue byte
    180    * storage plus backtrace/iterator allocations. Reset on every call/resume so
    181    * materialized values stay valid exactly until the next one. */
    182   Arena values;
    183 
    184   /* set by dbg_step_resume when it has already driven the worker through
    185    * its own signal/wait cycles; tells kit_dbg_session_resume not to
    186    * issue another resume. */
    187   u8 pending_done;
    188   u8 pad2[3];
    189 };
    190 
    191 /* internal helpers shared between session.c and step.c */
    192 KitStatus dbg_session_wait_stop(struct KitDebugSession*);
    193 KitStatus dbg_session_signal_resume(struct KitDebugSession*);
    194 
    195 #endif