From a56b4cbb70b0612c6846b27478c8a3b19cb7047f Mon Sep 17 00:00:00 2001 From: zzy <2450266535@qq.com> Date: Thu, 6 Aug 2026 09:11:15 +0800 Subject: [PATCH] =?UTF-8?q?stage0=20=E6=94=AF=E6=8C=81=E7=B1=BBgdb?= =?UTF-8?q?=E7=9A=84=E8=B0=83=E8=AF=95vm=E6=89=8B=E6=AE=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- stage0/spl_cli.c | 689 ++++++++++++++++++++++++++++++++++++++++--- stage0/spl_mcode.c | 19 ++ stage0/spl_mcode.h | 2 + stage0/spl_syscall.c | 1 + stage0/spl_vm.c | 103 ++++++- stage0/spl_vm.h | 16 +- 6 files changed, 783 insertions(+), 47 deletions(-) diff --git a/stage0/spl_cli.c b/stage0/spl_cli.c index 8b5f9cf..0c68613 100644 --- a/stage0/spl_cli.c +++ b/stage0/spl_cli.c @@ -1,65 +1,686 @@ -/* spl_cli.c — SIR VM launcher +/* spl_cli.c — SIR VM launcher + gdb-style interactive debugger * - * Loads a compiled .sir binary and runs it via the SIR VM. - * Built-in syscalls are auto-registered via spl_syscall_register(). + * Usage: spl_cli [options] [args...] + * -d, --debug stack-canary checks + * -g, --debug-cli interactive debugger (REPL) + * --entry entry function (default: main) + * --trace print every instruction + * -h, --help show help * - * Usage: - * spl_cli [-d] [entry_point] + * spl 程序 argv:argv[0]=file.sir,argv[1..]=args。 + * REPL 配合 splc0 -g(.sir 尾部内嵌 debug 段:IR 行 + VAR 行)可显示 + * 当前 IR 节点/源行/局部变量。 */ #include "spl_mcode.h" #include "spl_syscall.h" #include "spl_vm.h" +#include #include +#include #include +/* ================================================================ + * 命令行解析 + * ================================================================ */ + +typedef struct { + int debug; + int debug_cli; + int trace; + const char *entry; + const char *path; /* file.sir */ + const char **args; /* spl 程序参数(file.sir 之后) */ + int nargs; +} cli_opts_t; + +static void usage(void) { + fprintf(stderr, "Usage: spl_cli [options] [args...]\n" + " -d, --debug stack-canary checks\n" + " -g, --debug-cli interactive debugger (REPL)\n" + " --entry entry function (default: main)\n" + " --trace print every instruction\n" + " -h, --help show help\n"); +} + +/* options 可穿插;`--` 终止 option 解析(本身不作为 file/args)。 + * 第一个非 option 为 file.sir,其后为 spl args。 */ +static int parse_args(int argc, const char **argv, cli_opts_t *o) { + memset(o, 0, sizeof *o); + o->entry = "main"; + int i = 1; + for (; i < argc; i++) { + const char *a = argv[i]; + if (a[0] == '-' && a[1]) { + if (strcmp(a, "--") == 0) { + i++; + break; + } + if (strcmp(a, "-d") == 0 || strcmp(a, "--debug") == 0) { + o->debug = 1; + } else if (strcmp(a, "-g") == 0 || strcmp(a, "--debug-cli") == 0) { + o->debug_cli = 1; + } else if (strcmp(a, "--trace") == 0) { + o->trace = 1; + } else if (strcmp(a, "--entry") == 0) { + if (i + 1 >= argc) { + fprintf(stderr, "spl_cli: --entry needs a name\n"); + return -1; + } + o->entry = argv[++i]; + } else if (strcmp(a, "-h") == 0 || strcmp(a, "--help") == 0) { + usage(); + return -1; + } else { + fprintf(stderr, "spl_cli: unknown option '%s'\n", a); + usage(); + return -1; + } + } else { + break; + } + } + /* 跳过前导 `--`(pipeline 调用会带 `--` 分隔 file 与 args) */ + while (i < argc && strcmp(argv[i], "--") == 0) + i++; + if (i >= argc) { + fprintf(stderr, "spl_cli: missing \n"); + usage(); + return -1; + } + o->path = argv[i++]; + while (i < argc && strcmp(argv[i], "--") == 0) + i++; + o->args = argv + i; + o->nargs = argc - i; + return 0; +} + +/* ================================================================ + * Debug map(splc0 -g 追加在 .sir 尾部的文本段) + * IR + * VAR + * ================================================================ */ + +typedef struct { + usize ip; + usize ref; + int line; + char kind[64]; +} dbg_irline_t; +typedef VEC(dbg_irline_t) dbg_irline_vec_t; + +typedef struct { + char func[128]; + char name[128]; + usize offset; + usize tid; + int is_param; +} dbg_var_t; +typedef VEC(dbg_var_t) dbg_var_vec_t; + +typedef struct { + dbg_irline_vec_t irlines; + dbg_var_vec_t vars; + int loaded; +} dbg_map_t; + +static void dbg_map_load(dbg_map_t *m, const char *text) { + memset(m, 0, sizeof *m); + vec_init(m->irlines); + vec_init(m->vars); + m->loaded = 0; + if (!text) + return; + const char *p = strstr(text, "SPLDBG"); + if (!p) + return; + p += 6; + while (p && *p) { + const char *nl = strchr(p, '\n'); + size_t len = nl ? (size_t)(nl - p) : strlen(p); + char line[512]; + if (len >= sizeof line) + len = sizeof line - 1; + memcpy(line, p, len); + line[len] = 0; + if (strncmp(line, "IR ", 3) == 0) { + dbg_irline_t ir; + memset(&ir, 0, sizeof ir); + sscanf(line + 3, "%zu %zu %d %63s", &ir.ip, &ir.ref, &ir.line, ir.kind); + vec_push(m->irlines, ir); + } else if (strncmp(line, "VAR ", 4) == 0) { + dbg_var_t v; + memset(&v, 0, sizeof v); + sscanf(line + 4, "%127s %127s %zu %zu %d", v.func, v.name, &v.offset, &v.tid, + &v.is_param); + vec_push(m->vars, v); + } + if (!nl) + break; + p = nl + 1; + } + m->loaded = 1; +} + +/* 当前 ip 所属 IR 节点行(ip 恰好是某节点首指令) */ +static const dbg_irline_t *irline_at_exact(dbg_map_t *m, usize ip) { + for (usize i = 0; i < m->irlines.size; i++) + if (m->irlines.data[i].ip == ip) + return &m->irlines.data[i]; + return NULL; +} + +/* ================================================================ + * 调试器核心 + * ================================================================ */ + +typedef struct { + spl_vm_t *vm; + spl_prog_t *prog; + const char *entry; + dbg_map_t map; + usize last_ptr; /* p 最近读到的值(x 无参重读) */ + int quit; +} dbg_t; + +/* ---- 位置/指令显示辅助 ---- */ + +static const char *fn_name_at(spl_prog_t *prog, usize ip) { + for (usize i = 0; i < vec_size(prog->funcs); i++) { + spl_func_t *f = &vec_at(prog->funcs, i); + if (ip >= f->address && ip < f->address + f->ninsns) + return f->name ? f->name : "?"; + } + return "?"; +} + +/* ip 所在函数的指令范围 [*start, *end) */ +static void cur_func_bounds(spl_prog_t *prog, usize ip, usize *start, usize *end) { + *start = 0; + *end = vec_size(prog->insns); + for (usize i = 0; i < vec_size(prog->funcs); i++) { + spl_func_t *f = &vec_at(prog->funcs, i); + if (ip >= f->address && ip < f->address + f->ninsns) { + *start = f->address; + *end = f->address + f->ninsns; + return; + } + } +} + +/* 格式化一条指令(不含行号前缀) */ +static void fmt_insn(const spl_ins_t *ins, char *buf, size_t n) { + snprintf(buf, n, "%-11s %-5s %zu", spl_opcode_name(ins->opcode), spl_type_tag_name(ins->type), + ins->imm); +} + +static void dbg_show_location(dbg_t *d) { + spl_vm_t *vm = d->vm; + if (vm->ip >= vec_size(vm->prog->insns)) { + printf(" (end of program)\n"); + return; + } + char ib[80]; + fmt_insn(&vec_at(vm->prog->insns, vm->ip), ib, sizeof ib); + printf(" %s @ ip=%zu %s [sp=%zu fp=%zu cp=%zu]\n", fn_name_at(vm->prog, vm->ip), vm->ip, + ib, vm->sp, vm->fp, vm->cp); + const dbg_irline_t *ir = irline_at_exact(&d->map, vm->ip); + if (ir) + printf(" -> ir node#%zu line %d %s\n", ir->ref, ir->line, ir->kind); +} + +static void dbg_handle_status(dbg_t *d, int r) { + if (r == 1) { + printf(" program halted, exit_code=%d\n", d->vm->exit_code); + d->quit = 1; + } else if (r == 2) { + printf(" breakpoint hit\n"); + } else if (r == -1) { + printf(" VM error: %s\n", d->vm->error_msg[0] ? d->vm->error_msg : "(no detail)"); + d->quit = 1; + } +} + +/* ---- 变量查找(p / x 复用) ---- */ + +static dbg_var_t *dbg_find_var(dbg_t *d, const char *fn, const char *name) { + for (usize i = 0; i < d->map.vars.size; i++) { + dbg_var_t *v = &d->map.vars.data[i]; + if (strcmp(v->func, fn) == 0 && strcmp(v->name, name) == 0) + return v; + } + return NULL; +} + +/* ================================================================ + * 命令 + * ================================================================ */ + +static int cmd_help(dbg_t *d, const char *args); +static int cmd_run(dbg_t *d, const char *args); +static int cmd_step(dbg_t *d, const char *args); +static int cmd_next(dbg_t *d, const char *args); +static int cmd_finish(dbg_t *d, const char *args); +static int cmd_break(dbg_t *d, const char *args); +static int cmd_delete(dbg_t *d, const char *args); +static int cmd_info(dbg_t *d, const char *args); +static int cmd_list(dbg_t *d, const char *args); +static int cmd_backtrace(dbg_t *d, const char *args); +static int cmd_stack(dbg_t *d, const char *args); +static int cmd_x(dbg_t *d, const char *args); +static int cmd_p(dbg_t *d, const char *args); +static int cmd_quit(dbg_t *d, const char *args); + +typedef struct { + const char *name; + const char *alias; + const char *usage; + const char *help; + int (*fn)(dbg_t *, const char *); +} dbg_cmd_t; + +static const dbg_cmd_t cmds[] = { + /* 会话 */ + {"help", "h", "[cmd]", "list commands / help", cmd_help}, + {"quit", "q", "", "quit debugger", cmd_quit}, + /* 执行 */ + {"run", "r", "", "run to next breakpoint or exit", cmd_run}, + {"continue", "c", "", "alias of run", cmd_run}, + {"step", "s", "", "step one instruction (into calls)", cmd_step}, + {"next", "n", "", "step over a call", cmd_next}, + {"finish", "f", "", "run until current function returns", cmd_finish}, + /* 断点 */ + {"break", "b", "", "set breakpoint (no arg: list)", cmd_break}, + {"delete", "d", "", "clear all breakpoints", cmd_delete}, + /* 信息 */ + {"info", "i", "b|stack|locals|args|var", "breakpoints/stack/locals", cmd_info}, + {"list", "l", "[N]", "disassemble ip±N of current function", cmd_list}, + {"backtrace", "bt", "", "print call stack backtrace", cmd_backtrace}, + {"stack", "st", "", "dump stack words", cmd_stack}, + /* 内存/变量 */ + {"x", "x", "", "read memory (no arg: last pointer)", cmd_x}, + {"print", "p", "", "print local variable (needs splc0 -g)", cmd_p}, + {NULL, NULL, NULL, NULL, NULL}, +}; + +static const dbg_cmd_t *find_cmd(const char *name) { + for (const dbg_cmd_t *c = cmds; c->name; c++) + if (!strcmp(c->name, name) || (c->alias && !strcmp(c->alias, name))) + return c; + return NULL; +} + +static int cmd_help(dbg_t *d, const char *args) { + (void)d; + if (*args) { + const dbg_cmd_t *c = find_cmd(args); + if (c) { + printf(" %-10s %-22s %s", c->name, c->usage, c->help); + if (c->alias) + printf(" (alias: %s)", c->alias); + printf("\n"); + } else { + printf(" unknown command '%s'\n", args); + } + return 0; + } + printf("commands:\n"); + for (const dbg_cmd_t *c = cmds; c->name; c++) { + printf(" %-10s %-22s %s", c->name, c->usage, c->help); + if (c->alias) + printf(" (alias: %s)", c->alias); + printf("\n"); + } + return 0; +} + +/* ---- 执行 ---- */ + +static int ip_is_breakpoint(dbg_t *d) { + for (usize i = 0; i < vec_size(d->vm->breakpoints); i++) + if (d->vm->breakpoints.data[i] == (usize)d->vm->ip) + return 1; + return 0; +} + +static int cmd_run(dbg_t *d, const char *args) { + (void)args; + if (ip_is_breakpoint(d)) /* 越过当前断点指令,避免原地重停 */ + spl_vm_skip_breakpoint(d->vm); + dbg_handle_status(d, spl_vm_run_until(d->vm, 0)); + return 0; +} + +static int cmd_step(dbg_t *d, const char *args) { + (void)args; + dbg_handle_status(d, spl_vm_run_once(d->vm)); + return 0; +} + +static int cmd_next(dbg_t *d, const char *args) { + (void)args; + /* 执行 1 条;若进入被调(cp 增),继续执行到返回原深度 */ + usize base_cp = d->vm->cp; + int r = spl_vm_run_once(d->vm); + if (r) + return dbg_handle_status(d, r), 0; + while (d->vm->cp > base_cp) { + r = spl_vm_run_once(d->vm); + if (r) + return dbg_handle_status(d, r), 0; + } + return 0; +} + +static int cmd_finish(dbg_t *d, const char *args) { + (void)args; + usize base_cp = d->vm->cp; + while (d->vm->cp >= base_cp) { + int r = spl_vm_run_once(d->vm); + if (r) + return dbg_handle_status(d, r), 0; + } + return 0; +} + +/* ---- 断点 ---- */ + +static int is_all_digits(const char *s) { + if (!*s) + return 0; + for (; *s; s++) + if (!isdigit((unsigned char)*s)) + return 0; + return 1; +} + +static int cmd_break(dbg_t *d, const char *args) { + if (!*args) { + printf(" ip breakpoints:\n"); + for (usize i = 0; i < vec_size(d->vm->breakpoints); i++) + printf(" %zu\n", d->vm->breakpoints.data[i]); + printf(" fn breakpoints:\n"); + for (usize i = 0; i < vec_size(d->vm->fn_breakpoints); i++) + printf(" %s\n", d->vm->fn_breakpoints.data[i]); + return 0; + } + while (*args && isspace((unsigned char)*args)) + args++; + if (is_all_digits(args)) { + usize ip = (usize)strtoul(args, NULL, 10); + spl_vm_add_breakpoint(d->vm, ip); + printf(" breakpoint at ip=%zu\n", ip); + } else { + spl_vm_add_breakpoint_fn(d->vm, args); + printf(" breakpoint at function '%s'\n", args); + } + return 0; +} + +static int cmd_delete(dbg_t *d, const char *args) { + (void)args; + spl_vm_clear_breakpoints(d->vm); + printf(" breakpoints cleared\n"); + return 0; +} + +/* ---- 信息 ---- */ + +/* 列出当前函数变量:args_flag=1 列参数,local_flag=1 列局部 */ +static void list_vars(dbg_t *d, int args_flag, int local_flag) { + if (!d->map.loaded) { + printf(" no debug info (compile with splc0 -g)\n"); + return; + } + const char *fn = fn_name_at(d->prog, d->vm->ip); + int any = 0; + for (usize i = 0; i < d->map.vars.size; i++) { + dbg_var_t *v = &d->map.vars.data[i]; + if (strcmp(v->func, fn) != 0) + continue; + if ((v->is_param && !args_flag) || (!v->is_param && !local_flag)) + continue; + printf(" %-20s @ fp+%-5zu %s\n", v->name, v->offset, v->is_param ? "[param]" : "[local]"); + any = 1; + } + if (!any) + printf(" (no matching variables in %s)\n", fn); +} + +static int cmd_info(dbg_t *d, const char *args) { + while (*args && isspace((unsigned char)*args)) + args++; + if (!strncmp(args, "b", 1) || !strncmp(args, "break", 5)) + return cmd_break(d, ""); + if (!strncmp(args, "st", 2) || !strncmp(args, "stack", 5)) + spl_vm_stackdump(d->vm, d->vm->sp); + else if (!strncmp(args, "l", 1) || !strncmp(args, "local", 5)) + list_vars(d, 0, 1); + else if (!strncmp(args, "a", 1) || !strncmp(args, "arg", 3)) + list_vars(d, 1, 0); + else if (!strncmp(args, "v", 1) || !strncmp(args, "var", 3)) + list_vars(d, 1, 1); + else + printf(" usage: info b|stack|locals|args|var\n"); + return 0; +} + +/* l [N]:当前函数内 ip±N 反汇编 */ +static int cmd_list(dbg_t *d, const char *args) { + usize ip = d->vm->ip; + long half = 5; + if (*args) + half = strtol(args, NULL, 10); + if (half < 0) + half = 0; + usize fstart, fend; + cur_func_bounds(d->prog, ip, &fstart, &fend); + usize lo = (ip > (usize)half) ? ip - (usize)half : fstart; + usize hi = ip + (usize)half; + if (hi >= fend) + hi = fend - 1; + if (lo < fstart) + lo = fstart; + for (usize i = lo; i <= hi; i++) { + char ib[80]; + fmt_insn(&vec_at(d->prog->insns, i), ib, sizeof ib); + printf("%s%6zu: %s", i == ip ? "=>" : " ", i, ib); + const dbg_irline_t *ir = irline_at_exact(&d->map, i); + if (ir) + printf(" || node#%zu line %d %s", ir->ref, ir->line, ir->kind); + printf("\n"); + } + return 0; +} + +static int cmd_backtrace(dbg_t *d, const char *args) { + (void)args; + spl_vm_backtrace(d->vm, d->vm->fp); + return 0; +} + +static int cmd_stack(dbg_t *d, const char *args) { + (void)args; + spl_vm_stackdump(d->vm, d->vm->sp); + return 0; +} + +/* ---- 内存 / 变量 ---- */ + +static int cmd_x(dbg_t *d, const char *args) { + while (*args && isspace((unsigned char)*args)) + args++; + unsigned long long addr = 0; + if (*args) { + if (strncmp(args, "fp+", 3) == 0 || strncmp(args, "sp+", 3) == 0) { + int is_fp = args[0] == 'f'; + unsigned long long off = strtoull(args + 3, NULL, 0); + addr = (uintptr_t)((char *)&d->vm->stacks.data[is_fp ? d->vm->fp : d->vm->sp] + off); + printf(" (%s+%llu)\n", is_fp ? "fp" : "sp", off); + } else if (d->map.loaded && !isdigit((unsigned char)*args)) { + const char *fn = fn_name_at(d->prog, d->vm->ip); + dbg_var_t *v = dbg_find_var(d, fn, args); + if (v) { + addr = (uintptr_t)((char *)&d->vm->stacks.data[d->vm->fp] + v->offset); + printf(" (%s @ fp+%zu)\n", v->name, v->offset); + } else { + printf(" no variable '%s' in %s\n", args, fn); + } + } else { + addr = strtoull(args, NULL, 0); + } + } else { + addr = (unsigned long long)d->last_ptr; + } + if (!addr) { + printf(" usage: x (no arg: re-read last printed pointer)\n"); + return 0; + } + unsigned char *sbase = (unsigned char *)d->vm->stacks.data; + usize slen = d->vm->config.max_stack_depth * sizeof(spl_val_t); + printf(" addr=%#llx stack=[%p,+%zu) in=%d\n", addr, sbase, slen, + addr >= (uintptr_t)sbase && addr < (uintptr_t)(sbase + slen)); + unsigned char *p = (unsigned char *)(uintptr_t)addr; + printf(" "); + for (int i = 0; i < 32; i++) + printf("%02x ", p[i]); + printf("\n"); + return 0; +} + +static int cmd_p(dbg_t *d, const char *args) { + while (*args && isspace((unsigned char)*args)) + args++; + if (!*args) { + printf(" usage: p \n"); + return 0; + } + if (!d->map.loaded) { + printf(" no debug info (compile with splc0 -g)\n"); + return 0; + } + const char *fn = fn_name_at(d->prog, d->vm->ip); + dbg_var_t *v = dbg_find_var(d, fn, args); + if (!v) { + printf(" no variable '%s' in %s\n", args, fn); + return 0; + } + spl_val_t *addr = (spl_val_t *)((char *)&d->vm->stacks.data[d->vm->fp] + v->offset); + printf(" %s = %zu (0x%zx) @ fp+%zu\n", v->name, *addr, *addr, v->offset); + d->last_ptr = (usize)*addr; /* 供 x 无参重读 */ + return 0; +} + +static int cmd_quit(dbg_t *d, const char *args) { + (void)args; + d->quit = 1; + return 0; +} + +static void repl_loop(dbg_t *d) { + char line[1024]; + while (!d->quit) { + dbg_show_location(d); + printf("spl-dbg> "); + fflush(stdout); + if (!fgets(line, sizeof line, stdin)) + break; + char *nl = strchr(line, '\n'); + if (nl) + *nl = 0; + char *p = line; + while (*p && isspace((unsigned char)*p)) + p++; + if (!*p) + continue; + /* 拆命令名 + 参数 */ + char *sp = p; + while (*sp && !isspace((unsigned char)*sp)) + sp++; + char saved = *sp; + *sp = 0; + const dbg_cmd_t *c = find_cmd(p); + *sp = saved; + char *args = sp; + while (*args && isspace((unsigned char)*args)) + args++; + if (c) { + c->fn(d, args); + } else { + printf(" unknown command '%s' (h for help)\n", p); + } + } +} + int main(int argc, const char **argv) { - int debug_mode = 0; - int arg_idx = 1; - - if (argc >= 2 && strcmp(argv[1], "-d") == 0) { - debug_mode = 1; - arg_idx = 2; - } - - if (arg_idx >= argc) { - fprintf(stderr, "Usage: spl_cli [-d] [entry_point]\n"); + cli_opts_t o; + if (parse_args(argc, argv, &o) != 0) return 1; - } - - const char *path = argv[arg_idx]; - const char *entry = argc >= arg_idx + 2 ? argv[arg_idx + 1] : "main"; spl_prog_t prog; - if (spl_prog_load_from_file(path, &prog) != 0) { - fprintf(stderr, "spl_cli: cannot load '%s'\n", path); + if (spl_prog_load_from_file(o.path, &prog) != 0) { + fprintf(stderr, "spl_cli: cannot load '%s'\n", o.path); return 1; } - spl_syscall_register(&prog); spl_vm_t vm; spl_vm_init(&vm); - if (debug_mode) spl_vm_set_debug(&vm, 1); + if (o.debug) + spl_vm_set_debug(&vm, 1); + if (o.trace) + spl_vm_set_trace(&vm, 1); if (spl_vm_load_prog(&vm, &prog) != 0) { - fprintf(stderr, "vm: prog '%s' not found\n", entry); - spl_prog_drop(&prog); - return 1; - } - if (spl_vm_prepare(&vm, entry, argc, argv, NULL) != 0) { - fprintf(stderr, "vm: entry point '%s' not found\n", entry); + fprintf(stderr, "spl_cli: failed to load prog\n"); + spl_vm_drop(&vm); spl_prog_drop(&prog); return 1; } - int ret = spl_vm_run_until(&vm, 0); + /* spl 程序 argv:argv[0]=file.sir(程序名),argv[1..]=args */ + const char **spl_argv = (const char **)malloc(sizeof(char *) * (size_t)(o.nargs + 1)); + if (!spl_argv) { + spl_vm_drop(&vm); + spl_prog_drop(&prog); + return 1; + } + spl_argv[0] = o.path; + for (int i = 0; i < o.nargs; i++) + spl_argv[i + 1] = o.args[i]; + + if (spl_vm_prepare(&vm, o.entry, o.nargs + 1, spl_argv, NULL) != 0) { + fprintf(stderr, "spl_cli: entry point '%s' not found\n", o.entry); + free(spl_argv); + spl_vm_drop(&vm); + spl_prog_drop(&prog); + return 1; + } + + int ret = 0; + if (o.debug_cli) { + dbg_t d; + d.vm = &vm; + d.prog = &prog; + d.entry = o.entry; + d.quit = 0; + d.last_ptr = 0; + dbg_map_load(&d.map, prog.debug); + repl_loop(&d); + vec_free(d.map.irlines); + vec_free(d.map.vars); + } else { + ret = spl_vm_run_until(&vm, 0); + if (ret == 2) + fprintf(stderr, "spl_cli: hit breakpoint (run with -g to debug)\n"); + } + + free(spl_argv); spl_vm_drop(&vm); spl_prog_drop(&prog); - if (ret < 0) { - fprintf(stderr, "spl_cli: VM error (exit_code=%d)\n", vm.exit_code); - return (int)vm.exit_code; - } + if (ret < 0) + return 1; return (int)vm.exit_code; } diff --git a/stage0/spl_mcode.c b/stage0/spl_mcode.c index e14fa9e..4508b6a 100644 --- a/stage0/spl_mcode.c +++ b/stage0/spl_mcode.c @@ -39,6 +39,8 @@ void spl_prog_drop(spl_prog_t *prog) { vec_for(prog->strtab, i) { free((void *)vec_at(prog->strtab, i)); } vec_free(prog->strtab); map_free(prog->symtab); + free(prog->debug); + prog->debug = NULL; } /* ---- LE read/write helpers ---- */ @@ -201,6 +203,23 @@ int spl_prog_load_from_file(const char *fname, spl_prog_t *prog) { vec_push(prog->gdata, entry); } + /* ---- debug 段(splc0 -g 追加在文件尾部) ---- */ + prog->debug = NULL; + prog->debug_size = 0; + { + ptrdiff_t used = p - data; + if (used >= 0 && (usize)used < (usize)len) { + size_t dlen = (size_t)len - (size_t)used; + char *dbg = (char *)malloc(dlen + 1); + if (dbg) { + memcpy(dbg, data + used, dlen); + dbg[dlen] = 0; + prog->debug = dbg; + prog->debug_size = dlen; + } + } + } + free(data); return 0; } diff --git a/stage0/spl_mcode.h b/stage0/spl_mcode.h index 83a8d3a..78140f0 100644 --- a/stage0/spl_mcode.h +++ b/stage0/spl_mcode.h @@ -193,6 +193,8 @@ typedef struct spl_prog { spl_data_t gdata; spl_strtab_t strtab; spl_symtab_t symtab; + char *debug; /* 文件尾部 debug 段(splc0 -g 追加的文本),无则 NULL */ + usize debug_size; } spl_prog_t; void spl_prog_init(spl_prog_t *prog); diff --git a/stage0/spl_syscall.c b/stage0/spl_syscall.c index 1da61ff..08dfa57 100644 --- a/stage0/spl_syscall.c +++ b/stage0/spl_syscall.c @@ -200,6 +200,7 @@ static spl_val_t vm_printf(int nargs, spl_val_t *args) { for (usize j = 0; j < args[arg_idx]; ++j) { vec_push(buffer, ((const char *)args[arg_idx + 1])[j]); } + arg_idx += 1; /* %.*s 消耗 2 个实参(len, data),遇 % 已 +1,这里补 1 */ i += 2; } break; case 'd': diff --git a/stage0/spl_vm.c b/stage0/spl_vm.c index f468b5d..aeb7a07 100644 --- a/stage0/spl_vm.c +++ b/stage0/spl_vm.c @@ -81,15 +81,17 @@ static int spl_type_size(spl_type_t t) { return -1; \ } while (0) -#define CHECK_ADDR(addr, label) do { \ - if (vm->debug_addr && (uintptr_t)(addr) < 0x1000) { \ - fprintf(stderr, "vm: %s at ip=%zd: LOW ADDR=%p sp=%zd fp=%zd\n", \ - label, vm->ip - 1, (void*)(uintptr_t)(addr), vm->sp, vm->fp); \ - spl_vm_stackdump(vm, vm->sp); \ - spl_vm_backtrace(vm, vm->fp); \ - vm->exit_code = 1; return -1; \ - } \ -} while(0) +#define CHECK_ADDR(addr, label) \ + do { \ + if (vm->debug_addr && (uintptr_t)(addr) < 0x1000) { \ + fprintf(stderr, "vm: %s at ip=%zd: LOW ADDR=%p sp=%zd fp=%zd\n", label, vm->ip - 1, \ + (void *)(uintptr_t)(addr), vm->sp, vm->fp); \ + spl_vm_stackdump(vm, vm->sp); \ + spl_vm_backtrace(vm, vm->fp); \ + vm->exit_code = 1; \ + return -1; \ + } \ + } while (0) /* ================================================================ * Stack push/pop (stacks.data is pre-allocated in init) @@ -553,12 +555,15 @@ void spl_vm_init_ex(spl_vm_t *vm, int stack_size, int call_depth) { vec_realloc(vm->stacks, (usize)vm->config.max_stack_depth); vec_init(vm->frames); vec_realloc(vm->frames, (usize)vm->config.max_call_depth); + vec_init(vm->breakpoints); + vec_init(vm->fn_breakpoints); vm->sp = vm->fp = vm->ip = vm->cp = vm->gp = 0; vm->prog = NULL; vm->trace = 0; vm->debug = 1; vm->debug_addr = 0; + vm->skip_bp = 0; vm->exit_code = 0; } @@ -569,6 +574,68 @@ void spl_vm_drop(spl_vm_t *vm) { return; vec_free(vm->stacks); vec_free(vm->frames); + for (usize i = 0; i < vec_size(vm->fn_breakpoints); i++) + free(vm->fn_breakpoints.data[i]); + vec_free(vm->fn_breakpoints); + vec_free(vm->breakpoints); +} + +void spl_vm_add_breakpoint(spl_vm_t *vm, usize ip) { + if (!vm) + return; + for (usize i = 0; i < vec_size(vm->breakpoints); i++) + if (vm->breakpoints.data[i] == ip) + return; + vec_push(vm->breakpoints, ip); +} + +void spl_vm_add_breakpoint_fn(spl_vm_t *vm, const char *name) { + if (!vm || !name) + return; + for (usize i = 0; i < vec_size(vm->fn_breakpoints); i++) + if (strcmp(vm->fn_breakpoints.data[i], name) == 0) + return; + vec_push(vm->fn_breakpoints, strdup(name)); +} + +void spl_vm_clear_breakpoints(spl_vm_t *vm) { + if (!vm) + return; + vm->breakpoints.size = 0; + for (usize i = 0; i < vec_size(vm->fn_breakpoints); i++) + free(vm->fn_breakpoints.data[i]); + vm->fn_breakpoints.size = 0; +} + +void spl_vm_skip_breakpoint(spl_vm_t *vm) { + if (vm) + vm->skip_bp = 1; +} + +/* 前向声明(定义在文件尾部 dump 区域) */ +static const char *func_name_by_ip(spl_prog_t *prog, spl_val_t ip); + +/* 函数名断点命中:addr 所在函数名是否在断点表 */ +static int fn_breakpoint_hit(spl_vm_t *vm, spl_val_t addr) { + if (!vm || !vm->prog || !vec_size(vm->fn_breakpoints)) + return 0; + const char *fn = func_name_by_ip(vm->prog, addr); + if (!fn) + return 0; + for (usize i = 0; i < vec_size(vm->fn_breakpoints); i++) + if (strcmp(vm->fn_breakpoints.data[i], fn) == 0) + return 1; + return 0; +} + +/* ip 断点命中:vm->ip(即将执行的指令)在断点表 */ +static int ip_breakpoint_hit(spl_vm_t *vm) { + if (!vm || !vec_size(vm->breakpoints)) + return 0; + for (usize i = 0; i < vec_size(vm->breakpoints); i++) + if (vm->breakpoints.data[i] == (usize)vm->ip) + return 1; + return 0; } /* ================================================================ @@ -678,6 +745,16 @@ int spl_vm_run_once(spl_vm_t *vm) { return -1; } + /* 断点:即将执行的指令命中 ip 断点 → 暂停(返回 2)。 + * skip_bp 置位时执行当前指令(continue 越过当前断点)。 */ + if (ip_breakpoint_hit(vm)) { + if (vm->skip_bp) { + vm->skip_bp = 0; + } else { + return 2; + } + } + ins = &vec_at(prog->insns, vm->ip); vm->ip++; @@ -870,14 +947,20 @@ int spl_vm_run_once(spl_vm_t *vm) { case SPL_CALL: { spl_val_t _nargs = ins->imm; spl_val_t _addr = POP(); + int _hit = fn_breakpoint_hit(vm, _addr); spl_vm_call(vm, _addr, _nargs); + if (_hit) + return 2; /* 已进入函数,暂停在函数入口 */ break; } case SPL_CALLI: { spl_val_t _addr = POP(); spl_val_t _nargs = POP(); + int _hit = fn_breakpoint_hit(vm, _addr); spl_vm_call(vm, _addr, _nargs); + if (_hit) + return 2; break; } @@ -1053,7 +1136,7 @@ int spl_vm_run_once(spl_vm_t *vm) { } case SPL_BK: { - return 0; /* breakpoint: pause execution */ + return 2; /* breakpoint: pause execution */ } default: diff --git a/stage0/spl_vm.h b/stage0/spl_vm.h index 22771c2..15cdd66 100644 --- a/stage0/spl_vm.h +++ b/stage0/spl_vm.h @@ -28,9 +28,12 @@ typedef struct { uintptr_t cp; // call pointer uintptr_t ip; // instr pointer int exit_code; - int trace; /* non-zero to print each instruction */ - int debug; /* non-zero to enable canary checks */ - int debug_addr; /* non-zero to check for low-address memory access */ + int trace; /* non-zero to print each instruction */ + int debug; /* non-zero to enable canary checks */ + int debug_addr; /* non-zero to check for low-address memory access */ + int skip_bp; /* non-zero: run_once 忽略下一次断点(continue 越过当前断点指令) */ + VEC(usize) breakpoints; /* ip 断点(执行到该指令前暂停,run_once 返回 2) */ + VEC(char *) fn_breakpoints; /* 函数名断点(CALL/CALLI 目标函数入口暂停) */ spl_prog_t *prog; char error_msg[1024]; struct { @@ -61,6 +64,13 @@ void spl_vm_set_debug(spl_vm_t *vm, int enabled); int spl_vm_run_once(spl_vm_t *vm); int spl_vm_run_until(spl_vm_t *vm, size_t step); +/* 断点:ip 断点 / 函数名断点(CALL/CALLI 目标函数入口)。命中时 run_once 返回 2。 */ +void spl_vm_add_breakpoint(spl_vm_t *vm, usize ip); +void spl_vm_add_breakpoint_fn(spl_vm_t *vm, const char *name); +void spl_vm_clear_breakpoints(spl_vm_t *vm); +/* 让 run_once 执行当前指令(忽略一次断点命中);continue 越过当前断点用 */ +void spl_vm_skip_breakpoint(spl_vm_t *vm); + void spl_vm_dump_instr(spl_vm_t *vm, spl_val_t ip); void spl_vm_stackdump(spl_vm_t *vm, spl_val_t sp); int spl_vm_backtrace(spl_vm_t *vm, spl_val_t fp);