Files
HomeAgent/scripts/kernel-stress/README.md
JianFeeeee 9f80289a89 test(kernel-stress): 把"真二进制压力测试"harness 固化进仓库(私有 netns + mock LLM + 多连接驱动)
本轮压测是从 /var/tmp 里现写脚本跑的,能复现才能长期用 —— 固化到这。

为什么要有它(与 go test 的分工):单测都在进程内、参数显式给,抓不到集成面问题;
这一轮它抓到了三个只有真二进制才暴露的问题(DataDir 漏接线、插件通道未登记为
inputch、create 后子不开工),以及两处生成器缺陷(旧 SDK 导致生成工程编译失败;
模板工程从不示范通道登记)。

- `launch.sh`:在**私有 netns** 里同时起 mock LLM 与内核实例。
  必须隔离的原因:生产实例占着 *:8080/*:9890/*:9876 且插件设了 SO_REUSEADDR,
  同机再起一个实例会在 127.0.0.1 上与之并存绑定(首次实测抢到 9890 约 1 分钟);
  netns 里只有 lo,结构上不可能碰到生产端口。unix socket 是文件系统对象,
  所以驱动脚本在 netns 外照样能连。
- `mockllm.py`:OpenAI 兼容 mock(可控延迟 + SSE 分块 + 按 `!resident`/`!notify`
  标记回工具调用),让无外网也能跑、且**流式段可被中断**(压抢占路径的前提)。
  注意必须实现 `do_HEAD`:内核探活用 HEAD,501 会被判不可达 → degraded → rollback 循环。
- `stress.py`:多并发连接轰炸(单连接是串行的,造不出队列压力)+ 中断线程 + 峰值采样。
- `kcli.py`:CLI socket 客户端(`/auth` → `/kernel` / 广播)。
- README:前置配置(含探活端点、rollback 关掉)、用法、指标读法、已知坑。

实测结果(详见 6e60351 提交信息):密集 274 任务 executed=274/rejected=0;
稀疏中断下 suspended=resumed=preempted=27;驻留子全链路 + 优雅退出不留孤儿。
2026-09-13 11:38:31 +08:00

64 lines
3.5 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 内核二进制压力测试kernel-stress
对本仓库**编译出来的真实内核**做压力测试 —— 与 `go test` 的区别是:它跑真二进制、
真插件加载、真 unix socket 协议,因此能抓到只在集成面上出现的问题
(已有战绩:根 agent `DataDir` 漏接线、插件通道没登记为 inputch、create 后子不开工)。
## 为什么必须放在私有 netns 里
生产实例占着 `*:8080 / *:9890 / *:9876`,而插件的监听都设了 `SO_REUSEADDR`
同机再起一个实例会**在 127.0.0.1 上与之并存绑定**(实测抢到过 `127.0.0.1:9890` 约 1 分钟)。
`unshare -n` 后实例只有 `lo`,结构上不可能碰到生产端口。
unix socket 是文件系统对象,跨 netns 仍可驱动,所以驱动脚本在 netns 外也能用。
## 前置
```bash
go build -o /tmp/homed-stress ./cmd/homed # 被压的内核
export GOCACHE=/tmp/gocache GOPATH=/tmp/gopath TMPDIR=/var/tmp/gotmp
```
## 用法
```bash
# 1) 准备数据目录 + 把 LLM 指向本地 mock无外网也能跑且快、可控
DATA=/var/tmp/kstress
mkdir -p $DATA
# 先跑一次实例建出 config.db再写入下面这些键也可直接复用现成目录
# core.llm.provider=mock
# core.llm.sources.mock.base_url=http://127.0.0.1:9099/v1
# core.llm.sources.mock.model=mock api_key=mock adapter=openai
# core.llm.sources.mock.adapter_path=adapters/openai.lua priority=100
# core.defaults.llm_endpoints=http://127.0.0.1:9099/v1/models # 探活端点(探活用 HEAD
# core.defaults.rollback.max_retries=100000 auto_rollback=false
# 并把 core.llm.sources.deepseek* 删掉netns 里它不可达,会让 agent 被判 degraded → rollback 循环)
# 2) 起 mock LLM + 内核(都在同一个私有 netns 里)
MOCK_DELAY_MS=300 MOCK_CHUNKS=8 ./launch.sh /tmp/homed-stress
# 3) 取认证密钥cli 插件回落到 webui.api_key
export KCLI_KEY=$(sqlite3 $DATA/config.db "select value from config_webui where key='api_key';")
# 4) 压
./kcli.py $DATA/cli.sock stats full # 看内核状态(含 scheduler 计数)
./stress.py $DATA/cli.sock "$KCLI_KEY" 16 12 4 20 dense 0.05 # 16 连接×12 输入 + 4 线程×20 中断
./stress.py $DATA/cli.sock "$KCLI_KEY" 12 12 1 25 0.0 3.0 # 稀疏中断 ⇒ 压抢占/挂起/恢复
./stress.py $DATA/cli.sock "$KCLI_KEY" 1 1 0 0 resident 0.0 # 驻留子全链路mock 见 !resident 标记)
```
## 读结果
| 指标 | 含义 |
|---|---|
| `executed` / `rejected` | 任务执行数 / 被拒数(压力下应为 0 |
| 峰值 `峰值_队列` / `峰值_待处理中断` | 采样到的最大排队深度 / 待处理中断数 |
| `suspended` / `resumed` / `preempted` | 抢占三件套。**中断要放稀**才会打在高优先级任务上密集中断会互相同级L4 vs L4不抢占数值会很低 |
| `max_suspend_depth` | 中断栈结构上界(= 中断级数 4 |
## 已知坑
- 探活用 **HEAD**`internal/network/monitor.go`mock 必须实现 `do_HEAD`,否则 501 → 判不可达 → agent degraded → rollback 循环(会 reset 合并目录)。
- CLI 协议**每条连接串行**处理(`handleChat` 阻塞到本次响应产出):单连接狂发只会排成一条线,造不出队列压力 —— 必须多连接。
- 认证:连接后先发 `/auth <key>`,否则一切命令返回 `unauthorized`
- `mockllm.py``!resident` / `!notify` 标记用来让 mock 回工具调用,从而在真内核里驱动 `resident_agents` / `notify_parent`