stage2 提供近乎完整的功能但出现严重性能问题 添加stage0的原生性能记录 stage1添加更多内置函数 nob重构代码使其更加健壮
This commit is contained in:
1 parent
b5a7a811b3
commit
e02b0c633a
35 files changed
+14018
-216
No files matched your search
@@ -30,6 +30,9 @@ typedef struct {
|
||||
int debug_cli;
|
||||
int trace;
|
||||
int disasm;
|
||||
int profile;
|
||||
int profile_interval;
|
||||
const char *profile_out;
|
||||
const char *entry;
|
||||
const char *path; /* file.sir */
|
||||
const char **args; /* spl 程序参数(file.sir 之后) */
|
||||
@@ -43,6 +46,8 @@ static void usage(void) {
|
||||
" --disasm disasm dump sir\n"
|
||||
" --entry <name> entry function (default: main)\n"
|
||||
" --trace print every instruction\n"
|
||||
" --profile[=N] native sampling profiler (every N insns, default 10000)\n"
|
||||
" --profile-out <p> profile output prefix (default build/profile)\n"
|
||||
" -h, --help show help\n");
|
||||
}
|
||||
|
||||
@@ -67,6 +72,17 @@ static int parse_args(int argc, const char **argv, cli_opts_t *o) {
|
||||
o->trace = 1;
|
||||
} else if (strcmp(a, "--disasm") == 0) {
|
||||
o->disasm = 1;
|
||||
} else if (strcmp(a, "--profile") == 0) {
|
||||
o->profile = 1;
|
||||
} else if (strncmp(a, "--profile=", 10) == 0) {
|
||||
o->profile = 1;
|
||||
o->profile_interval = (int)strtol(a + 10, NULL, 10);
|
||||
} else if (strcmp(a, "--profile-out") == 0) {
|
||||
if (i + 1 >= argc) {
|
||||
fprintf(stderr, "spl_cli: --profile-out needs a prefix\n");
|
||||
return -1;
|
||||
}
|
||||
o->profile_out = argv[++i];
|
||||
} else if (strcmp(a, "--entry") == 0) {
|
||||
if (i + 1 >= argc) {
|
||||
fprintf(stderr, "spl_cli: --entry needs a name\n");
|
||||
@@ -651,6 +667,11 @@ int main(int argc, const char **argv) {
|
||||
spl_prog_drop(&prog);
|
||||
return 1;
|
||||
}
|
||||
if (o.profile) {
|
||||
usize interval = o.profile_interval > 0 ? (usize)o.profile_interval : 10000;
|
||||
spl_vm_profile_enable(&vm, interval);
|
||||
fprintf(stderr, "spl_cli: profile mode on (interval=%zu)\n", interval);
|
||||
}
|
||||
|
||||
/* spl 程序 argv:argv[0]=file.sir(程序名),argv[1..]=args */
|
||||
const char **spl_argv = (const char **)malloc(sizeof(char *) * (size_t)(o.nargs + 1));
|
||||
@@ -689,6 +710,17 @@ int main(int argc, const char **argv) {
|
||||
fprintf(stderr, "spl_cli: hit breakpoint (run with -g to debug)\n");
|
||||
}
|
||||
|
||||
if (o.profile) {
|
||||
const char *prefix = o.profile_out ? o.profile_out : "build/profile";
|
||||
char folded[1024];
|
||||
char table[1024];
|
||||
snprintf(folded, sizeof folded, "%s.folded", prefix);
|
||||
snprintf(table, sizeof table, "%s.table", prefix);
|
||||
spl_vm_profile_dump_folded(&vm, folded);
|
||||
spl_vm_profile_dump_table(&vm, table);
|
||||
fprintf(stderr, "spl_cli: profile written to %s / %s\n", folded, table);
|
||||
}
|
||||
|
||||
free(spl_argv);
|
||||
spl_vm_drop(&vm);
|
||||
spl_prog_drop(&prog);
|
||||
|
||||
@@ -283,6 +283,8 @@ int spl_prog_load_from_file(const char *fname, spl_prog_t *prog) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int spl_prog_load_from_buffer(const char *buffer, usize len, spl_prog_t *prog) {}
|
||||
|
||||
int spl_prog_store_to_file(const char *fname, spl_prog_t *prog) {
|
||||
unsigned char *buf, *p;
|
||||
spl_vm_val_t i;
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
|
||||
/* =================================================================
|
||||
* Macro templates
|
||||
@@ -322,6 +323,41 @@ static spl_vm_val_t vm_printf(spl_vm_t *_vm, int nargs, spl_vm_val_t *args) {
|
||||
return (spl_vm_val_t)nargs;
|
||||
}
|
||||
|
||||
/* 探测接口(syscall 风格,非必选,逐步扩展)。
|
||||
* 不假设一定可用:不支持时统一返回 0。
|
||||
* case 0 -> 是否存在 time:1 = 存在(则 case 1/2 有效),0 = 不存在
|
||||
* case 1 -> 当前秒(time_t)
|
||||
* case 2 -> 当前纳秒部分(tv_nsec)
|
||||
* case 3 -> VM 已执行指令数(参考量;解码/执行恒定时可换算时间)
|
||||
* case 4 -> 完整纳秒时间戳(单次调用,避免 sec/ns 跨秒竞态;
|
||||
* 纪元纳秒约 1.8e18 < u64 上限,usize 足够,无需 i128)
|
||||
* 新增探测点先加 case,再在 SPL 侧逐步验证。 */
|
||||
static spl_vm_val_t vm_timestamp(spl_vm_t *_vm, int nargs, spl_vm_val_t *args) {
|
||||
CHECK_NARGS("vm_timestamp", 1);
|
||||
spl_vm_val_t arg = args[0];
|
||||
struct timespec ts;
|
||||
int has_time = (timespec_get(&ts, TIME_UTC) == TIME_UTC);
|
||||
switch (arg) {
|
||||
case 0:
|
||||
return has_time ? 1 : 0;
|
||||
case 1:
|
||||
return has_time ? (spl_vm_val_t)ts.tv_sec : 0;
|
||||
case 2:
|
||||
return has_time ? (spl_vm_val_t)ts.tv_nsec : 0;
|
||||
case 3:
|
||||
if (!_vm)
|
||||
return 0;
|
||||
return (spl_vm_val_t)_vm->steps;
|
||||
case 4:
|
||||
return has_time ? (spl_vm_val_t)((unsigned long long)ts.tv_sec * 1000000000ull +
|
||||
(unsigned long long)ts.tv_nsec)
|
||||
: 0;
|
||||
default:
|
||||
fprintf(stderr, "vm_timestamp: invalid arg %zu\n", (size_t)arg);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* =================================================================
|
||||
* VM syscalls (for comptime - compile-time code execution)
|
||||
*
|
||||
@@ -480,6 +516,8 @@ void spl_syscall_register(spl_prog_t *prog) {
|
||||
{"vm_memcpy", 0, vm_memcpy},
|
||||
{"vm_memcmp", 0, vm_memcmp},
|
||||
{"vm_printf", 0, vm_printf},
|
||||
|
||||
{"vm_timestamp", 0, vm_timestamp},
|
||||
/* VM operations (comptime) */
|
||||
{"vm_new", 0, vm_new},
|
||||
{"vm_drop", 0, vm_drop},
|
||||
|
||||
+141
@@ -5,12 +5,15 @@
|
||||
*/
|
||||
|
||||
#include "spl_vm.h"
|
||||
#include "include/core_map.h"
|
||||
#include "spl_mcode.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
static void spl_vm_profile_free(spl_vm_t *vm);
|
||||
|
||||
#if defined(_WIN32) || defined(_WIN64)
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#include <windows.h>
|
||||
@@ -566,6 +569,9 @@ void spl_vm_init_ex(spl_vm_t *vm, int stack_size, int call_depth) {
|
||||
vec_init(vm->fn_breakpoints);
|
||||
|
||||
vm->sp = vm->fp = vm->ip = vm->cp = vm->gp = 0;
|
||||
vm->steps = 0;
|
||||
vm->profile = NULL;
|
||||
vm->profile_interval = 0;
|
||||
vm->prog = NULL;
|
||||
vm->trace = 0;
|
||||
vm->debug = 1;
|
||||
@@ -585,6 +591,7 @@ void spl_vm_drop(spl_vm_t *vm) {
|
||||
free(vm->fn_breakpoints.data[i]);
|
||||
vec_free(vm->fn_breakpoints);
|
||||
vec_free(vm->breakpoints);
|
||||
spl_vm_profile_free(vm);
|
||||
}
|
||||
|
||||
// FIXME
|
||||
@@ -643,6 +650,7 @@ void spl_vm_skip_breakpoint(spl_vm_t *vm) {
|
||||
|
||||
/* 前向声明(定义在文件尾部 dump 区域) */
|
||||
static const char *func_name_by_ip(spl_prog_t *prog, spl_vm_val_t ip);
|
||||
static void spl_vm_profile_free(spl_vm_t *vm);
|
||||
|
||||
/* 函数名断点命中:addr 所在函数名是否在断点表 */
|
||||
static int fn_breakpoint_hit(spl_vm_t *vm, spl_vm_val_t addr) {
|
||||
@@ -766,6 +774,136 @@ int spl_vm_prepare(spl_vm_t *vm, const char *entry, int argc, const char **argv,
|
||||
* Returns: 0 = still running, 1 = halted, 2 = breakpoint (SPL_DBG), -1 = error
|
||||
* ================================================================ */
|
||||
|
||||
/* ================= native 采样 profiler =================
|
||||
* 每隔 interval 条指令采样一次当前调用栈(root;...;leaf),累积计数。
|
||||
* 输出折叠格式(folded)供火焰图工具使用,以及每函数 self 采样表。 */
|
||||
|
||||
static int func_index_by_ip(spl_prog_t *prog, spl_vm_val_t ip) {
|
||||
if (!prog)
|
||||
return -1;
|
||||
isize lo = 0, hi = (isize)vec_size(prog->funcs) - 1;
|
||||
while (lo <= hi) {
|
||||
isize mid = (lo + hi) / 2;
|
||||
spl_vm_func_t *f = &vec_at(prog->funcs, (usize)mid);
|
||||
if (ip < f->address)
|
||||
hi = mid - 1;
|
||||
else if (ip >= f->address + f->ninsns)
|
||||
lo = mid + 1;
|
||||
else
|
||||
return (int)mid;
|
||||
}
|
||||
for (usize i = 0; i < vec_size(prog->funcs); i++) {
|
||||
spl_vm_func_t *f = &vec_at(prog->funcs, i);
|
||||
if (ip >= f->address && ip < f->address + f->ninsns)
|
||||
return (int)i;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void spl_vm_sample(spl_vm_t *vm) {
|
||||
spl_prof_t *p = (spl_prof_t *)vm->profile;
|
||||
spl_prog_t *prog = vm->prog;
|
||||
if (!p || !prog)
|
||||
return;
|
||||
|
||||
char buf[8192];
|
||||
usize len = 0;
|
||||
for (isize i = 0; i < vm->cp; i++) {
|
||||
intptr_t s = (intptr_t)vm->frames.data[i].saved_ip;
|
||||
if (s <= 0)
|
||||
continue;
|
||||
int fi = func_index_by_ip(prog, (spl_vm_val_t)(s - 1));
|
||||
const char *n = fi >= 0 ? vec_at(prog->funcs, (usize)fi).name : "?";
|
||||
size_t l = strlen(n);
|
||||
if (len + l + 2 < sizeof(buf)) {
|
||||
if (len)
|
||||
buf[len++] = ';';
|
||||
memcpy(buf + len, n, l);
|
||||
len += l;
|
||||
}
|
||||
}
|
||||
int leaf = func_index_by_ip(prog, vm->ip > 0 ? vm->ip - 1 : 0);
|
||||
const char *ln = leaf >= 0 ? vec_at(prog->funcs, (usize)leaf).name : "?";
|
||||
size_t ll = strlen(ln);
|
||||
if (len + ll + 1 < sizeof(buf)) {
|
||||
if (len)
|
||||
buf[len++] = ';';
|
||||
memcpy(buf + len, ln, ll);
|
||||
len += ll;
|
||||
}
|
||||
buf[len] = 0;
|
||||
|
||||
usize old = 0;
|
||||
if (map_get(p->stacks, buf, &old)) {
|
||||
map_put(p->stacks, buf, old + 1);
|
||||
} else {
|
||||
map_put(p->stacks, strdup(buf), (usize)1);
|
||||
}
|
||||
if (leaf >= 0 && (usize)leaf < vec_size(p->self_samples))
|
||||
p->self_samples.data[leaf]++;
|
||||
p->samples++;
|
||||
}
|
||||
|
||||
void spl_vm_profile_enable(spl_vm_t *vm, usize interval) {
|
||||
if (!vm)
|
||||
return;
|
||||
spl_prof_t *p = (spl_prof_t *)malloc(sizeof(*p));
|
||||
if (!p)
|
||||
return;
|
||||
memset(p, 0, sizeof(*p));
|
||||
map_init(p->stacks, MAP_HASH_STR, MAP_CMP_STR);
|
||||
vec_init(p->self_samples);
|
||||
if (vm->prog) {
|
||||
usize n = vec_size(vm->prog->funcs);
|
||||
for (usize i = 0; i < n; i++)
|
||||
vec_push(p->self_samples, (usize)0);
|
||||
}
|
||||
vm->profile = p;
|
||||
vm->profile_interval = interval ? interval : 100000;
|
||||
}
|
||||
|
||||
static void spl_vm_profile_free(spl_vm_t *vm) {
|
||||
spl_prof_t *p = (spl_prof_t *)vm->profile;
|
||||
if (!p)
|
||||
return;
|
||||
map_for(p->stacks, i) { free((void *)p->stacks.data[i].key); }
|
||||
map_free(p->stacks);
|
||||
vec_free(p->self_samples);
|
||||
free(p);
|
||||
vm->profile = NULL;
|
||||
}
|
||||
|
||||
void spl_vm_profile_dump_folded(spl_vm_t *vm, const char *path) {
|
||||
spl_prof_t *p = vm ? (spl_prof_t *)vm->profile : NULL;
|
||||
if (!p || !path)
|
||||
return;
|
||||
FILE *f = fopen(path, "wb");
|
||||
if (!f)
|
||||
return;
|
||||
map_for(p->stacks, i) { fprintf(f, "%s %zu\n", p->stacks.data[i].key, p->stacks.data[i].val); }
|
||||
fclose(f);
|
||||
}
|
||||
|
||||
void spl_vm_profile_dump_table(spl_vm_t *vm, const char *path) {
|
||||
spl_prof_t *p = vm ? (spl_prof_t *)vm->profile : NULL;
|
||||
if (!p || !path || !vm->prog)
|
||||
return;
|
||||
FILE *f = fopen(path, "wb");
|
||||
if (!f)
|
||||
return;
|
||||
fprintf(f, "samples=%zu interval=%zu\n", p->samples, vm->profile_interval);
|
||||
fprintf(f, "func\tself_samples\tpercent\n");
|
||||
for (usize i = 0; i < vec_size(vm->prog->funcs); i++) {
|
||||
usize c = i < vec_size(p->self_samples) ? p->self_samples.data[i] : 0;
|
||||
if (c == 0)
|
||||
continue;
|
||||
spl_vm_func_t *fn = &vec_at(vm->prog->funcs, i);
|
||||
fprintf(f, "%s\t%zu\t%.2f\n", fn->name, c,
|
||||
p->samples ? (100.0 * (double)c / (double)p->samples) : 0.0);
|
||||
}
|
||||
fclose(f);
|
||||
}
|
||||
|
||||
int spl_vm_run_once(spl_vm_t *vm) {
|
||||
const spl_vm_ins_t *ins;
|
||||
spl_prog_t *prog;
|
||||
@@ -792,6 +930,9 @@ int spl_vm_run_once(spl_vm_t *vm) {
|
||||
|
||||
ins = &vec_at(prog->insns, vm->ip);
|
||||
vm->ip++;
|
||||
vm->steps++;
|
||||
if (vm->profile && vm->profile_interval && (vm->steps % vm->profile_interval) == 0)
|
||||
spl_vm_sample(vm);
|
||||
|
||||
if (vm->trace) {
|
||||
fprintf(stderr, "vm: ip=%zd op=%s type=%s imm=%zu sp=%zd fp=%zd\n", vm->ip - 1,
|
||||
|
||||
+22
-5
@@ -3,6 +3,7 @@
|
||||
#ifndef __SPL_VM_H__
|
||||
#define __SPL_VM_H__
|
||||
|
||||
#include "include/core_map.h"
|
||||
#include "include/core_vec.h"
|
||||
#include "spl_mcode.h"
|
||||
#include <stdint.h>
|
||||
@@ -19,19 +20,28 @@ typedef struct {
|
||||
typedef VEC(spl_vm_val_t) spl_stack_vec_t;
|
||||
typedef VEC(spl_callframe_t) spl_frame_vec_t;
|
||||
|
||||
typedef struct {
|
||||
MAP(const char *, usize) stacks;
|
||||
VEC(usize) self_samples;
|
||||
usize samples;
|
||||
} spl_prof_t;
|
||||
|
||||
typedef struct spl_vm {
|
||||
spl_stack_vec_t stacks;
|
||||
spl_frame_vec_t frames;
|
||||
uintptr_t gp; // global pointer
|
||||
uintptr_t sp; // stack pointer
|
||||
uintptr_t fp; // frame pointer
|
||||
uintptr_t cp; // call pointer
|
||||
uintptr_t ip; // instr pointer
|
||||
uintptr_t gp; // global pointer
|
||||
uintptr_t sp; // stack pointer
|
||||
uintptr_t fp; // frame pointer
|
||||
uintptr_t cp; // call pointer
|
||||
uintptr_t ip; // instr pointer
|
||||
uint64_t steps; // 已执行指令数(供性能分析参考)
|
||||
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 skip_bp; /* non-zero: run_once 忽略下一次断点(continue 越过当前断点指令) */
|
||||
spl_prof_t *profile; /* native 采样 profiler 状态(spl_prof_t*),NULL = 关闭 */
|
||||
usize profile_interval; /* 采样间隔(指令数) */
|
||||
VEC(usize) breakpoints; /* ip 断点(执行到该指令前暂停,run_once 返回 2) */
|
||||
VEC(char *) fn_breakpoints; /* 函数名断点(CALL/CALLI 目标函数入口暂停) */
|
||||
spl_prog_t *prog;
|
||||
@@ -73,6 +83,13 @@ void spl_vm_clear_breakpoints(spl_vm_t *vm);
|
||||
/* 让 run_once 执行当前指令(忽略一次断点命中);continue 越过当前断点用 */
|
||||
void spl_vm_skip_breakpoint(spl_vm_t *vm);
|
||||
|
||||
/* ---- native 采样 profiler ----
|
||||
* 每隔 interval 条指令记录一次当前调用栈(折叠格式),用于生成火焰图。
|
||||
* 必须在 prog 加载后调用 enable;dump 输出折叠栈/函数表,再 drop 时释放。 */
|
||||
void spl_vm_profile_enable(spl_vm_t *vm, usize interval);
|
||||
void spl_vm_profile_dump_folded(spl_vm_t *vm, const char *path);
|
||||
void spl_vm_profile_dump_table(spl_vm_t *vm, const char *path);
|
||||
|
||||
void spl_vm_dump_instr(spl_vm_t *vm, spl_vm_val_t ip);
|
||||
void spl_vm_stackdump(spl_vm_t *vm, spl_vm_val_t sp);
|
||||
int spl_vm_backtrace(spl_vm_t *vm, spl_vm_val_t fp);
|
||||
|
||||
Reference in new issue
Block a user