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

3.5 KiB
Raw Blame History

内核二进制压力测试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 外也能用。

前置

go build -o /tmp/homed-stress ./cmd/homed      # 被压的内核
export GOCACHE=/tmp/gocache GOPATH=/tmp/gopath TMPDIR=/var/tmp/gotmp

用法

# 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

已知坑

  • 探活用 HEADinternal/network/monitor.gomock 必须实现 do_HEAD,否则 501 → 判不可达 → agent degraded → rollback 循环(会 reset 合并目录)。
  • CLI 协议每条连接串行处理(handleChat 阻塞到本次响应产出):单连接狂发只会排成一条线,造不出队列压力 —— 必须多连接。
  • 认证:连接后先发 /auth <key>,否则一切命令返回 unauthorized
  • mockllm.py!resident / !notify 标记用来让 mock 回工具调用,从而在真内核里驱动 resident_agents / notify_parent