stage0 支持类gdb的调试vm手段
This commit is contained in:
681
stage0/spl_cli.c
681
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.
|
* Usage: spl_cli [options] <file.sir> [args...]
|
||||||
* Built-in syscalls are auto-registered via spl_syscall_register().
|
* -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 程序 argv:argv[0]=file.sir,argv[1..]=args。
|
||||||
* spl_cli [-d] <file.sir> [entry_point]
|
* REPL 配合 splc0 -g(.sir 尾部内嵌 debug 段:IR 行 + VAR 行)可显示
|
||||||
|
* 当前 IR 节点/源行/局部变量。
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "spl_mcode.h"
|
#include "spl_mcode.h"
|
||||||
#include "spl_syscall.h"
|
#include "spl_syscall.h"
|
||||||
#include "spl_vm.h"
|
#include "spl_vm.h"
|
||||||
|
|
||||||
|
#include <ctype.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
#include <string.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) {
|
typedef struct {
|
||||||
debug_mode = 1;
|
int debug;
|
||||||
arg_idx = 2;
|
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");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (arg_idx >= argc) {
|
/* options 可穿插;`--` 终止 option 解析(本身不作为 file/args)。
|
||||||
fprintf(stderr, "Usage: spl_cli [-d] <file.sir> [entry_point]\n");
|
* 第一个非 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ================================================================
|
||||||
|
* Debug map(splc0 -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;
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
const char *path = argv[arg_idx];
|
static int cmd_break(dbg_t *d, const char *args) {
|
||||||
const char *entry = argc >= arg_idx + 2 ? argv[arg_idx + 1] : "main";
|
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;
|
spl_prog_t prog;
|
||||||
if (spl_prog_load_from_file(path, &prog) != 0) {
|
if (spl_prog_load_from_file(o.path, &prog) != 0) {
|
||||||
fprintf(stderr, "spl_cli: cannot load '%s'\n", path);
|
fprintf(stderr, "spl_cli: cannot load '%s'\n", o.path);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
spl_syscall_register(&prog);
|
spl_syscall_register(&prog);
|
||||||
|
|
||||||
spl_vm_t vm;
|
spl_vm_t vm;
|
||||||
spl_vm_init(&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) {
|
if (spl_vm_load_prog(&vm, &prog) != 0) {
|
||||||
fprintf(stderr, "vm: prog '%s' not found\n", entry);
|
fprintf(stderr, "spl_cli: failed to load prog\n");
|
||||||
spl_prog_drop(&prog);
|
spl_vm_drop(&vm);
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
if (spl_vm_prepare(&vm, entry, argc, argv, NULL) != 0) {
|
|
||||||
fprintf(stderr, "vm: entry point '%s' not found\n", entry);
|
|
||||||
spl_prog_drop(&prog);
|
spl_prog_drop(&prog);
|
||||||
return 1;
|
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_vm_drop(&vm);
|
||||||
spl_prog_drop(&prog);
|
spl_prog_drop(&prog);
|
||||||
|
|
||||||
if (ret < 0) {
|
if (ret < 0)
|
||||||
fprintf(stderr, "spl_cli: VM error (exit_code=%d)\n", vm.exit_code);
|
return 1;
|
||||||
return (int)vm.exit_code;
|
|
||||||
}
|
|
||||||
return (int)vm.exit_code;
|
return (int)vm.exit_code;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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_for(prog->strtab, i) { free((void *)vec_at(prog->strtab, i)); }
|
||||||
vec_free(prog->strtab);
|
vec_free(prog->strtab);
|
||||||
map_free(prog->symtab);
|
map_free(prog->symtab);
|
||||||
|
free(prog->debug);
|
||||||
|
prog->debug = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ---- LE read/write helpers ---- */
|
/* ---- 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);
|
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);
|
free(data);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -193,6 +193,8 @@ typedef struct spl_prog {
|
|||||||
spl_data_t gdata;
|
spl_data_t gdata;
|
||||||
spl_strtab_t strtab;
|
spl_strtab_t strtab;
|
||||||
spl_symtab_t symtab;
|
spl_symtab_t symtab;
|
||||||
|
char *debug; /* 文件尾部 debug 段(splc0 -g 追加的文本),无则 NULL */
|
||||||
|
usize debug_size;
|
||||||
} spl_prog_t;
|
} spl_prog_t;
|
||||||
|
|
||||||
void spl_prog_init(spl_prog_t *prog);
|
void spl_prog_init(spl_prog_t *prog);
|
||||||
|
|||||||
@@ -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) {
|
for (usize j = 0; j < args[arg_idx]; ++j) {
|
||||||
vec_push(buffer, ((const char *)args[arg_idx + 1])[j]);
|
vec_push(buffer, ((const char *)args[arg_idx + 1])[j]);
|
||||||
}
|
}
|
||||||
|
arg_idx += 1; /* %.*s 消耗 2 个实参(len, data),遇 % 已 +1,这里补 1 */
|
||||||
i += 2;
|
i += 2;
|
||||||
} break;
|
} break;
|
||||||
case 'd':
|
case 'd':
|
||||||
|
|||||||
@@ -81,13 +81,15 @@ static int spl_type_size(spl_type_t t) {
|
|||||||
return -1; \
|
return -1; \
|
||||||
} while (0)
|
} while (0)
|
||||||
|
|
||||||
#define CHECK_ADDR(addr, label) do { \
|
#define CHECK_ADDR(addr, label) \
|
||||||
|
do { \
|
||||||
if (vm->debug_addr && (uintptr_t)(addr) < 0x1000) { \
|
if (vm->debug_addr && (uintptr_t)(addr) < 0x1000) { \
|
||||||
fprintf(stderr, "vm: %s at ip=%zd: LOW ADDR=%p sp=%zd fp=%zd\n", \
|
fprintf(stderr, "vm: %s at ip=%zd: LOW ADDR=%p sp=%zd fp=%zd\n", label, vm->ip - 1, \
|
||||||
label, vm->ip - 1, (void*)(uintptr_t)(addr), vm->sp, vm->fp); \
|
(void *)(uintptr_t)(addr), vm->sp, vm->fp); \
|
||||||
spl_vm_stackdump(vm, vm->sp); \
|
spl_vm_stackdump(vm, vm->sp); \
|
||||||
spl_vm_backtrace(vm, vm->fp); \
|
spl_vm_backtrace(vm, vm->fp); \
|
||||||
vm->exit_code = 1; return -1; \
|
vm->exit_code = 1; \
|
||||||
|
return -1; \
|
||||||
} \
|
} \
|
||||||
} while (0)
|
} while (0)
|
||||||
|
|
||||||
@@ -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_realloc(vm->stacks, (usize)vm->config.max_stack_depth);
|
||||||
vec_init(vm->frames);
|
vec_init(vm->frames);
|
||||||
vec_realloc(vm->frames, (usize)vm->config.max_call_depth);
|
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->sp = vm->fp = vm->ip = vm->cp = vm->gp = 0;
|
||||||
vm->prog = NULL;
|
vm->prog = NULL;
|
||||||
vm->trace = 0;
|
vm->trace = 0;
|
||||||
vm->debug = 1;
|
vm->debug = 1;
|
||||||
vm->debug_addr = 0;
|
vm->debug_addr = 0;
|
||||||
|
vm->skip_bp = 0;
|
||||||
vm->exit_code = 0;
|
vm->exit_code = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -569,6 +574,68 @@ void spl_vm_drop(spl_vm_t *vm) {
|
|||||||
return;
|
return;
|
||||||
vec_free(vm->stacks);
|
vec_free(vm->stacks);
|
||||||
vec_free(vm->frames);
|
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;
|
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);
|
ins = &vec_at(prog->insns, vm->ip);
|
||||||
vm->ip++;
|
vm->ip++;
|
||||||
|
|
||||||
@@ -870,14 +947,20 @@ int spl_vm_run_once(spl_vm_t *vm) {
|
|||||||
case SPL_CALL: {
|
case SPL_CALL: {
|
||||||
spl_val_t _nargs = ins->imm;
|
spl_val_t _nargs = ins->imm;
|
||||||
spl_val_t _addr = POP();
|
spl_val_t _addr = POP();
|
||||||
|
int _hit = fn_breakpoint_hit(vm, _addr);
|
||||||
spl_vm_call(vm, _addr, _nargs);
|
spl_vm_call(vm, _addr, _nargs);
|
||||||
|
if (_hit)
|
||||||
|
return 2; /* 已进入函数,暂停在函数入口 */
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case SPL_CALLI: {
|
case SPL_CALLI: {
|
||||||
spl_val_t _addr = POP();
|
spl_val_t _addr = POP();
|
||||||
spl_val_t _nargs = POP();
|
spl_val_t _nargs = POP();
|
||||||
|
int _hit = fn_breakpoint_hit(vm, _addr);
|
||||||
spl_vm_call(vm, _addr, _nargs);
|
spl_vm_call(vm, _addr, _nargs);
|
||||||
|
if (_hit)
|
||||||
|
return 2;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1053,7 +1136,7 @@ int spl_vm_run_once(spl_vm_t *vm) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
case SPL_BK: {
|
case SPL_BK: {
|
||||||
return 0; /* breakpoint: pause execution */
|
return 2; /* breakpoint: pause execution */
|
||||||
}
|
}
|
||||||
|
|
||||||
default:
|
default:
|
||||||
|
|||||||
@@ -31,6 +31,9 @@ typedef struct {
|
|||||||
int trace; /* non-zero to print each instruction */
|
int trace; /* non-zero to print each instruction */
|
||||||
int debug; /* non-zero to enable canary checks */
|
int debug; /* non-zero to enable canary checks */
|
||||||
int debug_addr; /* non-zero to check for low-address memory access */
|
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;
|
spl_prog_t *prog;
|
||||||
char error_msg[1024];
|
char error_msg[1024];
|
||||||
struct {
|
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_once(spl_vm_t *vm);
|
||||||
int spl_vm_run_until(spl_vm_t *vm, size_t step);
|
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_dump_instr(spl_vm_t *vm, spl_val_t ip);
|
||||||
void spl_vm_stackdump(spl_vm_t *vm, spl_val_t sp);
|
void spl_vm_stackdump(spl_vm_t *vm, spl_val_t sp);
|
||||||
int spl_vm_backtrace(spl_vm_t *vm, spl_val_t fp);
|
int spl_vm_backtrace(spl_vm_t *vm, spl_val_t fp);
|
||||||
|
|||||||
Reference in New Issue
Block a user