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