stage0 支持类gdb的调试vm手段

This commit is contained in:
zzy
2026-08-06 09:11:15 +08:00
parent ca7cf221c8
commit a56b4cbb70
6 changed files with 783 additions and 47 deletions

View File

@@ -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] <file.sir> [args...]
* -d, --debug stack-canary checks
* -g, --debug-cli interactive debugger (REPL)
* --entry <name> entry function (default: main)
* --trace print every instruction
* -h, --help show help
*
* Usage:
* spl_cli [-d] <file.sir> [entry_point]
* spl 程序 argvargv[0]=file.sirargv[1..]=args。
* REPL 配合 splc0 -g.sir 尾部内嵌 debug 段IR 行 + VAR 行)可显示
* 当前 IR 节点/源行/局部变量。
*/
#include "spl_mcode.h"
#include "spl_syscall.h"
#include "spl_vm.h"
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
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;
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] <file.sir> [args...]\n"
" -d, --debug stack-canary checks\n"
" -g, --debug-cli interactive debugger (REPL)\n"
" --entry <name> 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 <file.sir>\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;
}
if (arg_idx >= argc) {
fprintf(stderr, "Usage: spl_cli [-d] <file.sir> [entry_point]\n");
/* ================================================================
* Debug mapsplc0 -g 追加在 .sir 尾部的文本段)
* IR <ip> <node_ref> <line> <kind>
* VAR <func> <name> <fp_offset> <tid> <is_param>
* ================================================================ */
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", "<fn|ip>", "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", "<var|addr|fp+N>", "read memory (no arg: last pointer)", cmd_x},
{"print", "p", "<var>", "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;
}
const char *path = argv[arg_idx];
const char *entry = argc >= arg_idx + 2 ? argv[arg_idx + 1] : "main";
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 <var|addr|fp+N> (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 <varname>\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) {
cli_opts_t o;
if (parse_args(argc, argv, &o) != 0)
return 1;
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 程序 argvargv[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;
}

View File

@@ -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;
}

View File

@@ -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);

View File

@@ -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':

View File

@@ -81,15 +81,17 @@ static int spl_type_size(spl_type_t t) {
return -1; \
} while (0)
#define CHECK_ADDR(addr, label) do { \
#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); \
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; \
vm->exit_code = 1; \
return -1; \
} \
} while(0)
} 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:

View File

@@ -31,6 +31,9 @@ typedef struct {
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);