kit

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

dbg.c (120329B)


      1 #include <kit/arch.h>
      2 #include <kit/compile.h>
      3 #include <kit/core.h>
      4 #include <kit/dbg.h>
      5 #include <kit/disasm.h>
      6 #include <kit/dwarf.h>
      7 #include <kit/jit.h>
      8 #include <kit/link.h>
      9 #include <kit/object.h>
     10 #include <stddef.h>
     11 #include <stdint.h>
     12 #include <string.h>
     13 
     14 #include "backtrace.h"
     15 #include "cflags.h"
     16 #include "driver.h"
     17 #include "inputs.h"
     18 
     19 /* `kit dbg` — interactive JIT debugger.
     20  *
     21  * Mirrors `kit run` for compile flags and argv shape, but instead of
     22  * calling the entry directly drops into a REPL that drives a
     23  * KitJitSession. The session (in libkit) owns the worker thread,
     24  * signal handlers, breakpoint patcher, and per-arch single-step /
     25  * displaced-step trampoline. This driver TU only:
     26  *
     27  *   - parses argv and turns the source list into a JIT image,
     28  *   - opens DWARF and a JIT-image view,
     29  *   - manages a driver-local breakpoint table (id, spec text, enabled
     30  *     flag) keyed off session-side handles,
     31  *   - parses LOC strings (file:line, sym[+off], 0xADDR) into addresses
     32  *     via kit_dwarf_line_to_addr / kit_jit_lookup,
     33  *   - reads commands from stdin and dispatches,
     34  *   - renders KitStopInfo into source-level stop messages and the
     35  *     backtrace via kit_dwarf_unwind_step,
     36  *   - decodes `p name` via kit_dwarf_var_at + kit_dwarf_loc_read.
     37  *
     38  * Forwarding Ctrl-C: while a session call is in flight the driver
     39  * installs a SIGINT handler that calls kit_jit_session_interrupt; on
     40  * return it restores SIG_DFL so Ctrl-C at the REPL prompt terminates
     41  * the program. */
     42 
     43 #define DBG_TOOL "dbg"
     44 #define LINE_CAP 1024
     45 #define WORD_CAP 256
     46 #define HEX_CAP 8 /* upper bound on per-arch trap-byte save */
     47 
     48 /* ============================================================
     49  * argv parsing (mirrors run.c)
     50  * ============================================================ */
     51 
     52 typedef enum { DBG_ENTRY_FILE, DBG_ENTRY_CMD } DbgEntryKind;
     53 typedef struct {
     54   DbgEntryKind kind;
     55   const char* value; /* argv pointer — no per-entry allocation */
     56 } DbgScriptEntry;
     57 
     58 typedef struct DbgOpts {
     59   DriverEnv* env;
     60   size_t argv_bound;
     61 
     62   int opt_level;
     63   int debug_info;
     64   const char* entry;
     65   KitLanguage default_lang;
     66   int has_default_lang;
     67 
     68   DriverCflags cf;
     69   DriverInputs inputs;
     70 
     71   char** prog_argv;
     72   uint32_t prog_argc;
     73 
     74   DbgScriptEntry* script_entries;
     75   uint32_t nscript_entries;
     76   uint32_t script_entries_cap;
     77   int batch_mode;
     78 } DbgOpts;
     79 
     80 void driver_help_dbg(void) {
     81   driver_printf(
     82       "%.*s",
     83       KIT_SLICE_ARG(KIT_SLICE_LIT(
     84           "kit dbg — interactive JIT debugger\n"
     85           "\n"
     86           "USAGE\n"
     87           "  kit dbg [options] [input.{c,toy,s} ...] [-- prog-arg ...]\n"
     88           "\n"
     89           "DESCRIPTION\n"
     90           "  Mirrors `kit run` for compile flags and argv shape, but "
     91           "instead\n"
     92           "  of calling the entry directly drops into a REPL that drives the\n"
     93           "  JIT session: breakpoints, source-line stepping (into / over),\n"
     94           "  instruction stepping,\n"
     95           "  finish, backtrace, registers, locals/args, variable read/write,\n"
     96           "  and raw memory examine. -g is forced on so source lines and\n"
     97           "  variable locations are available at runtime.\n"
     98           "\n"
     99           "  Anything after `--` is passed to the JITed program as argv.\n"
    100           "  With no input files, dbg starts an empty JIT session; append "
    101           "code\n"
    102           "  with `jit` or evaluate expressions directly from the REPL.\n"
    103           "\n"
    104           "COMPILE OPTIONS\n"
    105           "  -O0 -O1 -O2       Optimization level (default -O0)\n"
    106           "  -g                Emit DWARF (forced on)\n"
    107           "  -e SYMBOL         Entry symbol (default `main`)\n"
    108           "  -x LANG           Default REPL language: c, toy, asm, wasm/wat\n"
    109           "  --language LANG   Same as -x\n"
    110           "  -I DIR            Add quoted-include search path\n"
    111           "  -isystem DIR      Add system-include search path\n"
    112           "  -D NAME[=BODY]    Define a macro\n"
    113           "  -U NAME           Undefine a builtin/predefined macro\n"
    114           "\n"
    115           "REPL COMMANDS (also shown by `h` at the prompt)\n"
    116           "  h, help                      show REPL help\n"
    117           "  q, quit                      exit (Ctrl-D also works)\n"
    118           "  r, run                       start fresh execution at entry\n"
    119           "  c, cont                      continue after a stop\n"
    120           "  s, step                      step to next source line (into "
    121           "calls)\n"
    122           "  si, stepi                    single-step one instruction\n"
    123           "  n, next                      step to next source line (over "
    124           "calls)\n"
    125           "  finish                       run until current frame returns\n"
    126           "  jit [LANG|NAME] { ... }      compile and append a language "
    127           "snippet\n"
    128           "  { ... }                      same as jit { ... }\n"
    129           "  edit [LANG|NAME]             edit and append a language snippet\n"
    130           "  expr EXPR | expr { ... }     compile and call an expression "
    131           "thunk\n"
    132           "  EXPR                         same as expr EXPR\n"
    133           "                               LANG defaults to the selected "
    134           "language\n"
    135           "  jump ADDR                    set PC to ADDR (no resume)\n"
    136           "  bt, backtrace                print stack trace with arguments\n"
    137           "  b LOC                        set breakpoint at LOC:\n"
    138           "                                  0xADDR | sym[+off] | file.c:line\n"
    139           "  ignore N COUNT               skip the next COUNT hits of bp N\n"
    140           "  d N, delete N                delete breakpoint N\n"
    141           "  enable N | disable N         toggle breakpoint N\n"
    142           "  p NAME                       print variable / global\n"
    143           "  set NAME VALUE               write VALUE into NAME\n"
    144           "  x ADDR [count]               examine memory (default 16 bytes)\n"
    145           "  disasm [ADDR] [count], x/i    disassemble at PC or ADDR\n"
    146           "  list FILE:LINE | l FILE:LINE source listing around FILE:LINE\n"
    147           "  info b                       list breakpoints\n"
    148           "  info reg, info registers     dump registers\n"
    149           "  info locals | info args      list locals / args at current PC\n"
    150           "  info functions [PATTERN]     list JIT functions matching PATTERN\n"
    151           "  info variables [PATTERN]     list JIT globals  matching PATTERN\n"
    152           "\n"
    153           "BATCH / SCRIPTING\n"
    154           "  --script FILE     execute debugger commands from FILE (repeatable)\n"
    155           "  --command CMD     execute CMD as if typed at the REPL (repeatable)\n"
    156           "  -c CMD            same as --command\n"
    157           "  --batch           non-interactive: suppress banner, exit after\n"
    158           "                    --script / --command sources drain; exit 1 on\n"
    159           "                    any command error\n"
    160           "  Note: multi-line jit{} / expr{} blocks in --script files must fit\n"
    161           "  on a single line (continuation reads go to stdin, not the file).\n"
    162           "\n"
    163           "SIGNALS\n"
    164           "  Ctrl-C is forwarded into the running session as an interrupt; at\n"
    165           "  the REPL prompt it terminates the program normally.\n"
    166           "\n"
    167           "GETTING HELP\n"
    168           "  -h, --help                   Show this help and exit (this is "
    169           "the\n"
    170           "                               command-line help; once the REPL is\n"
    171           "                               running, type `h` for REPL "
    172           "commands)\n"
    173           "\n"
    174           "EXIT CODES\n"
    175           "  0   clean exit       1   compile/link/command error       2   "
    176           "bad "
    177           "usage\n")));
    178 }
    179 
    180 static int dbg_alloc_arrays(DbgOpts* o, int argc) {
    181   size_t bound = (size_t)argc;
    182   o->argv_bound = bound;
    183   o->prog_argv = driver_alloc_zeroed(o->env, bound * sizeof(*o->prog_argv));
    184   if (!o->prog_argv) {
    185     driver_errf(DBG_TOOL, "out of memory");
    186     return 1;
    187   }
    188   if (driver_inputs_init(&o->inputs, o->env, DBG_TOOL, argc) != 0) return 1;
    189   if (driver_cflags_init(&o->cf, o->env, argc) != 0) {
    190     driver_errf(DBG_TOOL, "out of memory");
    191     return 1;
    192   }
    193   return 0;
    194 }
    195 
    196 static int dbg_parse_language_name(const char* name, KitLanguage* out) {
    197   /* Resolve off the compile-time default frontend set (no compiler exists at
    198    * arg-parse time). Value-equivalent to the former explicit map:
    199    * c->C, toy->TOY, asm/s->ASM, wasm/wat->WASM. */
    200   KitLanguage lang;
    201   if (!name || !*name || !out) return 0;
    202   lang = kit_language_for_name(NULL, name);
    203   if (lang == KIT_LANG_UNKNOWN) return 0;
    204   *out = lang;
    205   return 1;
    206 }
    207 
    208 static int dbg_set_default_language(DbgOpts* o, const char* name) {
    209   KitLanguage lang = KIT_LANG_COUNT;
    210   if (!dbg_parse_language_name(name, &lang)) {
    211     driver_errf(DBG_TOOL, "unsupported language: %.*s",
    212                 KIT_SLICE_ARG(kit_slice_cstr(name)));
    213     return 1;
    214   }
    215   o->default_lang = lang;
    216   o->has_default_lang = 1;
    217   return 0;
    218 }
    219 
    220 static int dbg_script_entry_push(DbgOpts* o, DbgEntryKind kind,
    221                                   const char* value) {
    222   if (o->nscript_entries >= o->script_entries_cap) {
    223     uint32_t nc = o->script_entries_cap ? o->script_entries_cap * 2 : 4;
    224     size_t old_sz = (size_t)o->script_entries_cap * sizeof(DbgScriptEntry);
    225     size_t new_sz = (size_t)nc * sizeof(DbgScriptEntry);
    226     DbgScriptEntry* nb = o->env->heap->realloc(o->env->heap,
    227                                                o->script_entries,
    228                                                old_sz, new_sz,
    229                                                _Alignof(DbgScriptEntry));
    230     if (!nb) { driver_errf(DBG_TOOL, "out of memory"); return 1; }
    231     o->script_entries = nb;
    232     o->script_entries_cap = nc;
    233   }
    234   o->script_entries[o->nscript_entries].kind = kind;
    235   o->script_entries[o->nscript_entries].value = value;
    236   o->nscript_entries++;
    237   return 0;
    238 }
    239 
    240 static int dbg_parse(int argc, char** argv, DbgOpts* o) {
    241   int i;
    242   int after_dash_dash = 0;
    243   if (dbg_alloc_arrays(o, argc) != 0) return 1;
    244 
    245   for (i = 1; i < argc; ++i) {
    246     const char* a = argv[i];
    247 
    248     if (after_dash_dash) {
    249       o->prog_argv[o->prog_argc++] = argv[i];
    250       continue;
    251     }
    252     if (driver_streq(a, "--")) {
    253       after_dash_dash = 1;
    254       continue;
    255     }
    256 
    257     {
    258       int r =
    259           driver_cflags_try_consume(&o->cf, o->env, DBG_TOOL, argc, argv, &i);
    260       if (r < 0) return 1;
    261       if (r > 0) continue;
    262     }
    263 
    264     if (driver_streq(a, "-g")) {
    265       o->debug_info = 1;
    266       continue;
    267     }
    268     if (driver_streq(a, "-O0")) {
    269       o->opt_level = 0;
    270       continue;
    271     }
    272     if (driver_streq(a, "-O1")) {
    273       o->opt_level = 1;
    274       continue;
    275     }
    276     if (driver_streq(a, "-O2")) {
    277       o->opt_level = 2;
    278       continue;
    279     }
    280 
    281     if (driver_streq(a, "-e")) {
    282       if (++i >= argc) {
    283         driver_errf(DBG_TOOL, "-e requires an argument");
    284         return 1;
    285       }
    286       o->entry = argv[i];
    287       continue;
    288     }
    289 
    290     if (driver_streq(a, "-x") || driver_streq(a, "--language") ||
    291         driver_streq(a, "--lang")) {
    292       if (++i >= argc) {
    293         driver_errf(DBG_TOOL, "%.*s requires an argument",
    294                     KIT_SLICE_ARG(kit_slice_cstr(a)));
    295         return 1;
    296       }
    297       if (dbg_set_default_language(o, argv[i]) != 0) return 1;
    298       continue;
    299     }
    300     if (driver_strneq(a, "--language=", 11)) {
    301       if (dbg_set_default_language(o, a + 11) != 0) return 1;
    302       continue;
    303     }
    304     if (driver_strneq(a, "--lang=", 7)) {
    305       if (dbg_set_default_language(o, a + 7) != 0) return 1;
    306       continue;
    307     }
    308 
    309     if (driver_streq(a, "--script")) {
    310       if (++i >= argc) {
    311         driver_errf(DBG_TOOL, "--script requires a FILE argument");
    312         return 1;
    313       }
    314       if (dbg_script_entry_push(o, DBG_ENTRY_FILE, argv[i]) != 0) return 1;
    315       continue;
    316     }
    317     if (driver_streq(a, "--command") || driver_streq(a, "-c")) {
    318       if (++i >= argc) {
    319         driver_errf(DBG_TOOL, "%.*s requires a CMD argument",
    320                     KIT_SLICE_ARG(kit_slice_cstr(a)));
    321         return 1;
    322       }
    323       if (dbg_script_entry_push(o, DBG_ENTRY_CMD, argv[i]) != 0) return 1;
    324       continue;
    325     }
    326     if (driver_streq(a, "--batch")) {
    327       o->batch_mode = 1;
    328       continue;
    329     }
    330 
    331     if (a[0] == '-' && a[1] != '\0') {
    332       driver_errf(DBG_TOOL, "unknown flag: %.*s",
    333                   KIT_SLICE_ARG(kit_slice_cstr(a)));
    334       return 1;
    335     }
    336 
    337     {
    338       int r = driver_inputs_classify(&o->inputs, a);
    339       if (r < 0) return 1;
    340       if (r == 0) {
    341         driver_errf(DBG_TOOL, "input does not have a recognized suffix: %.*s",
    342                     KIT_SLICE_ARG(kit_slice_cstr(a)));
    343         return 1;
    344       }
    345     }
    346   }
    347 
    348   if (!o->entry) o->entry = "main";
    349   if (!o->debug_info) {
    350     /* Without -g there are no source lines or variable locations to
    351      * read at runtime; force it on so `b file:line` and `p name`
    352      * have something to look at. The user can always re-run without
    353      * if they want to inspect optimized code at the asm level. */
    354     o->debug_info = 1;
    355   }
    356   return 0;
    357 }
    358 
    359 static void dbg_options_release(DbgOpts* o) {
    360   size_t bound = o->argv_bound;
    361   driver_inputs_release(&o->inputs);
    362   driver_cflags_fini(&o->cf, o->env);
    363   driver_free(o->env, o->prog_argv, bound * sizeof(*o->prog_argv));
    364   if (o->script_entries)
    365     driver_free(o->env, o->script_entries,
    366                 (size_t)o->script_entries_cap * sizeof(DbgScriptEntry));
    367 }
    368 
    369 /* Compile every C source through a compiler owned by the caller and JIT-link
    370  * the result. Compiler ownership stays with the caller so DWARF lookups
    371  * during the REPL session can run against the live compiler. */
    372 static int dbg_compile_and_jit(DbgOpts* o, KitCompiler* compiler,
    373                                const KitJitHost* host, KitJit** out_jit) {
    374   KitCCompileOptions copts;
    375   const char* link_entry = driver_inputs_count(&o->inputs) ? o->entry : NULL;
    376   {
    377     KitCCompileOptions z = {0};
    378     copts = z;
    379   }
    380   KitPreprocessOptions pp;
    381   copts.code.opt_level = o->opt_level;
    382   copts.code.debug_info = o->debug_info;
    383   driver_cflags_fill_pp(&o->cf, &pp);
    384   return driver_inputs_compile_and_jit(&o->inputs, compiler, host, &copts, &pp,
    385                                        link_entry, driver_dlsym_resolver, NULL,
    386                                        out_jit);
    387 }
    388 
    389 static void dbg_fill_compile_options(DbgOpts* o, KitCCompileOptions* copts,
    390                                      KitPreprocessOptions* pp) {
    391   {
    392     KitCCompileOptions z = {0};
    393     *copts = z;
    394   }
    395   copts->code.opt_level = o->opt_level;
    396   copts->code.debug_info = o->debug_info;
    397   driver_cflags_fill_pp(&o->cf, pp);
    398 }
    399 
    400 /* ============================================================
    401  * Breakpoint table (driver-side)
    402  * ============================================================
    403  * Each entry pairs a session-side handle (assigned by libkit) with
    404  * driver-side bookkeeping: a stable integer id we expose to the user, the
    405  * spec text the user gave us, and a flag that lets us cheaply re-arm
    406  * temp-disabled breakpoints. */
    407 
    408 typedef enum BpKind {
    409   BP_ADDR,
    410   BP_SYM,
    411   BP_LINE,
    412 } BpKind;
    413 
    414 typedef struct Bp {
    415   int id; /* user-facing handle, 1.. */
    416   int enabled;
    417   BpKind kind;
    418   char* spec; /* heap-owned NUL-terminated copy */
    419   size_t spec_size;
    420   uint64_t addr;
    421   uint64_t skip_count; /* silent skips before the first stop  */
    422   uint64_t max_hits;   /* 0 = unlimited                       */
    423   uint32_t session_id; /* libkit handle; 0 when disarmed */
    424 } Bp;
    425 
    426 typedef struct DbgSource {
    427   char* name;
    428   size_t name_size;
    429   uint8_t* data;
    430   size_t data_size;
    431   size_t len;
    432 } DbgSource;
    433 
    434 /* ============================================================
    435  * Session-scoped state
    436  * ============================================================ */
    437 
    438 typedef struct DbgState {
    439   DriverEnv* env;
    440   KitCompiler* compiler;
    441   KitContext ctx;
    442   KitCCompileOptions copts;
    443   KitPreprocessOptions pp; /* preprocessor settings for REPL compiles */
    444   KitJit* jit;
    445   KitJitSession* session;
    446   const KitObjFile* view;
    447   KitDebugInfo* dwarf;
    448   void* entry_addr;
    449   const char* entry_name;
    450   KitLanguage default_jit_lang;
    451   const char* default_jit_name;
    452   /* Backing storage for default_jit_name (built from the canonical frontend
    453    * extension), kept stable for the session's lifetime. */
    454   char default_jit_name_buf[32];
    455   KitCompileSession* compile_sessions[KIT_LANG_COUNT];
    456   DbgSource* sources;
    457   uint32_t nsources;
    458   uint32_t sources_cap;
    459   int prog_argc;
    460   char** prog_argv;
    461 
    462   Bp* bps;
    463   uint32_t nbps;
    464   uint32_t bps_cap;
    465   int next_bp_id;
    466   uint64_t
    467       expr_counter; /* user-visible $N; advances only on a successful expr */
    468   uint64_t expr_attempt; /* unique thunk-symbol id; advances every attempt */
    469   uint64_t source_counter;
    470 
    471   int has_stop;
    472   KitStopInfo last_stop;
    473   uint64_t jit_counter;
    474   DriverLineHistory line_history;
    475 
    476   DbgScriptEntry* script_entries; /* transferred from DbgOpts */
    477   uint32_t nscript_entries;
    478   int batch_mode;
    479   int error_count;
    480 } DbgState;
    481 
    482 /* Like driver_errf but increments s->error_count so --batch can propagate
    483  * command failures to the exit code. */
    484 #define dbg_errf(s, ...) \
    485   (driver_errf(DBG_TOOL, __VA_ARGS__), (void)((s)->error_count++))
    486 
    487 #define DBG_LIST_CTX 5 /* lines printed before/after the target */
    488 
    489 /* SIGINT trampoline. The handler in env.c calls our cb with this state;
    490  * we forward into the session. kit_jit_session_interrupt is documented
    491  * async-signal-safe. */
    492 static void dbg_on_sigint(void* user) {
    493   DbgState* s = (DbgState*)user;
    494   if (s && s->session) kit_jit_session_interrupt(s->session);
    495 }
    496 
    497 /* PC-space translation between the JIT runtime address space (where
    498  * SIGTRAP fires and where the debugger installs breakpoints) and the
    499  * image-relative vaddr space DWARF was authored in.  Every DWARF call
    500  * that takes a PC consumes an image vaddr, and every DWARF result
    501  * that names a code address is image-relative — translate at the
    502  * boundary.  Fallback is pass-through so out-of-image PCs (e.g.
    503  * stops inside libc on a future multi-input setup) don't return 0
    504  * and silently degrade lookups. */
    505 static uint64_t dbg_pc_rt_to_img(DbgState* s, uint64_t rt) {
    506   uint64_t v = kit_jit_runtime_to_image(s->jit, rt);
    507   return v ? v : rt;
    508 }
    509 static uint64_t dbg_pc_img_to_rt(DbgState* s, uint64_t img) {
    510   uint64_t v = kit_jit_image_to_runtime(s->jit, img);
    511   return v ? v : img;
    512 }
    513 
    514 /* Build a frame view in image-PC space for DWARF queries that key off
    515  * frame->pc (subprogram_at, param_iter_new, vars_at_new, unwind_step).
    516  * The register snapshot and CFA stay in their original (runtime) form
    517  * because dw_eval_expr / loc_read interpret them as live host values. */
    518 static KitUnwindFrame dbg_frame_for_dwarf(DbgState* s,
    519                                           const KitUnwindFrame* rt) {
    520   KitUnwindFrame out = *rt;
    521   out.pc = dbg_pc_rt_to_img(s, rt->pc);
    522   return out;
    523 }
    524 
    525 /* Translate any image-vaddr fields stored on a KitDwarfVarLoc back
    526  * to runtime addresses before the loc is handed to a memory accessor
    527  * (session_read_mem / session_write_mem operate in runtime space).
    528  * Only DLOC_GLOBAL carries an absolute address straight from
    529  * .debug_info; DLOC_REG / DLOC_FRAME_OFS / DLOC_EXPR derive their
    530  * effective address from live register state or are evaluated against
    531  * the frame, both of which are already in runtime space. */
    532 static void dbg_translate_loc(DbgState* s, KitDwarfVarLoc* loc) {
    533   if (!loc) return;
    534   if (loc->kind == KIT_DLOC_GLOBAL)
    535     loc->v.global = dbg_pc_img_to_rt(s, loc->v.global);
    536 }
    537 
    538 /* ============================================================
    539  * Tiny driver-local string utilities
    540  * ============================================================
    541  * The driver TU may use plain libc <string.h>/<ctype.h> per the project
    542  * rules (no syscalls). We avoid pulling in libc here only because the
    543  * existing driver shims cover everything we need. */
    544 
    545 static int dbg_isspace(int c) {
    546   return c == ' ' || c == '\t' || c == '\r' || c == '\n';
    547 }
    548 static int dbg_isdigit(int c) { return c >= '0' && c <= '9'; }
    549 static int dbg_isxdigit(int c) {
    550   return dbg_isdigit(c) || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F');
    551 }
    552 
    553 static int dbg_xval(int c) {
    554   if (dbg_isdigit(c)) return c - '0';
    555   if (c >= 'a' && c <= 'f') return c - 'a' + 10;
    556   return c - 'A' + 10;
    557 }
    558 
    559 static char* dbg_dup(DriverEnv* env, const char* s, size_t n,
    560                      size_t* size_out) {
    561   char* p = driver_alloc(env, n + 1);
    562   if (!p) return NULL;
    563   driver_memcpy(p, s, n);
    564   p[n] = '\0';
    565   if (size_out) *size_out = n + 1;
    566   return p;
    567 }
    568 
    569 /* Parse a 0x-prefixed hex literal or a decimal literal into *out. Returns
    570  * the number of characters consumed, or 0 on failure. */
    571 static size_t dbg_parse_uint(const char* s, uint64_t* out) {
    572   size_t i = 0;
    573   uint64_t v = 0;
    574   if (s[0] == '0' && (s[1] == 'x' || s[1] == 'X')) {
    575     i = 2;
    576     if (!dbg_isxdigit((unsigned char)s[i])) return 0;
    577     for (; dbg_isxdigit((unsigned char)s[i]); ++i) {
    578       v = (v << 4) | (uint64_t)dbg_xval((unsigned char)s[i]);
    579     }
    580   } else {
    581     if (!dbg_isdigit((unsigned char)s[0])) return 0;
    582     for (; dbg_isdigit((unsigned char)s[i]); ++i) {
    583       v = v * 10 + (uint64_t)(s[i] - '0');
    584     }
    585   }
    586   *out = v;
    587   return i;
    588 }
    589 
    590 static size_t dbg_u64_dec(char* dst, size_t cap, uint64_t v);
    591 
    592 /* ============================================================
    593  * Breakpoint table operations
    594  * ============================================================ */
    595 
    596 static Bp* dbg_bp_grow(DbgState* s) {
    597   uint32_t nc;
    598   size_t old_size, new_size;
    599   Bp* nb;
    600   if (s->nbps < s->bps_cap) return &s->bps[s->nbps];
    601 
    602   nc = s->bps_cap ? s->bps_cap * 2 : 8;
    603   old_size = (size_t)s->bps_cap * sizeof(Bp);
    604   new_size = (size_t)nc * sizeof(Bp);
    605   nb = s->env->heap->realloc(s->env->heap, s->bps, old_size, new_size,
    606                              _Alignof(Bp));
    607   if (!nb) {
    608     dbg_errf(s, "out of memory growing breakpoint table");
    609     return NULL;
    610   }
    611   /* Zero the freshly grown tail so future driver_free walks it cleanly. */
    612   {
    613     char* z = (char*)nb + old_size;
    614     size_t n = new_size - old_size;
    615     size_t j;
    616     for (j = 0; j < n; ++j) z[j] = 0;
    617   }
    618   s->bps = nb;
    619   s->bps_cap = nc;
    620   return &s->bps[s->nbps];
    621 }
    622 
    623 static Bp* dbg_bp_find(DbgState* s, int id) {
    624   uint32_t i;
    625   for (i = 0; i < s->nbps; ++i) {
    626     if (s->bps[i].id == id) return &s->bps[i];
    627   }
    628   return NULL;
    629 }
    630 
    631 static void dbg_bp_release(DbgState* s, Bp* b) {
    632   if (b->session_id) {
    633     kit_jit_session_breakpoint_clear(s->session, b->session_id);
    634     b->session_id = 0;
    635   }
    636   if (b->spec) {
    637     driver_free(s->env, b->spec, b->spec_size);
    638     b->spec = NULL;
    639     b->spec_size = 0;
    640   }
    641 }
    642 
    643 static int dbg_bp_remove(DbgState* s, int id) {
    644   uint32_t i;
    645   for (i = 0; i < s->nbps; ++i) {
    646     if (s->bps[i].id == id) {
    647       dbg_bp_release(s, &s->bps[i]);
    648       /* Shift tail down to keep the array dense; ids stay stable. */
    649       {
    650         uint32_t j;
    651         for (j = i + 1; j < s->nbps; ++j) s->bps[j - 1] = s->bps[j];
    652       }
    653       s->nbps--;
    654       {
    655         Bp z = {0};
    656         s->bps[s->nbps] = z;
    657       }
    658       return 0;
    659     }
    660   }
    661   return 1;
    662 }
    663 
    664 static void dbg_bps_release_all(DbgState* s) {
    665   uint32_t i;
    666   for (i = 0; i < s->nbps; ++i) dbg_bp_release(s, &s->bps[i]);
    667   if (s->bps) {
    668     driver_free(s->env, s->bps, (size_t)s->bps_cap * sizeof(Bp));
    669     s->bps = NULL;
    670     s->bps_cap = 0;
    671     s->nbps = 0;
    672   }
    673 }
    674 
    675 static void dbg_compile_sessions_release(DbgState* s) {
    676   uint32_t i;
    677   for (i = 0; i < (uint32_t)KIT_LANG_COUNT; ++i) {
    678     kit_compile_session_free(s->compile_sessions[i]);
    679     s->compile_sessions[i] = NULL;
    680   }
    681 }
    682 
    683 static void dbg_sources_release_all(DbgState* s) {
    684   uint32_t i;
    685   for (i = 0; i < s->nsources; ++i) {
    686     DbgSource* src = &s->sources[i];
    687     if (src->name) driver_free(s->env, src->name, src->name_size);
    688     if (src->data) driver_free(s->env, src->data, src->data_size);
    689   }
    690   if (s->sources) {
    691     driver_free(s->env, s->sources,
    692                 (size_t)s->sources_cap * sizeof(*s->sources));
    693     s->sources = NULL;
    694   }
    695   s->nsources = 0;
    696   s->sources_cap = 0;
    697 }
    698 
    699 /* ============================================================
    700  * LOC parser
    701  * ============================================================
    702  * Resolves a user-supplied location specification into a single address
    703  * within the JIT image:
    704  *
    705  *   0xADDR              raw address
    706  *   sym[+0xN | +N]      symbol via kit_jit_lookup, optional offset
    707  *   file.c:LINE         DWARF lookup
    708  *
    709  * Returns 0 on success, 1 on parse / resolution failure (already
    710  * reported via driver_errf). */
    711 
    712 static int dbg_resolve_loc(DbgState* s, const char* spec, BpKind* kind_out,
    713                            uint64_t* addr_out) {
    714   /* file:line — uniquely identifiable by a colon NOT preceded by + and
    715    * followed by digits. We require the suffix after ':' to be all
    716    * digits so we don't confuse, say, hypothetical `func:42` (which
    717    * isn't a thing in C). */
    718   const char* colon = driver_strchr(spec, ':');
    719   if (colon && dbg_isdigit((unsigned char)colon[1])) {
    720     size_t file_n = (size_t)(colon - spec);
    721     char* file;
    722     uint64_t line64;
    723     size_t used;
    724     size_t file_size;
    725     uint64_t pc;
    726 
    727     if (file_n == 0) {
    728       dbg_errf(s, "empty file in '%.*s'",
    729                   KIT_SLICE_ARG(kit_slice_cstr(spec)));
    730       return 1;
    731     }
    732     file = dbg_dup(s->env, spec, file_n, &file_size);
    733     if (!file) {
    734       dbg_errf(s, "out of memory");
    735       return 1;
    736     }
    737     used = dbg_parse_uint(colon + 1, &line64);
    738     if (!used || colon[1 + used] != '\0') {
    739       dbg_errf(s, "expected file.c:LINE, got '%.*s'",
    740                   KIT_SLICE_ARG(kit_slice_cstr(spec)));
    741       driver_free(s->env, file, file_size);
    742       return 1;
    743     }
    744     if (!s->dwarf) {
    745       dbg_errf(s, "no DWARF: cannot resolve %.*s",
    746                   KIT_SLICE_ARG(kit_slice_cstr(spec)));
    747       driver_free(s->env, file, file_size);
    748       return 1;
    749     }
    750     {
    751       KitStatus rc = kit_dwarf_line_to_addr(s->dwarf, kit_slice_cstr(file),
    752                                             (uint32_t)line64, &pc);
    753       if (rc == KIT_NOT_FOUND) {
    754         dbg_errf(s, "no line %u in %.*s", (uint32_t)line64,
    755                     KIT_SLICE_ARG(kit_slice_cstr(file)));
    756         driver_free(s->env, file, file_size);
    757         return 1;
    758       }
    759       if (rc == KIT_AMBIGUOUS) {
    760         KitDwarfLineMatch cands[8];
    761         uint32_t n = 0;
    762         uint32_t k;
    763         kit_dwarf_line_to_addr_all(s->dwarf, kit_slice_cstr(file),
    764                                    (uint32_t)line64, cands, 8u, &n);
    765         dbg_errf(s, "ambiguous: %.*s:%u matches %u files",
    766                     KIT_SLICE_ARG(kit_slice_cstr(file)), (uint32_t)line64,
    767                     (unsigned)n);
    768         for (k = 0; k < n && k < 8u; ++k) {
    769           dbg_errf(s, "  %.*s (0x%llx)", KIT_SLICE_ARG(cands[k].file),
    770                       (unsigned long long)cands[k].pc);
    771         }
    772         if (n > 8u) dbg_errf(s, "  ... and %u more", n - 8u);
    773         dbg_errf(s, "use a longer path suffix (e.g. b dir/%.*s:%u)",
    774                     KIT_SLICE_ARG(kit_slice_cstr(file)), (uint32_t)line64);
    775         driver_free(s->env, file, file_size);
    776         return 1;
    777       }
    778       if (rc != KIT_OK) {
    779         dbg_errf(s, "no line entry for %.*s",
    780                     KIT_SLICE_ARG(kit_slice_cstr(spec)));
    781         driver_free(s->env, file, file_size);
    782         return 1;
    783       }
    784     }
    785     driver_free(s->env, file, file_size);
    786     *kind_out = BP_LINE;
    787     *addr_out = dbg_pc_img_to_rt(s, pc);
    788     return 0;
    789   }
    790 
    791   /* 0xADDR or decimal address. */
    792   if ((spec[0] == '0' && (spec[1] == 'x' || spec[1] == 'X')) ||
    793       dbg_isdigit((unsigned char)spec[0])) {
    794     uint64_t v;
    795     size_t used = dbg_parse_uint(spec, &v);
    796     if (!used || spec[used] != '\0') {
    797       dbg_errf(s, "trailing junk in address '%.*s'",
    798                   KIT_SLICE_ARG(kit_slice_cstr(spec)));
    799       return 1;
    800     }
    801     *kind_out = BP_ADDR;
    802     *addr_out = v;
    803     return 0;
    804   }
    805 
    806   /* sym[+off] */
    807   {
    808     const char* plus = driver_strchr(spec, '+');
    809     size_t name_n = plus ? (size_t)(plus - spec) : driver_strlen(spec);
    810     char* name;
    811     size_t name_size;
    812     void* resolved;
    813     uint64_t off = 0;
    814 
    815     if (name_n == 0) {
    816       dbg_errf(s, "empty symbol in '%.*s'",
    817                   KIT_SLICE_ARG(kit_slice_cstr(spec)));
    818       return 1;
    819     }
    820     name = dbg_dup(s->env, spec, name_n, &name_size);
    821     if (!name) {
    822       dbg_errf(s, "out of memory");
    823       return 1;
    824     }
    825     resolved = kit_jit_lookup(s->jit, kit_slice_cstr(name));
    826     if (!resolved) {
    827       dbg_errf(s, "symbol not found: %.*s",
    828                   KIT_SLICE_ARG(kit_slice_cstr(name)));
    829       driver_free(s->env, name, name_size);
    830       return 1;
    831     }
    832     driver_free(s->env, name, name_size);
    833 
    834     if (plus) {
    835       size_t used = dbg_parse_uint(plus + 1, &off);
    836       if (!used || plus[1 + used] != '\0') {
    837         dbg_errf(s, "bad offset in '%.*s'",
    838                     KIT_SLICE_ARG(kit_slice_cstr(spec)));
    839         return 1;
    840       }
    841     }
    842 
    843     *kind_out = BP_SYM;
    844     /* Object-pointer to integer cast: implementation-defined in
    845      * standard C, defined as the address on every host where a JIT
    846      * runs. */
    847     {
    848       union {
    849         void* p;
    850         uint64_t u;
    851       } cv;
    852       cv.p = resolved;
    853       *addr_out = cv.u + off;
    854     }
    855     return 0;
    856   }
    857 }
    858 
    859 /* ============================================================
    860  * Stop rendering
    861  * ============================================================ */
    862 
    863 static void dbg_print_pc(DbgState* s, uint64_t pc) {
    864   KitSlice sym = KIT_SLICE_NULL;
    865   uint64_t off = 0;
    866   KitSlice file = KIT_SLICE_NULL;
    867   uint32_t line = 0;
    868   uint32_t col = 0;
    869 
    870   driver_printf("0x%llx", (unsigned long long)pc);
    871   if (kit_jit_addr_to_sym(s->jit, pc, &sym, &off) == KIT_OK && sym.s) {
    872     if (off)
    873       driver_printf(" <%.*s+0x%llx>", KIT_SLICE_ARG(sym),
    874                     (unsigned long long)off);
    875     else
    876       driver_printf(" <%.*s>", KIT_SLICE_ARG(sym));
    877   }
    878   if (s->dwarf &&
    879       kit_dwarf_addr_to_line(s->dwarf, dbg_pc_rt_to_img(s, pc), &file, &line,
    880                              &col) == KIT_OK &&
    881       file.s) {
    882     driver_printf(" at %.*s:%u", KIT_SLICE_ARG(file), line);
    883     if (col) driver_printf(":%u", col);
    884   }
    885 }
    886 
    887 static void dbg_print_source_listing(DbgState* s, KitSlice file, uint32_t line,
    888                                      uint64_t pc, int report_errors);
    889 
    890 static KitSlice dbg_step_stop_label(KitStopReason reason) {
    891   switch (reason) {
    892     case KIT_STOP_REASON_STEP_INSN:
    893       return KIT_SLICE_LIT("Single-step complete at ");
    894     case KIT_STOP_REASON_STEP_LINE:
    895       return KIT_SLICE_LIT("Step complete at ");
    896     case KIT_STOP_REASON_NEXT_LINE:
    897       return KIT_SLICE_LIT("Next complete at ");
    898     case KIT_STOP_REASON_STEP_OUT:
    899       return KIT_SLICE_LIT("Finish complete at ");
    900     case KIT_STOP_REASON_UNKNOWN:
    901     case KIT_STOP_REASON_USER_BREAKPOINT:
    902     case KIT_STOP_REASON_SIGNAL:
    903     case KIT_STOP_REASON_TRAP:
    904     case KIT_STOP_REASON_INTERRUPT:
    905     case KIT_STOP_REASON_EXIT:
    906       break;
    907   }
    908   return KIT_SLICE_LIT("Internal breakpoint hit at ");
    909 }
    910 
    911 static void dbg_cmd_bt(DbgState* s);
    912 static KitStatus dbg_dwarf_read_mem(void* user, uint64_t addr, void* dst,
    913                                     size_t n);
    914 
    915 static void dbg_render_stop(DbgState* s, const KitStopInfo* st) {
    916   KitSlice file = {0};
    917   uint32_t line = 0;
    918   uint32_t col = 0;
    919   int has_source = 0;
    920 
    921   if (st->kind != KIT_STOP_EXIT && s->dwarf &&
    922       kit_dwarf_addr_to_line(s->dwarf, dbg_pc_rt_to_img(s, st->regs.pc), &file,
    923                              &line, &col) == KIT_OK &&
    924       file.s) {
    925     has_source = 1;
    926   }
    927 
    928   switch (st->kind) {
    929     case KIT_STOP_BREAKPOINT: {
    930       Bp* b = NULL;
    931       uint32_t i;
    932       for (i = 0; i < s->nbps; ++i) {
    933         if (s->bps[i].session_id == st->bp_id) {
    934           b = &s->bps[i];
    935           break;
    936         }
    937       }
    938       if (b)
    939         driver_printf("Breakpoint %d (%.*s) hit at ", b->id,
    940                       KIT_SLICE_ARG(kit_slice_cstr(b->spec)));
    941       else {
    942         KitSlice label = dbg_step_stop_label(st->reason);
    943         driver_printf("%.*s", KIT_SLICE_ARG(label));
    944       }
    945       dbg_print_pc(s, st->regs.pc);
    946       driver_printf("\n");
    947       break;
    948     }
    949     case KIT_STOP_SIGNAL:
    950       if (st->reason == KIT_STOP_REASON_TRAP)
    951         driver_printf("Stopped on trap signal %d at ", st->signal);
    952       else
    953         driver_printf("Stopped on signal %d at ", st->signal);
    954       dbg_print_pc(s, st->regs.pc);
    955       driver_printf("\n");
    956       /* A fault/trap is a crash, not a planned stop: print the backtrace
    957        * automatically (the user would type `bt` next anyway). Breakpoints and
    958        * step completions are separate stop kinds, so this only fires on a real
    959        * signal or __builtin_trap/assert. */
    960       if (s->dwarf) dbg_cmd_bt(s);
    961       break;
    962     case KIT_STOP_INTERRUPT:
    963       driver_printf("Interrupted at ");
    964       dbg_print_pc(s, st->regs.pc);
    965       driver_printf("\n");
    966       break;
    967     case KIT_STOP_EXIT:
    968       driver_printf("Program exited with code %d\n", st->exit_code);
    969       break;
    970   }
    971   if (has_source)
    972     dbg_print_source_listing(s, file, line, dbg_pc_rt_to_img(s, st->regs.pc),
    973                              0);
    974 }
    975 
    976 /* ============================================================
    977  * Run / continue / step
    978  * ============================================================
    979  * Both `r` and `c` flow through the same wrapper: install SIGINT, drive
    980  * the session (call or resume), restore SIGINT, render the stop. The
    981  * dbg owns no signal-handling complexity itself — that's the session's
    982  * job. */
    983 
    984 typedef enum DbgRunMode {
    985   RUN_FRESH,     /* r       — _call from entry         */
    986   RUN_CONTINUE,  /* c       — _resume(continue)        */
    987   RUN_STEP_INSN, /* si      — _resume(step_insn)       */
    988   RUN_STEP_LINE, /* s       — _resume(step_line)       */
    989   RUN_NEXT_LINE, /* n       — _resume(next_line)       */
    990   RUN_STEP_OUT,  /* finish  — _resume(step_out)        */
    991 } DbgRunMode;
    992 
    993 static int dbg_drive(DbgState* s, DbgRunMode mode) {
    994   KitStatus rc;
    995 
    996   if (mode == RUN_FRESH && s->has_stop) {
    997     /* The previous session is dead (entry returned or signal landed).
    998      * Start a new one. Try to abort it first in case it's parked in a fault. */
    999     if (s->session) {
   1000       kit_jit_session_resume(s->session, KIT_RESUME_ABORT, NULL);
   1001     }
   1002     s->has_stop = 0;
   1003   } else if (mode != RUN_FRESH && !s->has_stop) {
   1004     dbg_errf(s, "no program is running; use 'r' to start");
   1005     return 1;
   1006   }
   1007 
   1008   if (mode == RUN_FRESH && !s->entry_addr) {
   1009     if (!s->entry_name || !*s->entry_name) {
   1010       dbg_errf(s, "no entry symbol configured");
   1011       return 1;
   1012     }
   1013     s->entry_addr = kit_jit_lookup(s->jit, kit_slice_cstr(s->entry_name));
   1014     if (!s->entry_addr) {
   1015       dbg_errf(s, "entry symbol not found: %.*s",
   1016                   KIT_SLICE_ARG(kit_slice_cstr(s->entry_name)));
   1017       return 1;
   1018     }
   1019   }
   1020 
   1021   if (driver_install_sigint(dbg_on_sigint, s) != 0) {
   1022     dbg_errf(s, "failed to install SIGINT handler");
   1023     return 1;
   1024   }
   1025 
   1026   if (mode == RUN_FRESH) {
   1027     rc = kit_jit_session_call(s->session, s->entry_addr, KIT_ENTRY_INT_ARGV,
   1028                               s->prog_argc, s->prog_argv, &s->last_stop);
   1029   } else {
   1030     KitResumeMode rm = KIT_RESUME_CONTINUE;
   1031     switch (mode) {
   1032       case RUN_STEP_INSN:
   1033         rm = KIT_RESUME_STEP_INSN;
   1034         break;
   1035       case RUN_STEP_LINE:
   1036         rm = KIT_RESUME_STEP_LINE;
   1037         break;
   1038       case RUN_NEXT_LINE:
   1039         rm = KIT_RESUME_NEXT_LINE;
   1040         break;
   1041       case RUN_STEP_OUT:
   1042         rm = KIT_RESUME_STEP_OUT;
   1043         break;
   1044       case RUN_CONTINUE:
   1045         rm = KIT_RESUME_CONTINUE;
   1046         break;
   1047       case RUN_FRESH:
   1048         break; /* unreachable */
   1049     }
   1050     rc = kit_jit_session_resume(s->session, rm, &s->last_stop);
   1051   }
   1052 
   1053   driver_restore_sigint();
   1054 
   1055   if (rc != KIT_OK) {
   1056     driver_errf(
   1057         DBG_TOOL,
   1058         "session %.*s failed (st=%d) — "
   1059         "JIT session implementation pending",
   1060         KIT_SLICE_ARG(kit_slice_cstr(mode == RUN_FRESH ? "call" : "resume")),
   1061         (int)rc);
   1062     return 1;
   1063   }
   1064 
   1065   s->has_stop = 1;
   1066   dbg_render_stop(s, &s->last_stop);
   1067   if (s->last_stop.kind == KIT_STOP_EXIT) s->has_stop = 0;
   1068   return 0;
   1069 }
   1070 
   1071 /* Forward declarations: backtrace renders parameter values via the
   1072  * type-aware printer defined below. */
   1073 static void dbg_print_value(DbgState*, const KitDwarfType*, const uint8_t*,
   1074                             size_t got, int depth);
   1075 static int dbg_read_value(DbgState*, const KitDwarfVarLoc*,
   1076                           const KitUnwindFrame*, uint8_t* stack_buf,
   1077                           size_t stack_cap, uint8_t** buf_out,
   1078                           size_t* alloc_out, size_t* got_out);
   1079 static void dbg_release_value_buf(DbgState*, uint8_t* buf, size_t alloc);
   1080 static char* dbg_take_word(char* line, char** word_out);
   1081 
   1082 /* ============================================================
   1083  * Backtrace
   1084  * ============================================================
   1085  * Renders one line per frame:
   1086  *   #N 0xPC <sym+off> in func (arg1=val1, arg2=val2) at file:line
   1087  * Parameter rendering uses kit_dwarf_param_iter_* against the frame's
   1088  * PC and the unwound register snapshot. Inlined frames are flagged. */
   1089 
   1090 static void dbg_cmd_bt(DbgState* s) {
   1091   KitUnwindFrame frame;
   1092   int level = 0;
   1093 
   1094   if (!s->has_stop) {
   1095     dbg_errf(s, "no program is stopped");
   1096     return;
   1097   }
   1098   if (!s->dwarf) {
   1099     dbg_errf(s, "no DWARF: backtrace unavailable");
   1100     return;
   1101   }
   1102 
   1103   frame = s->last_stop.regs;
   1104   for (;;) {
   1105     KitDwarfSubprogram sp;
   1106     KitUnwindFrame img_frame;
   1107     int have_sp;
   1108 
   1109     driver_printf("#%-2d ", level);
   1110     driver_printf("0x%llx", (unsigned long long)frame.pc);
   1111 
   1112     {
   1113       KitSlice sym = KIT_SLICE_NULL;
   1114       uint64_t off = 0;
   1115       if (kit_jit_addr_to_sym(s->jit, frame.pc, &sym, &off) == KIT_OK &&
   1116           sym.s) {
   1117         if (off)
   1118           driver_printf(" <%.*s+0x%llx>", KIT_SLICE_ARG(sym),
   1119                         (unsigned long long)off);
   1120         else
   1121           driver_printf(" <%.*s>", KIT_SLICE_ARG(sym));
   1122       }
   1123     }
   1124 
   1125     img_frame = dbg_frame_for_dwarf(s, &frame);
   1126     have_sp = (kit_dwarf_subprogram_at(s->dwarf, img_frame.pc, &sp) == KIT_OK);
   1127     if (have_sp && sp.name.s) {
   1128       KitDwarfParamIter* it = NULL;
   1129       KitDwarfVar p;
   1130       int first = 1;
   1131       driver_printf(" in %.*s%.*s (", KIT_SLICE_ARG(sp.name),
   1132                     KIT_SLICE_ARG(sp.inlined ? KIT_SLICE_LIT(" [inlined]")
   1133                                              : KIT_SLICE_NULL));
   1134       if (kit_dwarf_param_iter_new(s->dwarf, img_frame.pc, &it) == KIT_OK) {
   1135         for (;;) {
   1136           KitIterResult r;
   1137           uint8_t stack_buf[64];
   1138           uint8_t* buf;
   1139           size_t alloc;
   1140           size_t got;
   1141           r = kit_dwarf_param_iter_next(it, &p);
   1142           if (r != KIT_ITER_ITEM) break;
   1143           if (!first) driver_printf(", ");
   1144           driver_printf("%.*s=",
   1145                         KIT_SLICE_ARG(p.name.s ? p.name : KIT_SLICE_LIT("?")));
   1146           dbg_translate_loc(s, &p.loc);
   1147           if (dbg_read_value(s, &p.loc, &frame, stack_buf, sizeof(stack_buf),
   1148                              &buf, &alloc, &got) == 0) {
   1149             dbg_print_value(s, p.loc.type, buf, got, 0);
   1150             dbg_release_value_buf(s, buf, alloc);
   1151           } else {
   1152             driver_printf("?");
   1153           }
   1154           first = 0;
   1155         }
   1156         kit_dwarf_param_iter_free(it);
   1157       }
   1158       driver_printf(")");
   1159     }
   1160 
   1161     {
   1162       KitSlice file = KIT_SLICE_NULL;
   1163       uint32_t line = 0;
   1164       uint32_t col = 0;
   1165       KitStatus rc =
   1166           kit_dwarf_addr_to_line(s->dwarf, img_frame.pc, &file, &line, &col);
   1167       if (rc == KIT_OK && file.s) {
   1168         driver_printf(" at %.*s:%u", KIT_SLICE_ARG(file), line);
   1169         if (col) driver_printf(":%u", col);
   1170       } else if (rc == KIT_NOT_FOUND) {
   1171         driver_printf(" [no debug info for this frame]");
   1172       }
   1173     }
   1174     driver_printf("\n");
   1175 
   1176     /* Advance to the caller by following the frame-pointer chain. kit keeps a
   1177      * frame pointer on every backend with a uniform record (fp[0] = caller fp,
   1178      * fp[1] = saved return address), so a memory-reading FP walk is reliable
   1179      * where kit_dwarf_unwind_step is not — the CFI stepper takes no memory
   1180      * provider and so cannot recover a return address spilled to the stack
   1181      * (the common case), terminating after the leaf frame. Reads go through the
   1182      * session; pc/fp/cfa stay runtime addresses, translated per DWARF query. */
   1183     {
   1184       KitArchKind arch = driver_host_target().arch;
   1185       int fpreg = driver_bt_fp_dwarf_reg(arch);
   1186       int ptr = driver_bt_ptr_size(arch);
   1187       uint64_t ra = 0, next_fp = 0;
   1188       if (fpreg < 0) break;
   1189       if (!driver_bt_fp_step(arch, dbg_dwarf_read_mem, s->session,
   1190                              frame.regs[fpreg], &ra, &next_fp))
   1191         break; /* bottom of stack */
   1192       /* Stop at the kit-image boundary: past `main` the chain runs into the
   1193        * session/JIT trampoline and host libc, which carry no symbols and whose
   1194        * depth is host-dependent. */
   1195       if (kit_jit_runtime_to_image(s->jit, ra) == 0) break;
   1196       frame.pc = ra;
   1197       frame.regs[fpreg] = next_fp;
   1198       /* Caller CFA in kit's layout: the address just above the saved pair. */
   1199       frame.cfa = next_fp + 2u * (uint64_t)ptr;
   1200     }
   1201     if (++level > 256) {
   1202       dbg_errf(s, "backtrace truncated at 256 frames");
   1203       break;
   1204     }
   1205   }
   1206 }
   1207 
   1208 /* ============================================================
   1209  * Type-aware value printer
   1210  * ============================================================
   1211  * Walks a KitDwarfType and pretty-prints its byte representation. Used
   1212  * by `p`, `info locals`, `info args`, and the backtrace-arg renderer.
   1213  * Self-recursive for aggregates (struct, union, array). When `type` is
   1214  * NULL (no DWARF type info recovered) the printer falls back to LE-as-u64
   1215  * for small reads and a hex dump for the rest. The function emits the
   1216  * value only — callers print any leading "name = " and the trailing
   1217  * newline. */
   1218 
   1219 static uint64_t dbg_load_le_u(const uint8_t* buf, size_t n) {
   1220   uint64_t v = 0;
   1221   size_t i;
   1222   for (i = 0; i < n && i < 8; ++i) v |= ((uint64_t)buf[i]) << (8 * i);
   1223   return v;
   1224 }
   1225 
   1226 static int64_t dbg_load_le_s(const uint8_t* buf, size_t n) {
   1227   uint64_t v = dbg_load_le_u(buf, n);
   1228   if (n > 0 && n < 8) {
   1229     uint64_t sign = (uint64_t)1 << (8 * n - 1);
   1230     if (v & sign) v |= ~((sign << 1) - 1);
   1231   }
   1232   return (int64_t)v;
   1233 }
   1234 
   1235 static void dbg_indent(int n) {
   1236   int i;
   1237   for (i = 0; i < n; ++i) driver_printf("  ");
   1238 }
   1239 
   1240 static void dbg_print_value(DbgState* s, const KitDwarfType* type,
   1241                             const uint8_t* buf, size_t got, int depth) {
   1242   KitDwarfTypeInfo ti;
   1243 
   1244   if (!type) {
   1245     if (got == 0) {
   1246       driver_printf("<empty>");
   1247       return;
   1248     }
   1249     if (got <= 8) {
   1250       uint64_t v = dbg_load_le_u(buf, got);
   1251       driver_printf("0x%llx (%llu)", (unsigned long long)v,
   1252                     (unsigned long long)v);
   1253       return;
   1254     }
   1255     {
   1256       size_t i;
   1257       driver_printf("{");
   1258       for (i = 0; i < got; ++i) driver_printf(" %02x", buf[i]);
   1259       driver_printf(" }");
   1260       return;
   1261     }
   1262   }
   1263 
   1264   ti = kit_dwarf_type_info(type);
   1265   switch (ti.kind) {
   1266     case KIT_DT_VOID:
   1267       driver_printf("void");
   1268       return;
   1269     case KIT_DT_SINT:
   1270     case KIT_DT_CHAR:
   1271       driver_printf("%lld", (long long)dbg_load_le_s(buf, got));
   1272       return;
   1273     case KIT_DT_UINT:
   1274     case KIT_DT_BOOL:
   1275       driver_printf("%llu", (unsigned long long)dbg_load_le_u(buf, got));
   1276       return;
   1277     case KIT_DT_PTR:
   1278       driver_printf("0x%llx", (unsigned long long)dbg_load_le_u(buf, got));
   1279       return;
   1280     case KIT_DT_FLOAT:
   1281       if (got == 4) {
   1282         union {
   1283           uint32_t u;
   1284           float f;
   1285         } cv;
   1286         cv.u = (uint32_t)dbg_load_le_u(buf, 4);
   1287         driver_printf("%g", (double)cv.f);
   1288       } else if (got == 8) {
   1289         union {
   1290           uint64_t u;
   1291           double d;
   1292         } cv;
   1293         cv.u = dbg_load_le_u(buf, 8);
   1294         driver_printf("%g", cv.d);
   1295       } else {
   1296         size_t i;
   1297         driver_printf("<float-%zu", got);
   1298         for (i = 0; i < got; ++i) driver_printf(" %02x", buf[i]);
   1299         driver_printf(">");
   1300       }
   1301       return;
   1302     case KIT_DT_ENUM: {
   1303       int64_t v = dbg_load_le_s(buf, got);
   1304       KitDwarfEnumIter* it = NULL;
   1305       KitDwarfEnumVal ev;
   1306       KitSlice match = KIT_SLICE_NULL;
   1307       if (kit_dwarf_enum_iter_new(s->dwarf, type, &it) == KIT_OK) {
   1308         for (;;) {
   1309           KitIterResult r = kit_dwarf_enum_iter_next(it, &ev);
   1310           if (r != KIT_ITER_ITEM) break;
   1311           if (ev.value == v) {
   1312             match = ev.name;
   1313             break;
   1314           }
   1315         }
   1316         kit_dwarf_enum_iter_free(it);
   1317       }
   1318       if (match.s)
   1319         driver_printf("%.*s (%lld)", KIT_SLICE_ARG(match), (long long)v);
   1320       else
   1321         driver_printf("%lld", (long long)v);
   1322       return;
   1323     }
   1324     case KIT_DT_TYPEDEF:
   1325       dbg_print_value(s, ti.inner, buf, got, depth);
   1326       return;
   1327     case KIT_DT_ARRAY: {
   1328       uint32_t n = ti.element_count;
   1329       size_t esz = 0;
   1330       uint32_t i;
   1331       if (ti.inner) {
   1332         KitDwarfTypeInfo ein = kit_dwarf_type_info(ti.inner);
   1333         esz = ein.byte_size;
   1334       }
   1335       if (esz == 0 || n == 0 || (size_t)n * esz > got) {
   1336         size_t k;
   1337         driver_printf("{");
   1338         for (k = 0; k < got; ++k) driver_printf(" %02x", buf[k]);
   1339         driver_printf(" }");
   1340         return;
   1341       }
   1342       driver_printf("{\n");
   1343       for (i = 0; i < n; ++i) {
   1344         dbg_indent(depth + 1);
   1345         driver_printf("[%u] = ", i);
   1346         dbg_print_value(s, ti.inner, buf + (size_t)i * esz, esz, depth + 1);
   1347         driver_printf(",\n");
   1348       }
   1349       dbg_indent(depth);
   1350       driver_printf("}");
   1351       return;
   1352     }
   1353     case KIT_DT_STRUCT:
   1354     case KIT_DT_UNION: {
   1355       KitDwarfFieldIter* it = NULL;
   1356       KitDwarfField f;
   1357       driver_printf("{\n");
   1358       if (kit_dwarf_field_iter_new(s->dwarf, type, &it) == KIT_OK) {
   1359         for (;;) {
   1360           KitIterResult r = kit_dwarf_field_iter_next(it, &f);
   1361           if (r != KIT_ITER_ITEM) break;
   1362           size_t fsz = 0;
   1363           dbg_indent(depth + 1);
   1364           driver_printf(
   1365               ".%.*s = ",
   1366               KIT_SLICE_ARG(f.name.len ? f.name : KIT_SLICE_LIT("<anon>")));
   1367           if (f.bit_size) {
   1368             /* Bitfield: read up to 8 bytes spanning the storage
   1369              * unit at byte_offset, shift, mask. */
   1370             size_t off = f.byte_offset;
   1371             size_t take = (off + 8 <= got) ? 8 : (off < got ? got - off : 0);
   1372             uint64_t raw = take ? dbg_load_le_u(buf + off, take) : 0;
   1373             uint64_t mask = (f.bit_size >= 64)
   1374                                 ? (uint64_t)-1
   1375                                 : (((uint64_t)1 << f.bit_size) - 1);
   1376             uint64_t v = (raw >> f.bit_offset) & mask;
   1377             driver_printf("%llu", (unsigned long long)v);
   1378           } else {
   1379             if (f.type) {
   1380               KitDwarfTypeInfo fti = kit_dwarf_type_info(f.type);
   1381               fsz = fti.byte_size;
   1382             }
   1383             if (f.type && fsz > 0 && (size_t)f.byte_offset + fsz <= got) {
   1384               dbg_print_value(s, f.type, buf + f.byte_offset, fsz, depth + 1);
   1385             } else {
   1386               driver_printf("<truncated>");
   1387             }
   1388           }
   1389           driver_printf(",\n");
   1390         }
   1391         kit_dwarf_field_iter_free(it);
   1392       }
   1393       dbg_indent(depth);
   1394       driver_printf("}");
   1395       return;
   1396     }
   1397     case KIT_DT_FUNC:
   1398       driver_printf("<function@0x%llx>",
   1399                     (unsigned long long)dbg_load_le_u(buf, got));
   1400       return;
   1401   }
   1402   driver_printf("<?>");
   1403 }
   1404 
   1405 /* KitDwarfReadMemFn adapter: forwards a DWARF-driven memory read into
   1406  * the JIT session's address space. The user pointer carries the
   1407  * KitJitSession. */
   1408 static KitStatus dbg_dwarf_read_mem(void* user, uint64_t addr, void* dst,
   1409                                     size_t n) {
   1410   KitJitSession* sess = (KitJitSession*)user;
   1411   if (!sess) return KIT_INVALID;
   1412   return kit_jit_session_read_mem(sess, addr, dst, n);
   1413 }
   1414 
   1415 /* Read a variable's bytes into a heap or stack buffer sized for its DIE
   1416  * type. On success returns 0 and sets *buf_out (which may point at
   1417  * stack_buf or at a heap allocation) plus *got_out. The caller frees
   1418  * *buf_out via dbg_release_value_buf when done. */
   1419 static int dbg_read_value(DbgState* s, const KitDwarfVarLoc* loc,
   1420                           const KitUnwindFrame* frame, uint8_t* stack_buf,
   1421                           size_t stack_cap, uint8_t** buf_out,
   1422                           size_t* alloc_out, size_t* got_out) {
   1423   uint8_t* buf = stack_buf;
   1424   size_t alloc = 0;
   1425   size_t cap = stack_cap;
   1426   size_t got = 0;
   1427 
   1428   if (loc->byte_size > cap) {
   1429     alloc = loc->byte_size;
   1430     buf = driver_alloc(s->env, alloc);
   1431     if (!buf) return 1;
   1432     cap = alloc;
   1433   }
   1434   if (kit_dwarf_loc_read(s->dwarf, loc, frame, dbg_dwarf_read_mem, s->session,
   1435                          buf, cap, &got) != KIT_OK) {
   1436     if (alloc) driver_free(s->env, buf, alloc);
   1437     return 1;
   1438   }
   1439   *buf_out = buf;
   1440   *alloc_out = alloc;
   1441   *got_out = got;
   1442   return 0;
   1443 }
   1444 
   1445 static void dbg_release_value_buf(DbgState* s, uint8_t* buf, size_t alloc) {
   1446   if (alloc) driver_free(s->env, buf, alloc);
   1447 }
   1448 
   1449 /* ============================================================
   1450  * `p name`
   1451  * ============================================================ */
   1452 
   1453 static void dbg_cmd_print(DbgState* s, const char* name) {
   1454   KitDwarfVarLoc loc;
   1455   uint8_t stack_buf[64];
   1456   uint8_t* buf;
   1457   size_t alloc;
   1458   size_t got;
   1459 
   1460   if (!s->has_stop) {
   1461     dbg_errf(s, "no program is stopped");
   1462     return;
   1463   }
   1464 
   1465   {
   1466     KitStatus rc =
   1467         s->dwarf ? kit_dwarf_var_at(s->dwarf,
   1468                                     dbg_pc_rt_to_img(s, s->last_stop.regs.pc),
   1469                                     kit_slice_cstr(name), &loc)
   1470                  : KIT_NOT_FOUND;
   1471     if (rc == KIT_OK) {
   1472       dbg_translate_loc(s, &loc);
   1473       if (dbg_read_value(s, &loc, &s->last_stop.regs, stack_buf,
   1474                          sizeof(stack_buf), &buf, &alloc, &got) != 0) {
   1475         dbg_errf(s, "could not read %.*s",
   1476                     KIT_SLICE_ARG(kit_slice_cstr(name)));
   1477         return;
   1478       }
   1479       driver_printf("%.*s = ", KIT_SLICE_ARG(kit_slice_cstr(name)));
   1480       dbg_print_value(s, loc.type, buf, got, 0);
   1481       driver_printf("\n");
   1482       dbg_release_value_buf(s, buf, alloc);
   1483       return;
   1484     }
   1485 
   1486     /* DWARF didn't know about it — try a global symbol. */
   1487     {
   1488       void* p = kit_jit_lookup(s->jit, kit_slice_cstr(name));
   1489       if (p) {
   1490         union {
   1491           void* p;
   1492           uint64_t u;
   1493         } cv;
   1494         cv.p = p;
   1495         driver_printf("%.*s = 0x%llx (no DWARF type info)\n",
   1496                       KIT_SLICE_ARG(kit_slice_cstr(name)),
   1497                       (unsigned long long)cv.u);
   1498         return;
   1499       }
   1500     }
   1501     dbg_errf(s, "no variable or symbol named '%.*s'",
   1502                 KIT_SLICE_ARG(kit_slice_cstr(name)));
   1503   }
   1504 }
   1505 
   1506 /* ============================================================
   1507  * `set NAME VALUE`
   1508  * ============================================================
   1509  * Writes a 64-bit value into a variable. Routes to write_mem for
   1510  * frame-relative and global locations, set_regs for register-resident
   1511  * variables. v1 supports integer/pointer scalars only — float and
   1512  * aggregate writes are out of scope. */
   1513 
   1514 static void dbg_cmd_set(DbgState* s, const char* name, uint64_t value) {
   1515   KitDwarfVarLoc loc;
   1516   uint8_t buf[8];
   1517   size_t sz;
   1518   size_t i;
   1519 
   1520   if (!s->has_stop) {
   1521     dbg_errf(s, "no program is stopped");
   1522     return;
   1523   }
   1524   {
   1525     KitStatus rc =
   1526         s->dwarf ? kit_dwarf_var_at(s->dwarf,
   1527                                     dbg_pc_rt_to_img(s, s->last_stop.regs.pc),
   1528                                     kit_slice_cstr(name), &loc)
   1529                  : KIT_NOT_FOUND;
   1530     if (rc != KIT_OK) {
   1531       dbg_errf(s, "no variable named '%.*s'",
   1532                   KIT_SLICE_ARG(kit_slice_cstr(name)));
   1533       return;
   1534     }
   1535   }
   1536   dbg_translate_loc(s, &loc);
   1537 
   1538   sz = (loc.byte_size == 0 || loc.byte_size > 8) ? 8 : loc.byte_size;
   1539   for (i = 0; i < sz; ++i) buf[i] = (uint8_t)(value >> (8 * i));
   1540 
   1541   switch (loc.kind) {
   1542     case KIT_DLOC_FRAME_OFS: {
   1543       uint64_t addr =
   1544           s->last_stop.regs.cfa + (uint64_t)(int64_t)loc.v.frame_ofs;
   1545       if (kit_jit_session_write_mem(s->session, addr, buf, sz) != KIT_OK) {
   1546         dbg_errf(s, "memory write failed");
   1547       }
   1548       return;
   1549     }
   1550     case KIT_DLOC_GLOBAL:
   1551       if (kit_jit_session_write_mem(s->session, loc.v.global, buf, sz) !=
   1552           KIT_OK) {
   1553         dbg_errf(s, "memory write failed");
   1554       }
   1555       return;
   1556     case KIT_DLOC_REG: {
   1557       KitUnwindFrame fr = s->last_stop.regs;
   1558       if (loc.v.reg >= 32) {
   1559         dbg_errf(s, "register %u outside the snapshot range",
   1560                     loc.v.reg);
   1561         return;
   1562       }
   1563       fr.regs[loc.v.reg] = value;
   1564       if (kit_jit_session_set_regs(s->session, &fr) != KIT_OK) {
   1565         dbg_errf(s, "register write failed");
   1566         return;
   1567       }
   1568       s->last_stop.regs = fr;
   1569       return;
   1570     }
   1571     case KIT_DLOC_EXPR:
   1572       dbg_errf(s, "cannot set '%.*s': location is a DWARF expression",
   1573                   KIT_SLICE_ARG(kit_slice_cstr(name)));
   1574       return;
   1575   }
   1576 }
   1577 
   1578 /* ============================================================
   1579  * `jump ADDR`
   1580  * ============================================================
   1581  * Move PC without resuming. The session validates that the new PC lies
   1582  * within the JIT image. */
   1583 
   1584 static void dbg_cmd_jump(DbgState* s, uint64_t pc) {
   1585   KitUnwindFrame fr;
   1586   if (!s->has_stop) {
   1587     dbg_errf(s, "no program is stopped");
   1588     return;
   1589   }
   1590   fr = s->last_stop.regs;
   1591   fr.pc = pc;
   1592   if (kit_jit_session_set_regs(s->session, &fr) != 0) {
   1593     dbg_errf(s, "jump failed (pc 0x%llx outside image?)",
   1594                 (unsigned long long)pc);
   1595     return;
   1596   }
   1597   s->last_stop.regs = fr;
   1598   driver_printf("PC set to 0x%llx\n", (unsigned long long)pc);
   1599 }
   1600 
   1601 /* ============================================================
   1602  * `info locals` / `info args` / `info reg`
   1603  * ============================================================ */
   1604 
   1605 static void dbg_cmd_info_vars(DbgState* s, uint32_t mask, const char* label) {
   1606   KitDwarfVarIter* it = NULL;
   1607   KitDwarfVar v;
   1608   int printed = 0;
   1609 
   1610   if (!s->has_stop) {
   1611     dbg_errf(s, "no program is stopped");
   1612     return;
   1613   }
   1614   if (!s->dwarf) {
   1615     dbg_errf(s, "no DWARF: %.*s unavailable",
   1616                 KIT_SLICE_ARG(kit_slice_cstr(label)));
   1617     return;
   1618   }
   1619 
   1620   if (kit_dwarf_vars_at_new(s->dwarf, dbg_pc_rt_to_img(s, s->last_stop.regs.pc),
   1621                             mask, &it) != KIT_OK) {
   1622     driver_printf("No %.*s.\n", KIT_SLICE_ARG(kit_slice_cstr(label)));
   1623     return;
   1624   }
   1625   for (;;) {
   1626     KitIterResult r = kit_dwarf_vars_at_next(it, &v);
   1627     if (r != KIT_ITER_ITEM) break;
   1628     uint8_t stack_buf[64];
   1629     uint8_t* buf;
   1630     size_t alloc;
   1631     size_t got;
   1632     printed = 1;
   1633     dbg_translate_loc(s, &v.loc);
   1634     if (dbg_read_value(s, &v.loc, &s->last_stop.regs, stack_buf,
   1635                        sizeof(stack_buf), &buf, &alloc, &got) != 0) {
   1636       driver_printf("  %.*s = <unreadable>\n", KIT_SLICE_ARG(v.name));
   1637       continue;
   1638     }
   1639     driver_printf("  %.*s = ", KIT_SLICE_ARG(v.name));
   1640     dbg_print_value(s, v.loc.type, buf, got, 1);
   1641     driver_printf("\n");
   1642     dbg_release_value_buf(s, buf, alloc);
   1643   }
   1644   kit_dwarf_vars_at_free(it);
   1645   if (!printed)
   1646     driver_printf("No %.*s.\n", KIT_SLICE_ARG(kit_slice_cstr(label)));
   1647 }
   1648 
   1649 static void dbg_cmd_info_reg(DbgState* s) {
   1650   KitArchKind arch = driver_host_target().arch;
   1651   uint32_t n = kit_arch_register_count(arch);
   1652   uint32_t i;
   1653 
   1654   if (!s->has_stop) {
   1655     dbg_errf(s, "no program is stopped");
   1656     return;
   1657   }
   1658   if (n == 0) {
   1659     dbg_errf(s, "no register table for this arch");
   1660     return;
   1661   }
   1662   driver_printf("pc     0x%016llx\n", (unsigned long long)s->last_stop.regs.pc);
   1663   driver_printf("cfa    0x%016llx\n",
   1664                 (unsigned long long)s->last_stop.regs.cfa);
   1665   for (i = 0; i < n; ++i) {
   1666     KitArchReg r;
   1667     if (kit_arch_register_at(arch, i, &r) != KIT_OK) continue;
   1668     if (r.dwarf_idx >= 32) continue; /* outside KitUnwindFrame.regs */
   1669     driver_printf("%-6.*s 0x%016llx\n", KIT_SLICE_ARG(r.name),
   1670                   (unsigned long long)s->last_stop.regs.regs[r.dwarf_idx]);
   1671   }
   1672 }
   1673 
   1674 /* ============================================================
   1675  * `info functions [PATTERN]` / `info variables [PATTERN]`
   1676  * ============================================================ */
   1677 
   1678 /* Tiny glob matcher: '*' matches any run, '?' matches any single byte.
   1679  * NULL pattern matches every name. */
   1680 static int dbg_glob(const char* pat, const char* s) {
   1681   if (!pat) return 1;
   1682   while (*pat && *s) {
   1683     if (*pat == '*') {
   1684       if (pat[1] == '\0') return 1;
   1685       while (*s) {
   1686         if (dbg_glob(pat + 1, s)) return 1;
   1687         ++s;
   1688       }
   1689       return dbg_glob(pat + 1, s);
   1690     }
   1691     if (*pat != '?' && *pat != *s) return 0;
   1692     ++pat;
   1693     ++s;
   1694   }
   1695   while (*pat == '*') ++pat;
   1696   return *pat == '\0' && *s == '\0';
   1697 }
   1698 
   1699 static void dbg_cmd_info_syms(DbgState* s, KitSymKind want,
   1700                               const char* pattern) {
   1701   KitJitSymIter* it = NULL;
   1702   KitJitSym sym;
   1703   int printed = 0;
   1704 
   1705   if (kit_jit_sym_iter_new(s->jit, &it) != KIT_OK) {
   1706     dbg_errf(s, "symbol enumeration unavailable");
   1707     return;
   1708   }
   1709   for (;;) {
   1710     KitIterResult r = kit_jit_sym_iter_next(it, &sym);
   1711     if (r != KIT_ITER_ITEM) break;
   1712     if (sym.kind != want) continue;
   1713     if (pattern && !dbg_glob(pattern, sym.name.s)) continue;
   1714     driver_printf("0x%016llx  %.*s\n", (unsigned long long)sym.addr,
   1715                   KIT_SLICE_ARG(sym.name));
   1716     printed = 1;
   1717   }
   1718   kit_jit_sym_iter_free(it);
   1719   if (!printed) driver_printf("(none)\n");
   1720 }
   1721 
   1722 static int dbg_refresh_dwarf(DbgState* s) {
   1723   if (s->dwarf) {
   1724     kit_dwarf_free(s->dwarf);
   1725     s->dwarf = NULL;
   1726   }
   1727   s->view = kit_jit_view(s->jit);
   1728   if (s->view) {
   1729     if (kit_dwarf_open(&s->ctx, s->view, &s->dwarf) != KIT_OK) s->dwarf = NULL;
   1730     if (s->dwarf && s->session) {
   1731       kit_jit_session_attach_dwarf(s->session, s->dwarf);
   1732     }
   1733   } else if (s->session) {
   1734     kit_jit_session_attach_dwarf(s->session, NULL);
   1735   }
   1736   return 0;
   1737 }
   1738 
   1739 static int dbg_buf_append(DbgState* s, char** buf, size_t* len, size_t* cap,
   1740                           const char* src, size_t n) {
   1741   if (*len + n + 1u > *cap) {
   1742     size_t nc = *cap ? *cap * 2u : 1024u;
   1743     char* nb;
   1744     while (nc < *len + n + 1u) nc *= 2u;
   1745     nb = (char*)s->env->heap->realloc(s->env->heap, *buf, *cap, nc,
   1746                                       _Alignof(char));
   1747     if (!nb) return 1;
   1748     *buf = nb;
   1749     *cap = nc;
   1750   }
   1751   driver_memcpy(*buf + *len, src, n);
   1752   *len += n;
   1753   (*buf)[*len] = '\0';
   1754   return 0;
   1755 }
   1756 
   1757 static DbgSource* dbg_source_find(DbgState* s, KitSlice name) {
   1758   uint32_t i;
   1759   if (!s || !name.s || !name.len) return NULL;
   1760   for (i = 0; i < s->nsources; ++i) {
   1761     DbgSource* src = &s->sources[i];
   1762     if (src->name && src->name_size == name.len + 1u &&
   1763         memcmp(src->name, name.s, name.len) == 0) {
   1764       return src;
   1765     }
   1766   }
   1767   return NULL;
   1768 }
   1769 
   1770 static DbgSource* dbg_source_grow(DbgState* s) {
   1771   uint32_t nc;
   1772   size_t old_size, new_size;
   1773   DbgSource* ns;
   1774   if (s->nsources < s->sources_cap) return &s->sources[s->nsources];
   1775 
   1776   nc = s->sources_cap ? s->sources_cap * 2 : 8;
   1777   old_size = (size_t)s->sources_cap * sizeof(*s->sources);
   1778   new_size = (size_t)nc * sizeof(*s->sources);
   1779   ns = (DbgSource*)s->env->heap->realloc(s->env->heap, s->sources, old_size,
   1780                                          new_size, _Alignof(DbgSource));
   1781   if (!ns) return NULL;
   1782   {
   1783     char* z = (char*)ns + old_size;
   1784     size_t n = new_size - old_size;
   1785     size_t j;
   1786     for (j = 0; j < n; ++j) z[j] = 0;
   1787   }
   1788   s->sources = ns;
   1789   s->sources_cap = nc;
   1790   return &s->sources[s->nsources];
   1791 }
   1792 
   1793 static int dbg_source_intern_name(DbgState* s, KitSlice name,
   1794                                   const char** out) {
   1795   DbgSource* src;
   1796   size_t name_size;
   1797   char* name_copy;
   1798   if (out) *out = NULL;
   1799   if (!s || !name.s || !name.len) return 1;
   1800 
   1801   src = dbg_source_find(s, name);
   1802   if (src) {
   1803     if (out) *out = src->name;
   1804     return 0;
   1805   }
   1806 
   1807   name_copy = dbg_dup(s->env, name.s, name.len, &name_size);
   1808   if (!name_copy) return 1;
   1809   src = dbg_source_grow(s);
   1810   if (!src) {
   1811     driver_free(s->env, name_copy, name_size);
   1812     return 1;
   1813   }
   1814   src->name = name_copy;
   1815   src->name_size = name_size;
   1816   s->nsources++;
   1817   if (out) *out = src->name;
   1818   return 0;
   1819 }
   1820 
   1821 static int dbg_source_cache_put(DbgState* s, KitSlice name, const char* data,
   1822                                 size_t len) {
   1823   DbgSource* src;
   1824   uint8_t* data_copy;
   1825   size_t data_size = len ? len : 1u;
   1826   if (!s || !name.s || !name.len || !data) return 1;
   1827 
   1828   data_copy = (uint8_t*)driver_alloc(s->env, data_size);
   1829   if (!data_copy) return 1;
   1830   if (len) driver_memcpy(data_copy, data, len);
   1831 
   1832   src = dbg_source_find(s, name);
   1833   if (!src) {
   1834     if (dbg_source_intern_name(s, name, NULL) != 0) {
   1835       driver_free(s->env, data_copy, data_size);
   1836       return 1;
   1837     }
   1838     src = dbg_source_find(s, name);
   1839     if (!src) {
   1840       driver_free(s->env, data_copy, data_size);
   1841       return 1;
   1842     }
   1843   }
   1844   if (src->data) {
   1845     driver_free(s->env, src->data, src->data_size);
   1846   }
   1847 
   1848   src->data = data_copy;
   1849   src->data_size = data_size;
   1850   src->len = len;
   1851   return 0;
   1852 }
   1853 
   1854 static int dbg_brace_delta(const char* p) {
   1855   int d = 0;
   1856   while (*p) {
   1857     if (*p == '{')
   1858       ++d;
   1859     else if (*p == '}')
   1860       --d;
   1861     ++p;
   1862   }
   1863   return d;
   1864 }
   1865 
   1866 /* Caller buffer sizes for the REPL filename builders below. ".<ext>" and
   1867  * "<dbg-jit.<ext>>" with the canonical frontend extension comfortably fit. */
   1868 #define DBG_JIT_SUFFIX_CAP 16
   1869 #define DBG_JIT_NAME_CAP 32
   1870 /* DbgState.default_jit_name_buf must match DBG_JIT_NAME_CAP (it is sized with a
   1871  * literal because it precedes this macro in the file). */
   1872 _Static_assert(sizeof(((DbgState*)0)->default_jit_name_buf) == DBG_JIT_NAME_CAP,
   1873                "default_jit_name_buf must equal DBG_JIT_NAME_CAP");
   1874 
   1875 /* Canonical language name for the JIT REPL (kit_language_name with a "c"
   1876  * fallback so C and any unnamed/out-of-range language render as "c", matching
   1877  * the former hardcoded default). The result is borrowed static storage. */
   1878 static const char* dbg_jit_language_name(KitCompiler* c, KitLanguage lang) {
   1879   const char* name = kit_language_name(c, lang);
   1880   return name ? name : "c";
   1881 }
   1882 
   1883 /* Append NUL-terminated `s` into `buf[cap]` starting at `*pos`, advancing
   1884  * `*pos`. Truncates rather than overflowing; the result is always
   1885  * NUL-terminated when cap > 0. (dbg.c builds strings with driver_* helpers
   1886  * rather than stdio.) */
   1887 static void dbg_str_append(char* buf, size_t cap, size_t* pos, const char* s) {
   1888   size_t n = driver_strlen(s);
   1889   size_t room;
   1890   if (*pos >= cap) return;
   1891   room = cap - 1u - *pos; /* leave space for NUL */
   1892   if (n > room) n = room;
   1893   driver_memcpy(buf + *pos, s, n);
   1894   *pos += n;
   1895   buf[*pos] = '\0';
   1896 }
   1897 
   1898 /* Build the dotted file suffix (e.g. ".toy") for `lang` into `buf`, defaulting
   1899  * to ".c" for C and any unnamed/out-of-range language. The canonical bare
   1900  * extension is the single source of truth (kit_language_default_extension);
   1901  * the leading "." is tool presentation. Returns `buf`. */
   1902 static const char* dbg_jit_language_suffix(KitCompiler* c, KitLanguage lang,
   1903                                            char* buf, size_t cap) {
   1904   const char* ext = kit_language_default_extension(c, lang);
   1905   size_t pos = 0;
   1906   if (lang == KIT_LANG_C || !ext) ext = "c";
   1907   if (cap > 0) buf[0] = '\0';
   1908   dbg_str_append(buf, cap, &pos, ".");
   1909   dbg_str_append(buf, cap, &pos, ext);
   1910   return buf;
   1911 }
   1912 
   1913 /* Build the default synthesized REPL source name (e.g. "<dbg-jit.toy>") for
   1914  * `lang` into `buf`, derived from the canonical suffix. Returns `buf`. */
   1915 static const char* dbg_jit_default_name(KitCompiler* c, KitLanguage lang,
   1916                                         char* buf, size_t cap) {
   1917   char suffix[DBG_JIT_SUFFIX_CAP];
   1918   size_t pos = 0;
   1919   dbg_jit_language_suffix(c, lang, suffix, sizeof suffix);
   1920   if (cap > 0) buf[0] = '\0';
   1921   dbg_str_append(buf, cap, &pos, "<dbg-jit");
   1922   dbg_str_append(buf, cap, &pos, suffix);
   1923   dbg_str_append(buf, cap, &pos, ">");
   1924   return buf;
   1925 }
   1926 
   1927 static int dbg_jit_uses_default_name(KitCompiler* c, KitLanguage lang,
   1928                                      const char* input_name) {
   1929   char def[DBG_JIT_NAME_CAP];
   1930   return !input_name ||
   1931          driver_streq(input_name,
   1932                       dbg_jit_default_name(c, lang, def, sizeof def));
   1933 }
   1934 
   1935 static int dbg_make_repl_source_name(KitCompiler* c, KitLanguage lang,
   1936                                      uint64_t id, char* out, size_t cap) {
   1937   const char* prefix = "<dbg-jit-";
   1938   char suffix_buf[DBG_JIT_SUFFIX_CAP];
   1939   const char* suffix =
   1940       dbg_jit_language_suffix(c, lang, suffix_buf, sizeof suffix_buf);
   1941   char num[32];
   1942   size_t prefix_len = driver_strlen(prefix);
   1943   size_t suffix_len = driver_strlen(suffix);
   1944   size_t num_len = dbg_u64_dec(num, sizeof(num), id);
   1945   size_t need;
   1946   if (!num_len) return 1;
   1947   need = prefix_len + num_len + suffix_len + 2u;
   1948   if (need > cap) return 1;
   1949   driver_memcpy(out, prefix, prefix_len);
   1950   driver_memcpy(out + prefix_len, num, num_len);
   1951   driver_memcpy(out + prefix_len + num_len, suffix, suffix_len);
   1952   out[prefix_len + num_len + suffix_len] = '>';
   1953   out[prefix_len + num_len + suffix_len + 1u] = '\0';
   1954   return 0;
   1955 }
   1956 
   1957 static KitLanguage dbg_jit_language_for_tag(DbgState* s, const char* tag,
   1958                                             const char** name_out) {
   1959   if (!tag || !*tag) {
   1960     if (name_out) *name_out = s->default_jit_name;
   1961     return s->default_jit_lang;
   1962   }
   1963   if (driver_streq(tag, "c")) {
   1964     if (name_out) *name_out = "<dbg-jit.c>";
   1965     return KIT_LANG_C;
   1966   }
   1967   if (driver_streq(tag, "toy")) {
   1968     if (name_out) *name_out = "<dbg-jit.toy>";
   1969     return KIT_LANG_TOY;
   1970   }
   1971   if (driver_streq(tag, "asm") || driver_streq(tag, "s")) {
   1972     if (name_out) *name_out = "<dbg-jit.s>";
   1973     return KIT_LANG_ASM;
   1974   }
   1975   if (driver_streq(tag, "wasm") || driver_streq(tag, "wat")) {
   1976     if (name_out) *name_out = "<dbg-jit.wat>";
   1977     return KIT_LANG_WASM;
   1978   }
   1979   if (name_out) *name_out = tag;
   1980   return kit_language_for_path(s->compiler, tag);
   1981 }
   1982 
   1983 static KitStatus dbg_compile_session_for(DbgState* s, KitLanguage lang,
   1984                                          KitCompileSession** out) {
   1985   KitCompileSessionOptions sopts;
   1986   KitStatus st;
   1987 
   1988   if (!s || !out || (unsigned)lang >= KIT_LANG_COUNT) return KIT_INVALID;
   1989   *out = NULL;
   1990   if (s->compile_sessions[lang]) {
   1991     *out = s->compile_sessions[lang];
   1992     return KIT_OK;
   1993   }
   1994   {
   1995     KitCompileSessionOptions z = {0};
   1996     sopts = z;
   1997   }
   1998   sopts.lang = lang;
   1999   sopts.compile.code = s->copts.code;
   2000   sopts.compile.diagnostics = s->copts.diagnostics;
   2001   sopts.compile.preprocess = s->pp;
   2002   st = kit_compile_session_new(s->compiler, &sopts, &s->compile_sessions[lang]);
   2003   if (st != KIT_OK) return st;
   2004   *out = s->compile_sessions[lang];
   2005   return KIT_OK;
   2006 }
   2007 
   2008 static int dbg_jit_compile_append_ex(DbgState* s, KitLanguage lang,
   2009                                      const char* input_name, const char* src,
   2010                                      size_t len,
   2011                                      KitFrontendInputKind input_kind,
   2012                                      const char* repl_entry_name) {
   2013   KitSourceInput sin;
   2014   KitCompileSession* session = NULL;
   2015   KitObjBuilder* ob = NULL;
   2016   KitStatus st;
   2017   char generated_name[96];
   2018   char default_name[DBG_JIT_NAME_CAP];
   2019   const char* effective_name =
   2020       input_name ? input_name
   2021                  : dbg_jit_default_name(s->compiler, lang, default_name,
   2022                                         sizeof default_name);
   2023   uint64_t source_id = s->source_counter + 1u;
   2024   int generated_source_name = 0;
   2025 
   2026   s->jit_counter++;
   2027   if (input_kind == KIT_FRONTEND_INPUT_REPL_TOPLEVEL &&
   2028       dbg_jit_uses_default_name(s->compiler, lang, input_name)) {
   2029     if (dbg_make_repl_source_name(s->compiler, lang, source_id, generated_name,
   2030                                   sizeof(generated_name)) != 0) {
   2031       dbg_errf(s, "repl source name overflow");
   2032       return 1;
   2033     }
   2034     if (dbg_source_intern_name(s, kit_slice_cstr(generated_name),
   2035                                &effective_name) != 0) {
   2036       dbg_errf(s, "out of memory naming repl source");
   2037       return 1;
   2038     }
   2039     generated_source_name = 1;
   2040   }
   2041   memset(&sin, 0, sizeof(sin));
   2042   sin.name = kit_slice_cstr(effective_name);
   2043   sin.bytes.data = (const uint8_t*)src;
   2044   sin.bytes.len = len;
   2045   sin.lang = lang;
   2046   sin.input_kind = input_kind;
   2047   sin.repl_entry_name = kit_slice_cstr(repl_entry_name);
   2048   st = dbg_compile_session_for(s, lang, &session);
   2049   /* Stage the compile: on success the frontend's durable declarations are left
   2050    * pending so we only commit them once the object has actually been published
   2051    * into the JIT image. A failed compile is already rolled back internally. */
   2052   if (st == KIT_OK) st = kit_compile_session_stage(session, &sin, &ob);
   2053   if (st != KIT_OK || !ob) {
   2054     if (ob) kit_obj_builder_free(ob);
   2055     dbg_errf(s, "jit compile failed");
   2056     return 1;
   2057   }
   2058   {
   2059     KitLinkSessionOptions lopts = {0};
   2060     KitLinkSession* link = NULL;
   2061     KitJitPublishOptions popts = {0};
   2062     KitJitPublishResult pres;
   2063     lopts.output_kind = KIT_LINK_OUTPUT_RELOCATABLE;
   2064     st = kit_link_session_new(s->compiler, &lopts, &link);
   2065     if (st == KIT_OK) st = kit_link_session_add_obj(link, ob);
   2066     popts.kind = KIT_JIT_PUBLISH_APPEND_OBJECTS;
   2067     popts.link = link;
   2068     if (st == KIT_OK) st = kit_jit_publish(s->jit, &popts, &pres);
   2069     kit_link_session_free(link);
   2070   }
   2071   if (st != KIT_OK) {
   2072     /* Publish rejected the object (e.g. a duplicate global). Roll back the
   2073      * staged declarations so the frontend never advertises a symbol the JIT
   2074      * image does not have. */
   2075     kit_compile_session_abort(session);
   2076     dbg_errf(s, "jit append failed");
   2077     return 1;
   2078   }
   2079   /* Published: make the staged declarations durable. */
   2080   kit_compile_session_commit(session);
   2081   if (generated_source_name) s->source_counter = source_id;
   2082   {
   2083     /* Cache the verbatim toplevel text only for frontends that re-read
   2084      * earlier toplevel source on later compiles (toy today), per the
   2085      * frontend's static capability rather than a hard-coded language. */
   2086     KitFrontendCaps caps = {0};
   2087     int cache_source =
   2088         kit_frontend_caps(s->compiler, lang, &caps) == KIT_OK &&
   2089         caps.cache_repl_toplevel_source;
   2090     if (cache_source && input_kind == KIT_FRONTEND_INPUT_REPL_TOPLEVEL &&
   2091         dbg_source_cache_put(s, sin.name, src, len) != 0) {
   2092       dbg_errf(s, "out of memory caching source for list");
   2093     }
   2094   }
   2095   dbg_refresh_dwarf(s);
   2096   return 0;
   2097 }
   2098 
   2099 static int dbg_jit_compile_append(DbgState* s, KitLanguage lang,
   2100                                   const char* input_name, const char* src,
   2101                                   size_t len) {
   2102   return dbg_jit_compile_append_ex(s, lang, input_name, src, len,
   2103                                    KIT_FRONTEND_INPUT_REPL_TOPLEVEL, NULL);
   2104 }
   2105 
   2106 static int dbg_parse_jit_lang_arg(DbgState* s, const char* rest,
   2107                                   KitLanguage* lang_out,
   2108                                   const char** input_name_out,
   2109                                   const char** after_out) {
   2110   const char* p = rest;
   2111   const char* input_name = NULL;
   2112   KitLanguage lang;
   2113 
   2114   while (*p && dbg_isspace((unsigned char)*p)) ++p;
   2115   if (*p && *p != '{') {
   2116     const char* tag = p;
   2117     size_t tag_n;
   2118     char tag_buf[64];
   2119     while (*p && !dbg_isspace((unsigned char)*p) && *p != '{') ++p;
   2120     tag_n = (size_t)(p - tag);
   2121     if (tag_n == 0 || tag_n >= sizeof(tag_buf)) {
   2122       dbg_errf(s, "language/name is too long");
   2123       return 1;
   2124     }
   2125     driver_memcpy(tag_buf, tag, tag_n);
   2126     tag_buf[tag_n] = '\0';
   2127     lang = dbg_jit_language_for_tag(s, tag_buf, &input_name);
   2128     if (input_name == tag_buf &&
   2129         dbg_source_intern_name(s, kit_slice_cstr(input_name), &input_name) !=
   2130             0) {
   2131       dbg_errf(s, "out of memory naming repl source");
   2132       return 1;
   2133     }
   2134     while (*p && dbg_isspace((unsigned char)*p)) ++p;
   2135   } else {
   2136     lang = dbg_jit_language_for_tag(s, NULL, &input_name);
   2137   }
   2138 
   2139   *lang_out = lang;
   2140   *input_name_out = input_name;
   2141   *after_out = p;
   2142   return 0;
   2143 }
   2144 
   2145 static void dbg_cmd_jit(DbgState* s, const char* rest) {
   2146   char* src = NULL;
   2147   size_t len = 0, cap = 0;
   2148   const char* p;
   2149   const char* input_name;
   2150   KitLanguage lang;
   2151   int depth;
   2152 
   2153   if (dbg_parse_jit_lang_arg(s, rest, &lang, &input_name, &p) != 0) return;
   2154   if (*p != '{') {
   2155     dbg_errf(s, "usage: jit [c|toy|asm|name.ext] { ... }");
   2156     return;
   2157   }
   2158   ++p;
   2159   depth = 1 + dbg_brace_delta(p);
   2160   {
   2161     const char* end = p + driver_strlen(p);
   2162     if (depth <= 0) {
   2163       while (end > p && end[-1] != '}') --end;
   2164       if (end > p) --end;
   2165     }
   2166     if (dbg_buf_append(s, &src, &len, &cap, p, (size_t)(end - p)) != 0)
   2167       goto oom;
   2168     if (dbg_buf_append(s, &src, &len, &cap, "\n", 1) != 0) goto oom;
   2169   }
   2170   while (depth > 0) {
   2171     char line[LINE_CAP];
   2172     int n;
   2173     driver_printf("     > ");
   2174     driver_flush_stdout();
   2175     n = driver_read_line(line, sizeof(line));
   2176     if (n <= 0) {
   2177       dbg_errf(s, "unterminated jit block");
   2178       goto out;
   2179     }
   2180     depth += dbg_brace_delta(line);
   2181     if (depth <= 0) {
   2182       char* close = line;
   2183       while (*close && *close != '}') ++close;
   2184       if (dbg_buf_append(s, &src, &len, &cap, line, (size_t)(close - line)) !=
   2185           0)
   2186         goto oom;
   2187       if (dbg_buf_append(s, &src, &len, &cap, "\n", 1) != 0) goto oom;
   2188       break;
   2189     }
   2190     if (dbg_buf_append(s, &src, &len, &cap, line, driver_strlen(line)) != 0)
   2191       goto oom;
   2192     if (dbg_buf_append(s, &src, &len, &cap, "\n", 1) != 0) goto oom;
   2193   }
   2194 
   2195   (void)dbg_jit_compile_append(s, lang, input_name, src, len);
   2196   goto out;
   2197 
   2198 oom:
   2199   dbg_errf(s, "out of memory");
   2200 out:
   2201   if (src) driver_free(s->env, src, cap);
   2202 }
   2203 
   2204 static void dbg_cmd_edit(DbgState* s, const char* rest) {
   2205   KitLanguage lang;
   2206   const char* input_name;
   2207   const char* p;
   2208   uint8_t* src = NULL;
   2209   size_t len = 0;
   2210   char suffix[DBG_JIT_SUFFIX_CAP];
   2211 
   2212   if (dbg_parse_jit_lang_arg(s, rest, &lang, &input_name, &p) != 0) return;
   2213   if (*p) {
   2214     dbg_errf(s, "usage: edit [c|toy|asm|name.ext]");
   2215     return;
   2216   }
   2217   if (!driver_edit_temp(s->env,
   2218                         dbg_jit_language_suffix(s->compiler, lang, suffix,
   2219                                                 sizeof suffix),
   2220                         NULL, 0, &src, &len)) {
   2221     dbg_errf(s, "editor failed");
   2222     return;
   2223   }
   2224   if (len == 0) {
   2225     dbg_errf(s, "empty editor buffer; nothing appended");
   2226     goto out;
   2227   }
   2228   (void)dbg_jit_compile_append(s, lang, input_name, (const char*)src, len);
   2229 
   2230 out:
   2231   driver_free(s->env, src, len);
   2232 }
   2233 
   2234 static void dbg_cmd_language(DbgState* s, const char* rest) {
   2235   char tmp[WORD_CAP];
   2236   const char* p = rest;
   2237   size_t n = 0;
   2238   KitLanguage lang;
   2239   const char* name;
   2240 
   2241   while (*p && dbg_isspace((unsigned char)*p)) ++p;
   2242   while (p[n] && !dbg_isspace((unsigned char)p[n])) ++n;
   2243   if (n == 0) {
   2244     const KitPreprocessOptions* pp = &s->pp;
   2245     driver_printf("Language: %.*s\n",
   2246                   KIT_SLICE_ARG(kit_slice_cstr(dbg_jit_language_name(
   2247                       s->compiler, s->default_jit_lang))));
   2248     driver_printf(
   2249         "Language options: input=%.*s opt=-O%d debug=%.*s includes=%u "
   2250         "system-includes=%u defines=%u undefines=%u session=%.*s\n",
   2251         KIT_SLICE_ARG(kit_slice_cstr(s->default_jit_name)),
   2252         s->copts.code.opt_level,
   2253         KIT_SLICE_ARG(kit_slice_cstr(s->copts.code.debug_info ? "on" : "off")),
   2254         (unsigned)pp->ninclude_dirs, (unsigned)pp->nsystem_include_dirs,
   2255         (unsigned)pp->ndefines, (unsigned)pp->nundefines,
   2256         KIT_SLICE_ARG(kit_slice_cstr(s->compile_sessions[s->default_jit_lang]
   2257                                          ? "cached"
   2258                                          : "not-created")));
   2259     return;
   2260   }
   2261   if (n >= sizeof(tmp)) {
   2262     dbg_errf(s, "usage: :language c|toy|asm|wat|wasm");
   2263     return;
   2264   }
   2265   driver_memcpy(tmp, p, n);
   2266   tmp[n] = '\0';
   2267   lang = dbg_jit_language_for_tag(s, tmp, &name);
   2268   (void)name;
   2269   if (lang == KIT_LANG_COUNT) {
   2270     dbg_errf(s, "unsupported language: %.*s",
   2271                 KIT_SLICE_ARG(kit_slice_cstr(tmp)));
   2272     return;
   2273   }
   2274   s->default_jit_lang = lang;
   2275   s->default_jit_name = dbg_jit_default_name(
   2276       s->compiler, lang, s->default_jit_name_buf, sizeof s->default_jit_name_buf);
   2277   driver_printf(
   2278       "Language: %.*s\n",
   2279       KIT_SLICE_ARG(kit_slice_cstr(dbg_jit_language_name(s->compiler, lang))));
   2280 }
   2281 
   2282 static size_t dbg_u64_dec(char* dst, size_t cap, uint64_t v) {
   2283   char tmp[32];
   2284   size_t n = 0;
   2285   size_t i;
   2286   if (!dst || cap == 0) return 0;
   2287   if (v == 0) {
   2288     if (cap < 2u) return 0;
   2289     dst[0] = '0';
   2290     dst[1] = '\0';
   2291     return 1;
   2292   }
   2293   while (v && n < sizeof(tmp)) {
   2294     tmp[n++] = (char)('0' + (v % 10u));
   2295     v /= 10u;
   2296   }
   2297   if (n + 1u > cap) return 0;
   2298   for (i = 0; i < n; ++i) dst[i] = tmp[n - 1u - i];
   2299   dst[n] = '\0';
   2300   return n;
   2301 }
   2302 
   2303 static int dbg_call_u64_entry(DbgState* s, void* entry, const uint64_t* args,
   2304                               uint32_t nargs, uint64_t* ret_out) {
   2305   uint64_t ret = 0;
   2306   KitStopInfo stop;
   2307 
   2308   if (driver_install_sigint(dbg_on_sigint, s) != 0) {
   2309     dbg_errf(s, "failed to install SIGINT handler");
   2310     return 1;
   2311   }
   2312   if (kit_jit_session_call_u64(s->session, entry, args, nargs, &ret, &stop) !=
   2313       KIT_OK) {
   2314     driver_restore_sigint();
   2315     dbg_errf(s, "call failed (debuggee must be idle or exited)");
   2316     return 1;
   2317   }
   2318   driver_restore_sigint();
   2319   s->last_stop = stop;
   2320   s->has_stop = (stop.kind != KIT_STOP_EXIT);
   2321   if (ret_out) *ret_out = ret;
   2322   if (stop.kind != KIT_STOP_EXIT) {
   2323     s->has_stop = 1;
   2324     dbg_render_stop(s, &stop);
   2325     return 2;
   2326   }
   2327   return 0;
   2328 }
   2329 
   2330 static void dbg_cmd_expr(DbgState* s, const char* expr) {
   2331   char* body = NULL;
   2332   size_t body_len = 0, body_cap = 0;
   2333   char num[32];
   2334   char name[64];
   2335   size_t num_len;
   2336   size_t prefix_len;
   2337   void* entry;
   2338   uint64_t ret = 0;
   2339   uint64_t attempt;
   2340   uint64_t id;
   2341   int is_block = 0;
   2342 
   2343   while (*expr && dbg_isspace((unsigned char)*expr)) ++expr;
   2344   if (!*expr) {
   2345     dbg_errf(s, "usage: expr EXPR | expr { STATEMENTS }");
   2346     return;
   2347   }
   2348 
   2349   if (*expr == '{') {
   2350     const char* p = expr + 1;
   2351     int depth = 1 + dbg_brace_delta(p);
   2352     const char* end = p + driver_strlen(p);
   2353     is_block = 1;
   2354     if (depth <= 0) {
   2355       while (end > p && end[-1] != '}') --end;
   2356       if (end > p) --end;
   2357     }
   2358     if (dbg_buf_append(s, &body, &body_len, &body_cap, p, (size_t)(end - p)) !=
   2359         0)
   2360       goto oom;
   2361     if (dbg_buf_append(s, &body, &body_len, &body_cap, "\n", 1) != 0) goto oom;
   2362     while (depth > 0) {
   2363       char line[LINE_CAP];
   2364       int n;
   2365       driver_printf("expr > ");
   2366       driver_flush_stdout();
   2367       n = driver_read_line(line, sizeof(line));
   2368       if (n <= 0) {
   2369         dbg_errf(s, "unterminated expr block");
   2370         goto out;
   2371       }
   2372       depth += dbg_brace_delta(line);
   2373       if (depth <= 0) {
   2374         char* close = line;
   2375         while (*close && *close != '}') ++close;
   2376         if (dbg_buf_append(s, &body, &body_len, &body_cap, line,
   2377                            (size_t)(close - line)) != 0)
   2378           goto oom;
   2379         if (dbg_buf_append(s, &body, &body_len, &body_cap, "\n", 1) != 0)
   2380           goto oom;
   2381         break;
   2382       }
   2383       if (dbg_buf_append(s, &body, &body_len, &body_cap, line,
   2384                          driver_strlen(line)) != 0)
   2385         goto oom;
   2386       if (dbg_buf_append(s, &body, &body_len, &body_cap, "\n", 1) != 0)
   2387         goto oom;
   2388     }
   2389   }
   2390 
   2391   /* The thunk symbol name uses a monotonic per-attempt counter so each
   2392    * compiled thunk gets a unique linkage name even across failed attempts.
   2393    * The user-visible result number ($N) is a separate counter advanced only
   2394    * after a full success (compile + publish + call), so a failed snippet does
   2395    * not consume a result number. */
   2396   attempt = ++s->expr_attempt;
   2397   num_len = dbg_u64_dec(num, sizeof(num), attempt);
   2398   if (!num_len) {
   2399     dbg_errf(s, "expression counter overflow");
   2400     goto out;
   2401   }
   2402   prefix_len = driver_strlen("__kit_dbg_expr_");
   2403   if (prefix_len + num_len + 1u > sizeof(name)) {
   2404     dbg_errf(s, "expression symbol too long");
   2405     goto out;
   2406   }
   2407   driver_memcpy(name, "__kit_dbg_expr_", prefix_len);
   2408   driver_memcpy(name + prefix_len, num, num_len + 1u);
   2409 
   2410   {
   2411     const char* src = is_block ? body : expr;
   2412     size_t len = is_block ? body_len : driver_strlen(expr);
   2413     KitFrontendInputKind kind =
   2414         is_block ? KIT_FRONTEND_INPUT_REPL_BLOCK : KIT_FRONTEND_INPUT_REPL_EXPR;
   2415     /* s->default_jit_name mirrors dbg_jit_default_name(s->default_jit_lang),
   2416      * kept in sync wherever default_jit_lang is set. */
   2417     if (dbg_jit_compile_append_ex(s, s->default_jit_lang, s->default_jit_name,
   2418                                   src, len, kind, name) != 0) {
   2419       goto out;
   2420     }
   2421   }
   2422 
   2423   entry = kit_jit_lookup(s->jit, kit_slice_cstr(name));
   2424   if (!entry) {
   2425     dbg_errf(s, "expression thunk not found: %.*s",
   2426                 KIT_SLICE_ARG(kit_slice_cstr(name)));
   2427     goto out;
   2428   }
   2429   if (dbg_call_u64_entry(s, entry, NULL, 0, &ret) == 0) {
   2430     id = ++s->expr_counter;
   2431     driver_printf("$%llu = %llu (0x%llx)\n", (unsigned long long)id,
   2432                   (unsigned long long)ret, (unsigned long long)ret);
   2433   }
   2434   goto out;
   2435 
   2436 oom:
   2437   dbg_errf(s, "out of memory");
   2438 out:
   2439   if (body) driver_free(s->env, body, body_cap);
   2440 }
   2441 
   2442 /* ============================================================
   2443  * `x ADDR [count]`
   2444  * ============================================================
   2445  * Examine memory: reads `count` bytes (default 16) at `addr` from the
   2446  * worker's address space and prints them as 16-byte rows. */
   2447 
   2448 static void dbg_cmd_examine(DbgState* s, uint64_t addr, size_t count) {
   2449   uint8_t buf[256];
   2450   size_t remaining = count;
   2451 
   2452   if (!s->has_stop) {
   2453     dbg_errf(s, "no program is stopped");
   2454     return;
   2455   }
   2456 
   2457   while (remaining) {
   2458     size_t chunk = remaining > sizeof(buf) ? sizeof(buf) : remaining;
   2459     size_t i;
   2460     if (kit_jit_session_read_mem(s->session, addr, buf, chunk) != KIT_OK) {
   2461       dbg_errf(s, "read failed at 0x%llx", (unsigned long long)addr);
   2462       return;
   2463     }
   2464     for (i = 0; i < chunk; i += 16) {
   2465       size_t j;
   2466       size_t row = chunk - i < 16 ? chunk - i : 16;
   2467       driver_printf("0x%llx:", (unsigned long long)(addr + i));
   2468       for (j = 0; j < row; ++j) driver_printf(" %02x", buf[i + j]);
   2469       driver_printf("\n");
   2470     }
   2471     addr += chunk;
   2472     remaining -= chunk;
   2473   }
   2474 }
   2475 
   2476 /* ============================================================
   2477  * `disasm [ADDR] [count]`, `x/i [ADDR] [count]`
   2478  * ============================================================
   2479  * Decode a small instruction window from the stopped program. Without an
   2480  * address, starts at the stopped PC; count is in instructions. */
   2481 
   2482 static void dbg_cmd_disasm(DbgState* s, uint64_t addr, size_t count) {
   2483   uint8_t buf[512];
   2484   KitDisasmContext dctx;
   2485   KitDisasmIter* it = NULL;
   2486   size_t byte_count;
   2487   size_t shown = 0;
   2488 
   2489   if (!s->has_stop) {
   2490     dbg_errf(s, "no program is stopped");
   2491     return;
   2492   }
   2493   if (count == 0) count = 8;
   2494   if (count > 32) {
   2495     dbg_errf(s, "disasm count too large");
   2496     return;
   2497   }
   2498   byte_count = count * 16u;
   2499   if (byte_count > sizeof(buf)) byte_count = sizeof(buf);
   2500   if (kit_jit_session_read_mem(s->session, addr, buf, byte_count) != KIT_OK) {
   2501     dbg_errf(s, "read failed at 0x%llx", (unsigned long long)addr);
   2502     return;
   2503   }
   2504 
   2505   memset(&dctx, 0, sizeof(dctx));
   2506   dctx.target = kit_compiler_target(s->compiler);
   2507   dctx.context = s->ctx;
   2508   if (kit_disasm_iter_new(&dctx, buf, byte_count, addr, s->view, &it) !=
   2509       KIT_OK) {
   2510     dbg_errf(s, "disassembler unavailable");
   2511     return;
   2512   }
   2513   while (shown < count) {
   2514     KitInsn insn;
   2515     KitIterResult r = kit_disasm_iter_next(it, &insn);
   2516     if (r == KIT_ITER_END) break;
   2517     if (r != KIT_ITER_ITEM) {
   2518       dbg_errf(s, "disassembly failed");
   2519       break;
   2520     }
   2521     driver_printf("0x%llx: %-8.*s", (unsigned long long)insn.vaddr,
   2522                   KIT_SLICE_ARG(insn.mnemonic));
   2523     if (insn.operands.len) driver_printf(" %.*s", KIT_SLICE_ARG(insn.operands));
   2524     if (insn.annotation.len)
   2525       driver_printf(" %.*s", KIT_SLICE_ARG(insn.annotation));
   2526     driver_printf("\n");
   2527     shown++;
   2528   }
   2529   kit_disasm_iter_free(it);
   2530 }
   2531 
   2532 /* ============================================================
   2533  * Source listing
   2534  * ============================================================
   2535  * Print a context window of source lines centered on `file:line`.
   2536  *
   2537  * Reads REPL snippets from the debugger's in-memory source cache and normal
   2538  * files from disk via env.file_io.  When neither is available (e.g. the DWARF
   2539  * came from a `.o` / `.a` whose source isn't available here), command-mode
   2540  * reports the DWARF line number alone and source stops silently keep the
   2541  * already-rendered location line. */
   2542 
   2543 static void dbg_print_source_bytes(const uint8_t* data, size_t size,
   2544                                    uint32_t line, int report_errors) {
   2545   static const uint8_t empty[1] = {0};
   2546   uint32_t target = line;
   2547   uint32_t lo = target > DBG_LIST_CTX ? target - DBG_LIST_CTX : 1;
   2548   uint32_t hi = target + DBG_LIST_CTX;
   2549   uint32_t cur = 1;
   2550   const uint8_t* p;
   2551   const uint8_t* end;
   2552   const uint8_t* line_start;
   2553 
   2554   if (!data && size != 0) return;
   2555   if (!data) data = empty;
   2556   p = data;
   2557   end = data + size;
   2558   line_start = p;
   2559 
   2560   while (p <= end) {
   2561     int eol = (p == end) || (*p == '\n');
   2562     if (eol) {
   2563       if (p == end && p == line_start && end > data && end[-1] == '\n') break;
   2564       if (cur >= lo && cur <= hi) {
   2565         size_t len = (size_t)(p - line_start);
   2566         driver_printf(
   2567             "%6u%.*s %.*s\n", cur,
   2568             KIT_SLICE_ARG(kit_slice_cstr(cur == target ? " >" : "  ")),
   2569             (int)len, (const char*)line_start);
   2570       }
   2571       ++cur;
   2572       if (p == end) break;
   2573       line_start = p + 1;
   2574     }
   2575     ++p;
   2576   }
   2577   if (report_errors && cur <= target)
   2578     driver_errf(DBG_TOOL, "file has only %u lines; %u requested", cur - 1u,
   2579                 target);
   2580 }
   2581 
   2582 static void dbg_print_source_listing(DbgState* s, KitSlice file, uint32_t line,
   2583                                      uint64_t pc, int report_errors) {
   2584   char path[1024];
   2585   KitFileData fd;
   2586   const KitFileIO* io;
   2587   DbgSource* cached;
   2588 
   2589   if (!file.s || file.len == 0) {
   2590     if (report_errors) {
   2591       dbg_errf(s, "bad file in '%.*s'", KIT_SLICE_ARG(file));
   2592     }
   2593     return;
   2594   }
   2595 
   2596   cached = dbg_source_find(s, file);
   2597   if (cached && cached->data) {
   2598     dbg_print_source_bytes(cached->data, cached->len, line, report_errors);
   2599     return;
   2600   }
   2601 
   2602   if (file.len >= sizeof(path)) {
   2603     if (report_errors) {
   2604       dbg_errf(s, "bad file in '%.*s'", KIT_SLICE_ARG(file));
   2605     }
   2606     return;
   2607   }
   2608   driver_memcpy(path, file.s, file.len);
   2609   path[file.len] = '\0';
   2610 
   2611   /* Try to read the file via file_io.  On miss, fall back to the
   2612    * DWARF-only summary line per doc/DBG.md §10. */
   2613   io = s->env && s->env->file_io.read_all ? &s->env->file_io : NULL;
   2614   if (!io || io->read_all(io->user, path, &fd) != KIT_OK) {
   2615     if (report_errors) {
   2616       driver_printf("%.*s:%u  [source not available; pc=0x%llx]\n",
   2617                     KIT_SLICE_ARG(kit_slice_cstr(path)), line,
   2618                     (unsigned long long)pc);
   2619     }
   2620     return;
   2621   }
   2622 
   2623   dbg_print_source_bytes(fd.data, fd.size, line, report_errors);
   2624 
   2625   if (io->release) io->release(io->user, &fd);
   2626 }
   2627 
   2628 static void dbg_cmd_list(DbgState* s, const char* spec) {
   2629   const char* colon;
   2630   size_t flen;
   2631   char path[1024];
   2632   uint64_t line_u;
   2633   size_t used;
   2634   uint64_t pc;
   2635   KitSlice file;
   2636 
   2637   if (!s->dwarf) {
   2638     dbg_errf(s, "no DWARF: cannot resolve %.*s",
   2639                 KIT_SLICE_ARG(kit_slice_cstr(spec)));
   2640     return;
   2641   }
   2642 
   2643   colon = driver_strchr(spec, ':');
   2644   if (!colon || !dbg_isdigit((unsigned char)colon[1])) {
   2645     dbg_errf(s, "usage: list file:line");
   2646     return;
   2647   }
   2648   flen = (size_t)(colon - spec);
   2649   if (flen == 0 || flen >= sizeof(path)) {
   2650     dbg_errf(s, "bad file in '%.*s'",
   2651                 KIT_SLICE_ARG(kit_slice_cstr(spec)));
   2652     return;
   2653   }
   2654   driver_memcpy(path, spec, flen);
   2655   path[flen] = '\0';
   2656   used = dbg_parse_uint(colon + 1, &line_u);
   2657   if (!used || colon[1 + used] != '\0') {
   2658     dbg_errf(s, "usage: list file:line");
   2659     return;
   2660   }
   2661 
   2662   /* Validate via DWARF first. */
   2663   {
   2664     KitStatus st = kit_dwarf_line_to_addr(s->dwarf, kit_slice_cstr(path),
   2665                                           (uint32_t)line_u, &pc);
   2666     if (st == KIT_NOT_FOUND) {
   2667       dbg_errf(s, "no line %u in %.*s", (uint32_t)line_u,
   2668                   KIT_SLICE_ARG(kit_slice_cstr(path)));
   2669       return;
   2670     }
   2671     if (st == KIT_AMBIGUOUS) {
   2672       dbg_errf(s,
   2673                   "ambiguous: %.*s:%u matches multiple files; "
   2674                   "use a longer path suffix",
   2675                   KIT_SLICE_ARG(kit_slice_cstr(path)), (uint32_t)line_u);
   2676       return;
   2677     }
   2678     if (st != KIT_OK) {
   2679       dbg_errf(s, "no line entry for %.*s",
   2680                   KIT_SLICE_ARG(kit_slice_cstr(spec)));
   2681       return;
   2682     }
   2683   }
   2684 
   2685   file = kit_slice_cstr(path);
   2686   dbg_print_source_listing(s, file, (uint32_t)line_u, pc, 1);
   2687 }
   2688 
   2689 /* ============================================================
   2690  * `info b`, `b LOC`, `d N`, `enable N` / `disable N`, `ignore N COUNT`
   2691  * ============================================================ */
   2692 
   2693 /* Arm a Bp through the appropriate session entry. The plain breakpoint_set
   2694  * is used when no skip/cap is in effect; otherwise the spec form. The
   2695  * driver does not yet wire conditions, but the spec callback slot is left
   2696  * NULL so this remains forward-compatible. */
   2697 static int dbg_bp_arm(DbgState* s, Bp* b) {
   2698   KitStatus st;
   2699   if (b->skip_count == 0 && b->max_hits == 0) {
   2700     st = kit_jit_session_breakpoint_set(s->session, b->addr, &b->session_id);
   2701   } else {
   2702     KitBreakpointSpec spec;
   2703     KitBreakpointSpec z = {0};
   2704     spec = z;
   2705     spec.addr = b->addr;
   2706     spec.skip_count = b->skip_count;
   2707     spec.max_hits = b->max_hits;
   2708     st = kit_jit_session_breakpoint_set_spec(s->session, &spec, &b->session_id);
   2709   }
   2710   return st == KIT_OK ? 0 : 1;
   2711 }
   2712 
   2713 static void dbg_cmd_break(DbgState* s, const char* spec) {
   2714   BpKind kind;
   2715   uint64_t addr;
   2716   Bp* b;
   2717 
   2718   if (!*spec) {
   2719     dbg_errf(s, "usage: b <0xADDR | sym[+off] | file.c:line>");
   2720     return;
   2721   }
   2722   if (dbg_resolve_loc(s, spec, &kind, &addr) != 0) return;
   2723 
   2724   b = dbg_bp_grow(s);
   2725   if (!b) return;
   2726 
   2727   {
   2728     Bp z = {0};
   2729     *b = z;
   2730   }
   2731   b->id = ++s->next_bp_id;
   2732   b->enabled = 1;
   2733   b->kind = kind;
   2734   b->addr = addr;
   2735   b->spec = dbg_dup(s->env, spec, driver_strlen(spec), &b->spec_size);
   2736   if (!b->spec) {
   2737     dbg_errf(s, "out of memory");
   2738     return;
   2739   }
   2740 
   2741   if (dbg_bp_arm(s, b) != 0) {
   2742     dbg_errf(s, "failed to arm breakpoint at 0x%llx",
   2743                 (unsigned long long)addr);
   2744     b->session_id = 0;
   2745     b->enabled = 0;
   2746   }
   2747 
   2748   s->nbps++;
   2749   driver_printf(
   2750       "Breakpoint %d at 0x%llx (%.*s)%.*s\n", b->id, (unsigned long long)addr,
   2751       KIT_SLICE_ARG(kit_slice_cstr(spec)),
   2752       KIT_SLICE_ARG(kit_slice_cstr(b->session_id ? "" : " [disarmed]")));
   2753 }
   2754 
   2755 static void dbg_cmd_info_b(DbgState* s) {
   2756   uint32_t i;
   2757   if (s->nbps == 0) {
   2758     driver_printf("No breakpoints.\n");
   2759     return;
   2760   }
   2761   driver_printf("Num  Enb  Address              Skip   Max    Spec\n");
   2762   for (i = 0; i < s->nbps; ++i) {
   2763     Bp* b = &s->bps[i];
   2764     driver_printf(
   2765         "%-4d %-4s 0x%-18llx %-6llu %-6llu %.*s%.*s\n", b->id,
   2766         b->enabled ? "y" : "n", (unsigned long long)b->addr,
   2767         (unsigned long long)b->skip_count, (unsigned long long)b->max_hits,
   2768         KIT_SLICE_ARG(kit_slice_cstr(b->spec)),
   2769         KIT_SLICE_ARG(kit_slice_cstr(b->session_id ? "" : " [disarmed]")));
   2770   }
   2771 }
   2772 
   2773 static void dbg_cmd_ignore(DbgState* s, int id, uint64_t count) {
   2774   Bp* b = dbg_bp_find(s, id);
   2775   if (!b) {
   2776     dbg_errf(s, "no breakpoint %d", id);
   2777     return;
   2778   }
   2779   if (b->session_id) {
   2780     kit_jit_session_breakpoint_clear(s->session, b->session_id);
   2781     b->session_id = 0;
   2782   }
   2783   b->skip_count = count;
   2784   if (b->enabled) {
   2785     if (dbg_bp_arm(s, b) != 0) {
   2786       dbg_errf(s, "failed to re-arm breakpoint %d", id);
   2787       return;
   2788     }
   2789   }
   2790   driver_printf("Breakpoint %d will skip the next %llu hits\n", id,
   2791                 (unsigned long long)count);
   2792 }
   2793 
   2794 static void dbg_cmd_delete(DbgState* s, int id) {
   2795   if (dbg_bp_remove(s, id) != 0) {
   2796     dbg_errf(s, "no breakpoint %d", id);
   2797   }
   2798 }
   2799 
   2800 static void dbg_cmd_set_enabled(DbgState* s, int id, int enable) {
   2801   Bp* b = dbg_bp_find(s, id);
   2802   if (!b) {
   2803     dbg_errf(s, "no breakpoint %d", id);
   2804     return;
   2805   }
   2806   if (enable && !b->enabled) {
   2807     if (dbg_bp_arm(s, b) != 0) {
   2808       dbg_errf(s, "failed to re-arm breakpoint %d", id);
   2809       return;
   2810     }
   2811     b->enabled = 1;
   2812   } else if (!enable && b->enabled) {
   2813     if (b->session_id) {
   2814       kit_jit_session_breakpoint_clear(s->session, b->session_id);
   2815       b->session_id = 0;
   2816     }
   2817     b->enabled = 0;
   2818   }
   2819 }
   2820 
   2821 /* ============================================================
   2822  * Help
   2823  * ============================================================ */
   2824 
   2825 static void dbg_cmd_help(void) {
   2826   driver_printf(
   2827       "%.*s",
   2828       KIT_SLICE_ARG(KIT_SLICE_LIT(
   2829           "Commands (abbrev. shown):\n"
   2830           "  h, help                     show this help\n"
   2831           "  q, quit                     exit (Ctrl-D also works)\n"
   2832           "  :language c|toy|asm|wasm/wat\n"
   2833           "                              select language for jit/expr input\n"
   2834           "  r, run                      start fresh execution at entry\n"
   2835           "  c, cont                     continue after a stop\n"
   2836           "  s, step                     step to next source line (into "
   2837           "calls)\n"
   2838           "  si, stepi                   single-step one instruction\n"
   2839           "  n, next                     step to next source line (over "
   2840           "calls)\n"
   2841           "  finish                      run until current frame returns\n"
   2842           "  jit [LANG|NAME] { ... }     compile and append a language "
   2843           "snippet\n"
   2844           "  { ... }                     same as jit { ... }\n"
   2845           "  edit [LANG|NAME], e [...]   edit and append a language snippet\n"
   2846           "  expr EXPR | expr { ... }    compile and call an expression thunk\n"
   2847           "  EXPR                        same as expr EXPR\n"
   2848           "                              LANG defaults to the selected "
   2849           "language\n"
   2850           "  jump ADDR                   set PC to ADDR (no resume)\n"
   2851           "  bt, backtrace               print stack trace with arguments\n"
   2852           "  b LOC                       set breakpoint at LOC:\n"
   2853           "                                 0xADDR | sym[+off] | file.c:line\n"
   2854           "  ignore N COUNT              skip the next COUNT hits of bp N\n"
   2855           "  d N, delete N               delete breakpoint N\n"
   2856           "  enable N | disable N        toggle breakpoint N\n"
   2857           "  p NAME                      print variable / global\n"
   2858           "  set NAME VALUE              write VALUE into NAME\n"
   2859           "  x ADDR [count]              examine memory (count bytes, default "
   2860           "16)\n"
   2861           "  disasm [ADDR] [count], x/i  disassemble at PC or ADDR\n"
   2862           "  list FILE:LINE, l FILE:LINE source listing around FILE:LINE\n"
   2863           "  info b                      list breakpoints\n"
   2864           "  info reg, info registers    dump registers\n"
   2865           "  info locals                 list locals at current PC\n"
   2866           "  info args                   list args at current PC\n"
   2867           "  info functions [PATTERN]    list JIT functions matching PATTERN\n"
   2868           "  info variables [PATTERN]    list JIT globals  matching "
   2869           "PATTERN\n")));
   2870 }
   2871 
   2872 static KitLanguage dbg_default_language_from_inputs(KitCompiler* c,
   2873                                                     const DbgOpts* o) {
   2874   if (o->has_default_lang) return o->default_lang;
   2875   if (o->inputs.nsources) return kit_language_for_path(c, o->inputs.sources[0]);
   2876   if (o->inputs.nsource_memory) return o->inputs.source_memory[0].lang;
   2877   return KIT_LANG_C;
   2878 }
   2879 
   2880 /* ============================================================
   2881  * Tokenizer / dispatch
   2882  * ============================================================ */
   2883 
   2884 /* Trim leading whitespace and split off one whitespace-delimited word.
   2885  * Returns the start of the next region (i.e. the rest of the line, with
   2886  * its own leading whitespace skipped). The input buffer is mutated:
   2887  * the byte after the first word is replaced with a NUL. */
   2888 static char* dbg_take_word(char* line, char** word_out) {
   2889   char* p = line;
   2890   char* w;
   2891   while (*p && dbg_isspace((unsigned char)*p)) ++p;
   2892   if (!*p) {
   2893     *word_out = p;
   2894     return p;
   2895   }
   2896   w = p;
   2897   while (*p && !dbg_isspace((unsigned char)*p)) ++p;
   2898   if (*p) {
   2899     *p = '\0';
   2900     ++p;
   2901   }
   2902   while (*p && dbg_isspace((unsigned char)*p)) ++p;
   2903   *word_out = w;
   2904   return p;
   2905 }
   2906 
   2907 static int dbg_dispatch(DbgState* s, char* line) {
   2908   char raw[LINE_CAP];
   2909   size_t raw_len = driver_strlen(line);
   2910   char* cmd;
   2911   char* rest;
   2912 
   2913   if (raw_len >= sizeof(raw)) raw_len = sizeof(raw) - 1u;
   2914   driver_memcpy(raw, line, raw_len);
   2915   raw[raw_len] = '\0';
   2916 
   2917   rest = dbg_take_word(line, &cmd);
   2918 
   2919   if (!*cmd) return 0; /* blank line */
   2920 
   2921   if (driver_streq(cmd, "h") || driver_streq(cmd, "help")) {
   2922     dbg_cmd_help();
   2923     return 0;
   2924   }
   2925   if (driver_streq(cmd, ":language") || driver_streq(cmd, ":lang") ||
   2926       driver_streq(cmd, "language")) {
   2927     dbg_cmd_language(s, rest);
   2928     return 0;
   2929   }
   2930   if (driver_streq(cmd, "q") || driver_streq(cmd, "quit") ||
   2931       driver_streq(cmd, "exit")) {
   2932     return 1; /* signal "exit REPL" */
   2933   }
   2934   if (driver_streq(cmd, "r") || driver_streq(cmd, "run")) {
   2935     dbg_drive(s, RUN_FRESH);
   2936     return 0;
   2937   }
   2938   if (driver_streq(cmd, "abort")) {
   2939     if (s->has_stop && s->session) {
   2940       kit_jit_session_resume(s->session, KIT_RESUME_ABORT, NULL);
   2941       s->has_stop = 0;
   2942       dbg_errf(s, "execution aborted");
   2943     } else {
   2944       dbg_errf(s, "nothing to abort");
   2945     }
   2946     return 0;
   2947   }
   2948   if (driver_streq(cmd, "c") || driver_streq(cmd, "cont") ||
   2949       driver_streq(cmd, "continue")) {
   2950     dbg_drive(s, RUN_CONTINUE);
   2951     return 0;
   2952   }
   2953   if (driver_streq(cmd, "s") || driver_streq(cmd, "step")) {
   2954     dbg_drive(s, RUN_STEP_LINE);
   2955     return 0;
   2956   }
   2957   if (driver_streq(cmd, "si") || driver_streq(cmd, "stepi")) {
   2958     dbg_drive(s, RUN_STEP_INSN);
   2959     return 0;
   2960   }
   2961   if (driver_streq(cmd, "n") || driver_streq(cmd, "next")) {
   2962     dbg_drive(s, RUN_NEXT_LINE);
   2963     return 0;
   2964   }
   2965   if (driver_streq(cmd, "finish")) {
   2966     dbg_drive(s, RUN_STEP_OUT);
   2967     return 0;
   2968   }
   2969   if (driver_streq(cmd, "jit")) {
   2970     dbg_cmd_jit(s, rest);
   2971     return 0;
   2972   }
   2973   if (driver_streq(cmd, "edit") || driver_streq(cmd, "e")) {
   2974     dbg_cmd_edit(s, rest);
   2975     return 0;
   2976   }
   2977   if (driver_streq(cmd, "expr")) {
   2978     if (!s->session) {
   2979       dbg_errf(s, "no JIT session");
   2980       return 0;
   2981     }
   2982     dbg_cmd_expr(s, rest);
   2983     return 0;
   2984   }
   2985   if (driver_streq(cmd, "bt") || driver_streq(cmd, "backtrace") ||
   2986       driver_streq(cmd, "where")) {
   2987     dbg_cmd_bt(s);
   2988     return 0;
   2989   }
   2990   if (driver_streq(cmd, "b") || driver_streq(cmd, "break")) {
   2991     char* loc;
   2992     dbg_take_word(rest, &loc);
   2993     dbg_cmd_break(s, loc);
   2994     return 0;
   2995   }
   2996   if (driver_streq(cmd, "info")) {
   2997     char* what;
   2998     rest = dbg_take_word(rest, &what);
   2999     if (driver_streq(what, "b") || driver_streq(what, "break") ||
   3000         driver_streq(what, "breakpoints")) {
   3001       dbg_cmd_info_b(s);
   3002     } else if (driver_streq(what, "reg") || driver_streq(what, "registers")) {
   3003       dbg_cmd_info_reg(s);
   3004     } else if (driver_streq(what, "locals")) {
   3005       dbg_cmd_info_vars(s, 1u << KIT_DVR_LOCAL, "locals");
   3006     } else if (driver_streq(what, "args")) {
   3007       dbg_cmd_info_vars(s, 1u << KIT_DVR_ARG, "args");
   3008     } else if (driver_streq(what, "functions") || driver_streq(what, "func")) {
   3009       char* pat;
   3010       dbg_take_word(rest, &pat);
   3011       dbg_cmd_info_syms(s, KIT_SK_FUNC, *pat ? pat : NULL);
   3012     } else if (driver_streq(what, "variables") || driver_streq(what, "var")) {
   3013       char* pat;
   3014       dbg_take_word(rest, &pat);
   3015       dbg_cmd_info_syms(s, KIT_SK_OBJ, *pat ? pat : NULL);
   3016     } else {
   3017       dbg_errf(s, "unknown 'info' subcommand: %.*s",
   3018                   KIT_SLICE_ARG(kit_slice_cstr(what)));
   3019     }
   3020     return 0;
   3021   }
   3022   if (driver_streq(cmd, "set")) {
   3023     char* name;
   3024     char* val_s;
   3025     uint64_t v;
   3026     size_t used;
   3027     rest = dbg_take_word(rest, &name);
   3028     if (!*name) {
   3029       dbg_errf(s, "usage: set NAME VALUE");
   3030       return 0;
   3031     }
   3032     /* Accept an optional `=` between name and value: `set x = 5`. */
   3033     if (driver_streq(name, "=")) {
   3034       dbg_errf(s, "usage: set NAME VALUE");
   3035       return 0;
   3036     }
   3037     rest = dbg_take_word(rest, &val_s);
   3038     if (driver_streq(val_s, "=")) {
   3039       rest = dbg_take_word(rest, &val_s);
   3040     }
   3041     if (!*val_s) {
   3042       dbg_errf(s, "usage: set NAME VALUE");
   3043       return 0;
   3044     }
   3045     used = dbg_parse_uint(val_s, &v);
   3046     if (!used || val_s[used] != '\0') {
   3047       dbg_errf(s, "expected integer value, got '%.*s'",
   3048                   KIT_SLICE_ARG(kit_slice_cstr(val_s)));
   3049       return 0;
   3050     }
   3051     dbg_cmd_set(s, name, v);
   3052     return 0;
   3053   }
   3054   if (driver_streq(cmd, "jump") || driver_streq(cmd, "j")) {
   3055     char* addr_s;
   3056     uint64_t addr;
   3057     size_t used;
   3058     dbg_take_word(rest, &addr_s);
   3059     if (!*addr_s) {
   3060       dbg_errf(s, "usage: jump ADDR");
   3061       return 0;
   3062     }
   3063     used = dbg_parse_uint(addr_s, &addr);
   3064     if (!used || addr_s[used] != '\0') {
   3065       dbg_errf(s, "bad address '%.*s'",
   3066                   KIT_SLICE_ARG(kit_slice_cstr(addr_s)));
   3067       return 0;
   3068     }
   3069     dbg_cmd_jump(s, addr);
   3070     return 0;
   3071   }
   3072   if (driver_streq(cmd, "ignore")) {
   3073     char* id_s;
   3074     char* cnt_s;
   3075     uint64_t id;
   3076     uint64_t cnt;
   3077     size_t used;
   3078     rest = dbg_take_word(rest, &id_s);
   3079     dbg_take_word(rest, &cnt_s);
   3080     if (!*id_s || !*cnt_s) {
   3081       dbg_errf(s, "usage: ignore N COUNT");
   3082       return 0;
   3083     }
   3084     used = dbg_parse_uint(id_s, &id);
   3085     if (!used || id_s[used] != '\0') {
   3086       dbg_errf(s, "expected breakpoint id");
   3087       return 0;
   3088     }
   3089     used = dbg_parse_uint(cnt_s, &cnt);
   3090     if (!used || cnt_s[used] != '\0') {
   3091       dbg_errf(s, "expected hit count");
   3092       return 0;
   3093     }
   3094     dbg_cmd_ignore(s, (int)id, cnt);
   3095     return 0;
   3096   }
   3097   if (driver_streq(cmd, "d") || driver_streq(cmd, "delete")) {
   3098     char* arg;
   3099     uint64_t id;
   3100     size_t used;
   3101     dbg_take_word(rest, &arg);
   3102     if (!*arg) {
   3103       dbg_errf(s, "usage: d <id>");
   3104       return 0;
   3105     }
   3106     used = dbg_parse_uint(arg, &id);
   3107     if (!used || arg[used] != '\0') {
   3108       dbg_errf(s, "expected breakpoint id");
   3109       return 0;
   3110     }
   3111     dbg_cmd_delete(s, (int)id);
   3112     return 0;
   3113   }
   3114   if (driver_streq(cmd, "enable") || driver_streq(cmd, "disable")) {
   3115     char* arg;
   3116     uint64_t id;
   3117     size_t used;
   3118     dbg_take_word(rest, &arg);
   3119     used = dbg_parse_uint(arg, &id);
   3120     if (!used || arg[used] != '\0') {
   3121       dbg_errf(s, "expected breakpoint id");
   3122       return 0;
   3123     }
   3124     dbg_cmd_set_enabled(s, (int)id, driver_streq(cmd, "enable"));
   3125     return 0;
   3126   }
   3127   if (driver_streq(cmd, "p") || driver_streq(cmd, "print")) {
   3128     char* name;
   3129     dbg_take_word(rest, &name);
   3130     if (!*name) {
   3131       dbg_errf(s, "usage: p <name>");
   3132       return 0;
   3133     }
   3134     dbg_cmd_print(s, name);
   3135     return 0;
   3136   }
   3137   if (driver_streq(cmd, "list") || driver_streq(cmd, "l")) {
   3138     char* loc;
   3139     dbg_take_word(rest, &loc);
   3140     if (!*loc) {
   3141       dbg_errf(s, "usage: list file:line");
   3142       return 0;
   3143     }
   3144     dbg_cmd_list(s, loc);
   3145     return 0;
   3146   }
   3147   if (driver_streq(cmd, "disasm") || driver_streq(cmd, "x/i")) {
   3148     char* addr_s;
   3149     char* count_s;
   3150     uint64_t addr = s->has_stop ? s->last_stop.regs.pc : 0;
   3151     uint64_t count = 8;
   3152     size_t used;
   3153     rest = dbg_take_word(rest, &addr_s);
   3154     if (*addr_s) {
   3155       used = dbg_parse_uint(addr_s, &addr);
   3156       if (!used || addr_s[used] != '\0') {
   3157         dbg_errf(s, "bad address '%.*s'",
   3158                     KIT_SLICE_ARG(kit_slice_cstr(addr_s)));
   3159         return 0;
   3160       }
   3161       dbg_take_word(rest, &count_s);
   3162       if (*count_s) {
   3163         used = dbg_parse_uint(count_s, &count);
   3164         if (!used || count_s[used] != '\0') {
   3165           dbg_errf(s, "bad count '%.*s'",
   3166                       KIT_SLICE_ARG(kit_slice_cstr(count_s)));
   3167           return 0;
   3168         }
   3169       }
   3170     }
   3171     dbg_cmd_disasm(s, addr, (size_t)count);
   3172     return 0;
   3173   }
   3174   if (driver_streq(cmd, "x") || driver_streq(cmd, "examine")) {
   3175     char* addr_s;
   3176     char* count_s;
   3177     uint64_t addr;
   3178     uint64_t count = 16;
   3179     size_t used;
   3180     rest = dbg_take_word(rest, &addr_s);
   3181     if (!*addr_s) {
   3182       dbg_errf(s, "usage: x <addr> [count]");
   3183       return 0;
   3184     }
   3185     used = dbg_parse_uint(addr_s, &addr);
   3186     if (!used || addr_s[used] != '\0') {
   3187       dbg_errf(s, "bad address '%.*s'",
   3188                   KIT_SLICE_ARG(kit_slice_cstr(addr_s)));
   3189       return 0;
   3190     }
   3191     dbg_take_word(rest, &count_s);
   3192     if (*count_s) {
   3193       used = dbg_parse_uint(count_s, &count);
   3194       if (!used || count_s[used] != '\0') {
   3195         dbg_errf(s, "bad count '%.*s'",
   3196                     KIT_SLICE_ARG(kit_slice_cstr(count_s)));
   3197         return 0;
   3198       }
   3199     }
   3200     dbg_cmd_examine(s, addr, (size_t)count);
   3201     return 0;
   3202   }
   3203 
   3204   if (cmd[0] == '{') {
   3205     dbg_cmd_jit(s, raw);
   3206     return 0;
   3207   }
   3208 
   3209   if (s->session) {
   3210     dbg_cmd_expr(s, raw);
   3211   } else {
   3212     dbg_errf(s, "unknown command: %.*s (try 'h')",
   3213                 KIT_SLICE_ARG(kit_slice_cstr(cmd)));
   3214   }
   3215   return 0;
   3216 }
   3217 
   3218 /* ============================================================
   3219  * Completion
   3220  * ============================================================ */
   3221 
   3222 static int dbg_slice_has_prefix(KitSlice s, const char* prefix,
   3223                                 size_t prefix_len) {
   3224   return s.len >= prefix_len && memcmp(s.s, prefix, prefix_len) == 0;
   3225 }
   3226 
   3227 static int dbg_cstr_has_prefix(const char* s, const char* prefix,
   3228                                size_t prefix_len) {
   3229   return driver_strlen(s) >= prefix_len && memcmp(s, prefix, prefix_len) == 0;
   3230 }
   3231 
   3232 static int dbg_completion_seen(DriverLineCompletionList* out, const char* text,
   3233                                size_t len) {
   3234   uint32_t i;
   3235   for (i = 0; i < out->count; ++i) {
   3236     if (driver_strlen(out->items[i].text) == len &&
   3237         memcmp(out->items[i].text, text, len) == 0)
   3238       return 1;
   3239   }
   3240   return 0;
   3241 }
   3242 
   3243 static void dbg_completion_add_cstr(DriverLineCompletionList* out,
   3244                                     const char* prefix, size_t prefix_len,
   3245                                     const char* text) {
   3246   size_t len = driver_strlen(text);
   3247   if (!dbg_cstr_has_prefix(text, prefix, prefix_len)) return;
   3248   if (dbg_completion_seen(out, text, len)) return;
   3249   (void)driver_line_completion_add(out, text, len);
   3250 }
   3251 
   3252 static void dbg_completion_add_slice(DriverLineCompletionList* out,
   3253                                      const char* prefix, size_t prefix_len,
   3254                                      KitSlice text) {
   3255   if (!text.s || !dbg_slice_has_prefix(text, prefix, prefix_len)) return;
   3256   if (dbg_completion_seen(out, text.s, text.len)) return;
   3257   (void)driver_line_completion_add(out, text.s, text.len);
   3258 }
   3259 
   3260 static void dbg_completion_add_u64(DriverLineCompletionList* out,
   3261                                    const char* prefix, size_t prefix_len,
   3262                                    uint64_t v) {
   3263   char buf[32];
   3264   size_t n = 0;
   3265   char rev[32];
   3266   size_t r = 0;
   3267   if (v == 0) {
   3268     rev[r++] = '0';
   3269   } else {
   3270     while (v && r < sizeof(rev)) {
   3271       rev[r++] = (char)('0' + (v % 10u));
   3272       v /= 10u;
   3273     }
   3274   }
   3275   while (r > 0 && n + 1u < sizeof(buf)) buf[n++] = rev[--r];
   3276   buf[n] = '\0';
   3277   dbg_completion_add_cstr(out, prefix, prefix_len, buf);
   3278 }
   3279 
   3280 static void dbg_word_bounds(const char* line, size_t cursor, size_t* start,
   3281                             size_t* end) {
   3282   size_t len = driver_strlen(line);
   3283   size_t s = cursor < len ? cursor : len;
   3284   size_t e = s;
   3285   while (s > 0 && !dbg_isspace((unsigned char)line[s - 1])) --s;
   3286   while (e < len && !dbg_isspace((unsigned char)line[e])) ++e;
   3287   *start = s;
   3288   *end = e;
   3289 }
   3290 
   3291 static size_t dbg_read_word_at(const char* line, size_t start, char* out,
   3292                                size_t cap) {
   3293   size_t i = start;
   3294   size_t n = 0;
   3295   while (line[i] && dbg_isspace((unsigned char)line[i])) ++i;
   3296   while (line[i] && !dbg_isspace((unsigned char)line[i])) {
   3297     if (n + 1u < cap) out[n++] = line[i];
   3298     ++i;
   3299   }
   3300   if (cap) out[n] = '\0';
   3301   return n;
   3302 }
   3303 
   3304 static void dbg_complete_commands(DriverLineCompletionList* out,
   3305                                   const char* prefix, size_t prefix_len) {
   3306   static const char* const cmds[] = {
   3307       "help",   "quit",     "exit",      "run",     "cont",  "continue",
   3308       "step",   "stepi",    "next",      "finish",  "jit",   "edit",
   3309       "expr",   "bt",       "backtrace", "where",   "break", "info",
   3310       "ignore", "delete",   "enable",    "disable", "print", "set",
   3311       "jump",   "list",     "disasm",    "x",       "x/i",   ":language",
   3312       ":lang",  "language", "abort",     "h",       "q",     "r",
   3313       "c",      "s",        "si",        "n",       "b",     "d",
   3314       "p",      "l",        "e",
   3315   };
   3316   size_t i;
   3317   for (i = 0; i < sizeof(cmds) / sizeof(cmds[0]); ++i)
   3318     dbg_completion_add_cstr(out, prefix, prefix_len, cmds[i]);
   3319 }
   3320 
   3321 static void dbg_complete_info(DriverLineCompletionList* out, const char* prefix,
   3322                               size_t prefix_len) {
   3323   static const char* const words[] = {
   3324       "b",    "break",     "breakpoints", "reg",       "registers", "locals",
   3325       "args", "functions", "func",        "variables", "var",
   3326   };
   3327   size_t i;
   3328   for (i = 0; i < sizeof(words) / sizeof(words[0]); ++i)
   3329     dbg_completion_add_cstr(out, prefix, prefix_len, words[i]);
   3330 }
   3331 
   3332 static void dbg_complete_languages(DriverLineCompletionList* out,
   3333                                    const char* prefix, size_t prefix_len) {
   3334   static const char* const langs[] = {"c", "toy", "asm", "wasm", "wat"};
   3335   size_t i;
   3336   for (i = 0; i < sizeof(langs) / sizeof(langs[0]); ++i)
   3337     dbg_completion_add_cstr(out, prefix, prefix_len, langs[i]);
   3338 }
   3339 
   3340 static void dbg_complete_symbols(DbgState* s, DriverLineCompletionList* out,
   3341                                  const char* prefix, size_t prefix_len,
   3342                                  int funcs, int objects) {
   3343   KitJitSymIter* it = NULL;
   3344   KitJitSym sym;
   3345   if (!s->jit || kit_jit_sym_iter_new(s->jit, &it) != KIT_OK) return;
   3346   for (;;) {
   3347     KitIterResult r = kit_jit_sym_iter_next(it, &sym);
   3348     if (r != KIT_ITER_ITEM) break;
   3349     if ((sym.kind == KIT_SK_FUNC && funcs) ||
   3350         (sym.kind == KIT_SK_OBJ && objects))
   3351       dbg_completion_add_slice(out, prefix, prefix_len, sym.name);
   3352   }
   3353   kit_jit_sym_iter_free(it);
   3354 }
   3355 
   3356 static void dbg_complete_locals(DbgState* s, DriverLineCompletionList* out,
   3357                                 const char* prefix, size_t prefix_len) {
   3358   KitDwarfVarIter* it = NULL;
   3359   KitDwarfVar v;
   3360   if (!s->has_stop || !s->dwarf) return;
   3361   if (kit_dwarf_vars_at_new(s->dwarf, dbg_pc_rt_to_img(s, s->last_stop.regs.pc),
   3362                             KIT_DVRM_LOCAL | KIT_DVRM_ARG, &it) != KIT_OK)
   3363     return;
   3364   for (;;) {
   3365     KitIterResult r = kit_dwarf_vars_at_next(it, &v);
   3366     if (r != KIT_ITER_ITEM) break;
   3367     dbg_completion_add_slice(out, prefix, prefix_len, v.name);
   3368   }
   3369   kit_dwarf_vars_at_free(it);
   3370 }
   3371 
   3372 static void dbg_complete_files(DbgState* s, DriverLineCompletionList* out,
   3373                                const char* prefix, size_t prefix_len) {
   3374   KitDwarfCuIter* cui = NULL;
   3375   KitDwarfCu cu;
   3376   if (!s->dwarf) return;
   3377   if (kit_dwarf_cu_iter_new(s->dwarf, &cui) != KIT_OK) return;
   3378   for (;;) {
   3379     KitDwarfLineIter* li = NULL;
   3380     KitDwarfLineRow row;
   3381     KitIterResult cr = kit_dwarf_cu_iter_next(cui, &cu);
   3382     if (cr != KIT_ITER_ITEM) break;
   3383     if (kit_dwarf_line_iter_new(s->dwarf, cu.offset, &li) != KIT_OK) continue;
   3384     for (;;) {
   3385       KitSlice file = KIT_SLICE_NULL;
   3386       KitIterResult lr = kit_dwarf_line_iter_next(li, &row);
   3387       if (lr != KIT_ITER_ITEM) break;
   3388       if (kit_dwarf_line_file(s->dwarf, cu.offset, row.file_index, &file) ==
   3389           KIT_OK)
   3390         dbg_completion_add_slice(out, prefix, prefix_len, file);
   3391     }
   3392     kit_dwarf_line_iter_free(li);
   3393   }
   3394   kit_dwarf_cu_iter_free(cui);
   3395 }
   3396 
   3397 static void dbg_complete_breakpoint_ids(DbgState* s,
   3398                                         DriverLineCompletionList* out,
   3399                                         const char* prefix, size_t prefix_len) {
   3400   uint32_t i;
   3401   for (i = 0; i < s->nbps; ++i)
   3402     dbg_completion_add_u64(out, prefix, prefix_len, (uint64_t)s->bps[i].id);
   3403 }
   3404 
   3405 static void dbg_complete(void* user, const char* line, size_t cursor,
   3406                          DriverLineCompletionList* out) {
   3407   DbgState* s = (DbgState*)user;
   3408   char cmd[WORD_CAP];
   3409   char arg1[WORD_CAP];
   3410   size_t start, end;
   3411   size_t prefix_len;
   3412   const char* prefix;
   3413 
   3414   dbg_word_bounds(line, cursor, &start, &end);
   3415   out->replace_start = start;
   3416   out->replace_end = end;
   3417   prefix = line + start;
   3418   prefix_len = cursor > start ? cursor - start : 0;
   3419 
   3420   dbg_read_word_at(line, 0, cmd, sizeof(cmd));
   3421   if (start == 0 || cmd[0] == '\0') {
   3422     dbg_complete_commands(out, prefix, prefix_len);
   3423     return;
   3424   }
   3425 
   3426   if (driver_streq(cmd, "info")) {
   3427     dbg_read_word_at(line, start == 0 ? 0 : driver_strlen(cmd), arg1,
   3428                      sizeof(arg1));
   3429     if (arg1[0] == '\0' || start <= driver_strlen(cmd) + 1u) {
   3430       dbg_complete_info(out, prefix, prefix_len);
   3431       return;
   3432     }
   3433   }
   3434 
   3435   if (driver_streq(cmd, ":language") || driver_streq(cmd, ":lang") ||
   3436       driver_streq(cmd, "language")) {
   3437     dbg_complete_languages(out, prefix, prefix_len);
   3438     return;
   3439   }
   3440   if (driver_streq(cmd, "jit") || driver_streq(cmd, "edit") ||
   3441       driver_streq(cmd, "e")) {
   3442     dbg_complete_languages(out, prefix, prefix_len);
   3443     dbg_complete_symbols(s, out, prefix, prefix_len, 1, 0);
   3444     return;
   3445   }
   3446   if (driver_streq(cmd, "b") || driver_streq(cmd, "break")) {
   3447     dbg_complete_symbols(s, out, prefix, prefix_len, 1, 0);
   3448     dbg_complete_files(s, out, prefix, prefix_len);
   3449     return;
   3450   }
   3451   if (driver_streq(cmd, "list") || driver_streq(cmd, "l")) {
   3452     dbg_complete_files(s, out, prefix, prefix_len);
   3453     return;
   3454   }
   3455   if (driver_streq(cmd, "p") || driver_streq(cmd, "print") ||
   3456       driver_streq(cmd, "set")) {
   3457     dbg_complete_locals(s, out, prefix, prefix_len);
   3458     dbg_complete_symbols(s, out, prefix, prefix_len, 0, 1);
   3459     return;
   3460   }
   3461   if (driver_streq(cmd, "d") || driver_streq(cmd, "delete") ||
   3462       driver_streq(cmd, "enable") || driver_streq(cmd, "disable") ||
   3463       driver_streq(cmd, "ignore")) {
   3464     dbg_complete_breakpoint_ids(s, out, prefix, prefix_len);
   3465     return;
   3466   }
   3467 
   3468   dbg_complete_locals(s, out, prefix, prefix_len);
   3469   dbg_complete_symbols(s, out, prefix, prefix_len, 1, 1);
   3470 }
   3471 
   3472 /* ============================================================
   3473  * Script source helpers
   3474  * ============================================================ */
   3475 
   3476 /* Execute a single command string as if typed at the REPL.
   3477  * Returns 1 if the REPL should exit (quit command), 0 otherwise. */
   3478 static int dbg_run_script_cmd(DbgState* s, const char* cmd) {
   3479   char line[LINE_CAP];
   3480   size_t n = driver_strlen(cmd);
   3481   if (n >= LINE_CAP) n = LINE_CAP - 1;
   3482   driver_memcpy(line, cmd, n);
   3483   line[n] = '\0';
   3484   return dbg_dispatch(s, line);
   3485 }
   3486 
   3487 /* Execute debugger commands from a file, one per line.
   3488  * Returns 1 if the REPL should exit (quit command), 0 otherwise. */
   3489 static int dbg_run_script_file(DbgState* s, const char* path) {
   3490   DriverLoad load = {0};
   3491   KitSlice bytes;
   3492   const char* p;
   3493   const char* end;
   3494   char line[LINE_CAP];
   3495   if (driver_load_bytes(&s->env->file_io, DBG_TOOL, path, &load, &bytes) != 0)
   3496     return 0; /* driver_load_bytes already emitted an error */
   3497   p = (const char*)bytes.data;
   3498   end = p + bytes.len;
   3499   while (p < end) {
   3500     const char* nl = p;
   3501     size_t len;
   3502     while (nl < end && *nl != '\n') nl++;
   3503     len = (size_t)(nl - p);
   3504     /* Strip trailing carriage return. */
   3505     if (len > 0 && p[len - 1] == '\r') len--;
   3506     if (len >= LINE_CAP) len = LINE_CAP - 1;
   3507     driver_memcpy(line, p, len);
   3508     line[len] = '\0';
   3509     p = (nl < end) ? nl + 1 : end;
   3510     if (dbg_dispatch(s, line)) {
   3511       driver_release_bytes(&s->env->file_io, &load);
   3512       return 1;
   3513     }
   3514   }
   3515   driver_release_bytes(&s->env->file_io, &load);
   3516   return 0;
   3517 }
   3518 
   3519 /* ============================================================
   3520  * REPL
   3521  * ============================================================ */
   3522 
   3523 static void dbg_repl(DbgState* s) {
   3524   char line[LINE_CAP];
   3525   uint32_t i;
   3526 
   3527   if (!s->batch_mode) driver_printf("kit dbg — 'h' for help, 'q' to quit\n");
   3528 
   3529   /* Phase 1: drain --script / --command sources in order. */
   3530   for (i = 0; i < s->nscript_entries; i++) {
   3531     DbgScriptEntry* e = &s->script_entries[i];
   3532     int quit = (e->kind == DBG_ENTRY_FILE)
   3533                    ? dbg_run_script_file(s, e->value)
   3534                    : dbg_run_script_cmd(s, e->value);
   3535     if (quit) return;
   3536   }
   3537 
   3538   /* Phase 2: in --batch mode, exit without falling through to stdin. */
   3539   if (s->batch_mode) return;
   3540 
   3541   /* Phase 3: interactive loop. */
   3542   for (;;) {
   3543     int n;
   3544     n = driver_read_line_edit(s->env, "(kit) ", line, sizeof(line),
   3545                               &s->line_history, dbg_complete, s);
   3546     if (n == 0) { /* EOF — Ctrl-D */
   3547       driver_printf("\n");
   3548       return;
   3549     }
   3550     if (n == -1) {
   3551       dbg_errf(s, "stdin read error");
   3552       return;
   3553     }
   3554     if (n == -2) { /* SIGINT during prompt */
   3555       driver_printf("\n");
   3556       continue;
   3557     }
   3558     if (dbg_dispatch(s, line)) return;
   3559   }
   3560 }
   3561 
   3562 /* ============================================================
   3563  * Top-level entry
   3564  * ============================================================ */
   3565 
   3566 int driver_dbg(int argc, char** argv) {
   3567   DriverEnv env;
   3568   DbgOpts o = {0};
   3569   KitCompiler* compiler = NULL;
   3570   KitTarget* target = NULL;
   3571   KitJit* jit = NULL;
   3572   DbgState st = {0};
   3573   KitContext ctx;
   3574   KitJitHost jhost;
   3575   KitDbgHost dhost;
   3576   int rc;
   3577 
   3578   if (driver_argv_wants_help(argc, argv, 1)) {
   3579     driver_help_dbg();
   3580     return 0;
   3581   }
   3582 
   3583   driver_env_init(&env);
   3584   o.env = &env;
   3585 
   3586   if (dbg_parse(argc, argv, &o) != 0) {
   3587     dbg_options_release(&o);
   3588     driver_env_fini(&env);
   3589     return 2;
   3590   }
   3591 
   3592   ctx = driver_env_to_context(&env);
   3593   jhost = driver_env_to_jit_host(&env);
   3594   dhost = driver_env_to_dbg_host(&env);
   3595   {
   3596     KitTargetOptions topts;
   3597     memset(&topts, 0, sizeof topts);
   3598     topts.spec = driver_host_target();
   3599     if (kit_target_new(&ctx, &topts, &target) != KIT_OK ||
   3600         driver_compiler_new(target, &ctx, &compiler) != KIT_OK) {
   3601       driver_errf(DBG_TOOL, "failed to initialize compiler");
   3602       kit_target_free(target);
   3603       dbg_options_release(&o);
   3604       driver_env_fini(&env);
   3605       return 1;
   3606     }
   3607   }
   3608 
   3609   rc = dbg_compile_and_jit(&o, compiler, &jhost, &jit);
   3610   if (rc != 0) {
   3611     driver_compiler_free(compiler);
   3612     kit_target_free(target);
   3613     dbg_options_release(&o);
   3614     driver_env_fini(&env);
   3615     return rc;
   3616   }
   3617 
   3618   st.env = &env;
   3619   st.compiler = compiler;
   3620   st.ctx = ctx;
   3621   dbg_fill_compile_options(&o, &st.copts, &st.pp);
   3622   st.jit = jit;
   3623   st.default_jit_lang = dbg_default_language_from_inputs(compiler, &o);
   3624   st.default_jit_name = dbg_jit_default_name(
   3625       st.compiler, st.default_jit_lang, st.default_jit_name_buf,
   3626       sizeof st.default_jit_name_buf);
   3627   st.prog_argc = (int)o.prog_argc;
   3628   st.prog_argv = o.prog_argv;
   3629   st.entry_name = o.entry;
   3630   st.script_entries = o.script_entries;
   3631   st.nscript_entries = o.nscript_entries;
   3632   st.batch_mode = o.batch_mode;
   3633 
   3634   if (driver_inputs_count(&o.inputs) != 0) {
   3635     st.entry_addr = kit_jit_lookup(jit, kit_slice_cstr(o.entry));
   3636     if (!st.entry_addr) {
   3637       driver_errf(DBG_TOOL, "entry symbol not found: %.*s",
   3638                   KIT_SLICE_ARG(kit_slice_cstr(o.entry)));
   3639       kit_jit_free(jit);
   3640       driver_compiler_free(compiler);
   3641       kit_target_free(target);
   3642       dbg_options_release(&o);
   3643       driver_env_fini(&env);
   3644       return 1;
   3645     }
   3646   }
   3647 
   3648   if (kit_jit_session_new(jit, &dhost, &st.session) != KIT_OK) {
   3649     driver_errf(DBG_TOOL,
   3650                 "JIT session not yet implemented in libkit — "
   3651                 "REPL will start in degraded mode (commands will surface "
   3652                 "'session implementation pending' until the lib lands)");
   3653     st.session = NULL;
   3654     /* Keep going so the surrounding driver path is exercised. */
   3655   }
   3656 
   3657   st.view = kit_jit_view(jit);
   3658   if (st.view) {
   3659     if (kit_dwarf_open(&ctx, st.view, &st.dwarf) != KIT_OK) st.dwarf = NULL;
   3660     if (st.dwarf && st.session) {
   3661       kit_jit_session_attach_dwarf(st.session, st.dwarf);
   3662     }
   3663   }
   3664 
   3665   dbg_repl(&st);
   3666 
   3667   driver_line_history_fini(&env, &st.line_history);
   3668   dbg_bps_release_all(&st);
   3669   dbg_compile_sessions_release(&st);
   3670   dbg_sources_release_all(&st);
   3671   if (st.dwarf) kit_dwarf_free(st.dwarf);
   3672   if (st.session) kit_jit_session_free(st.session);
   3673   kit_jit_free(jit);
   3674   driver_compiler_free(compiler);
   3675   kit_target_free(target);
   3676   dbg_options_release(&o);
   3677   driver_env_fini(&env);
   3678   return (st.batch_mode && st.error_count > 0) ? 1 : 0;
   3679 }