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

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