687 lines
21 KiB
C
687 lines
21 KiB
C
/* spl_cli.c — SIR VM launcher + gdb-style interactive debugger
|
||
*
|
||
* 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
|
||
*
|
||
* 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 <ctype.h>
|
||
#include <stdio.h>
|
||
#include <stdlib.h>
|
||
#include <string.h>
|
||
|
||
/* ================================================================
|
||
* 命令行解析
|
||
* ================================================================ */
|
||
|
||
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;
|
||
}
|
||
|
||
/* ================================================================
|
||
* 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;
|
||
}
|
||
|
||
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(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 (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, "spl_cli: failed to load prog\n");
|
||
spl_vm_drop(&vm);
|
||
spl_prog_drop(&prog);
|
||
return 1;
|
||
}
|
||
|
||
/* 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)
|
||
return 1;
|
||
return (int)vm.exit_code;
|
||
}
|