mirror of
https://gitcode.com/JianFeeeee/HomeAgent.git
synced 2026-09-21 17:38:10 +00:00
plugin: 17 插件全量重编 + 端到端冒烟验证(Part 6.3)
## 17 个插件源码零改动,全部重编为 plugin.bin 16 个 × 3 平台(linux/darwin/windows),qq 1 平台(plg.json 自己声明 bundle:false)。luademo 走 Lua 解释器不适用。 git status example/ 无输出 —— 这是「业务代码零改动」的硬证据。 批 3 那些预估高风险的插件(qq 2686 行双向通道、browser 12 工具 + InjectInterruptText、a2a/acp 的 InjectInputSync 同步注入)一次全过, 因为它们只碰公开 SDK 合同面,而合同面在 Part 2 已 51 个 method 全量平移。 唯一一次失败与迁移无关:rss 的 github.com/mmcdole/gofeed 不在本地模块 缓存且 proxy.golang.org 不通,换 GOPROXY=https://goproxy.cn 后通过。 ## 真实 homed 加载验证 15 个外部插件全部经 proc 通道建链(protocol=1 sdk=0.9.2,各自独立 PID), 31 个插件 loaded(15 外部 + 16 内置)。 ai_image / files 未走 proc 通道:同名内置插件优先(工厂编译期注册), 外部插件被遮蔽。这是既有行为,与迁移无关。 事件环与共享段均正常创建,且共享段是**一块** 256KB 服务全部 15 个插件。 ## 冒烟测试 4 项(internal/plugins/real_plugin_smoke_test.go) 用真实 example 产物而非 testdata 假插件;manifest 刻意写 "entry":"plugin.so" 验证工具链与内核都已不看 entry 值。未重编时 skip 而非 fail。 - ToolInvokeRoundTrip:工具真实调用往返(此前只验证到"注册")。 weather_current 返回结构化参数校验错误——这恰是链路通的证据。 - StageRewriteTakesEffect:sanitizer 清洗 ANSI 序列,改写经共享段回到 内核 StageContext - MultiPluginShareOneSegment:sanitizer + weather 并发,清洗结果不被覆盖 - CrashDoesNotKillKernel:SIGKILL 插件进程后 homed 存活、17 插件仍在 (对比 C ABI 下插件 panic 直接带崩 homed,§1.2 现网已发生) ## 开销实测与基线偏差 15 个插件进程 RSS=88.0MB PSS=87.9MB 线程=82,均摊 5.87MB / 5.5 线程。 RSS 88MB vs 实验 5 基线 29.1MB **不是回归,是基线不可比**:实验 5 用 2.68MB 最小插件,真实插件 3.1~14.8MB。可比的结构性指标: - 均摊线程 5.5 vs 4.9 —— 同量级,无线程膨胀 - PSS/RSS 99.9% vs 44% —— **明显差于基线** 第二项是真实发现:基线里 PSS 远低于 RSS 说明 Go runtime 只读代码页在 进程间共享;实测几乎不共享,因为 15 个插件是 15 个不同的二进制,没有 共同物理页可映射。这是「每插件独立二进制」的固有代价,意味着实际内存 开销高于 §4.3 的乐观估计。压这一项的方向是共享 launcher 二进制。 ## 工具脚本入 experiments/19-migration-verify scripts/ 被 .gitignore 排除,故放到已跟踪的 experiments 目录下, 与 01~18 的可复跑实验并列。 measure-plugin-overhead.sh 第一版有统计口径 bug:RSS 读 status 的 VmRSS、 PSS 读 smaps_rollup 的 Pss,输出 PSS(87.9MB) > RSS(69.1MB) —— 物理上不可能。 两者对共享内存段计入方式不同(smaps 的 Rss 含 Pss_Shmem)。已统一从 smaps_rollup 读。另修 bc 不可用导致 MB 全显示 0.0(改用 awk)。 Ref: docs/zh/plugin-migration-plan.md Part 6.3、docs/zh/架构迁移评估.md §4.3
This commit is contained in:
@ -0,0 +1,83 @@
|
||||
# 实验 19:迁移验证工具(Part 6.3)
|
||||
|
||||
外部插件从 C ABI 动态库迁移到子进程后的批量重编与开销实测工具。
|
||||
与 01~18 的性质不同:那些是**决策前**的可行性验证,这两个是**迁移执行期**
|
||||
反复使用的操作脚本。
|
||||
|
||||
## rebuild-plugins.sh
|
||||
|
||||
批量把 `example/` 下的插件重编为子进程模式(`plugin.bin`)。
|
||||
|
||||
```bash
|
||||
PLUGINDEV=/tmp/plugindev ./rebuild-plugins.sh weather sanitizer qq
|
||||
```
|
||||
|
||||
关键性质:**不修改任何插件源码**。`plg.json` 的 `entry` 仍写着 `"plugin.so"`
|
||||
也无妨——工具链已不看这个字段(Part 6.1)。
|
||||
|
||||
两个实现细节值得记:
|
||||
|
||||
- **成功判定看产物而非退出码**。plugindev 对部分错误只 `fmt.Printf` 不
|
||||
`os.Exit`,单看 `$?` 会把失败当成功。
|
||||
- 构建前清 `build/`+`dist/`。残留的 `.so` 不影响构建,但会让人误以为
|
||||
还在用旧通道。
|
||||
|
||||
已知环境依赖:`rss` 插件需要 `github.com/mmcdole/gofeed`,
|
||||
`proxy.golang.org` 不通时用 `GOPROXY=https://goproxy.cn,direct`。
|
||||
|
||||
## measure-plugin-overhead.sh
|
||||
|
||||
实测 homed + 插件子进程的常驻开销。
|
||||
|
||||
```bash
|
||||
./measure-plugin-overhead.sh $(pgrep -f 'homed -data' | head -1)
|
||||
```
|
||||
|
||||
### 一个统计口径的坑
|
||||
|
||||
第一版混用了两个来源:RSS 读 `/proc/pid/status` 的 `VmRSS`,
|
||||
PSS 读 `smaps_rollup` 的 `Pss`。结果输出 `PSS=87.9MB > RSS=69.1MB`——
|
||||
物理上不可能。
|
||||
|
||||
原因是两者对**共享内存段**的计入方式不同:`smaps_rollup` 的 `Rss` 含
|
||||
`Pss_Shmem`(共享段的按比例份额),`VmRSS` 不含。现已统一从
|
||||
`smaps_rollup` 读,保证 PSS ≤ RSS。
|
||||
|
||||
### 实测结果(2026-09-02,15 个真实插件)
|
||||
|
||||
```
|
||||
15 个插件进程 RSS=88.0 MB PSS=87.9 MB 线程=82
|
||||
均摊 5.87 MB 5.86 MB 5.5 线程
|
||||
homed 本体 RSS=182 MB 线程=15
|
||||
```
|
||||
|
||||
**与实验 5 基线(17 进程 RSS=29.1MB / PSS=12.9MB / 线程=84)的偏差解释**:
|
||||
|
||||
实验 5 用的是 2.68MB 的最小插件,真实插件 3.1~14.8MB(browser 依赖最多)。
|
||||
RSS 随二进制体积线性增长,故绝对数字不可比。可比的是结构性指标:
|
||||
|
||||
| 指标 | 基线 | 实测 | 判断 |
|
||||
|---|---|---|---|
|
||||
| 均摊线程 | 4.9 | 5.5 | 同量级,无线程膨胀 |
|
||||
| PSS/RSS | 44% | 99.9% | **明显差于基线** |
|
||||
|
||||
第二项是真实发现:基线里 PSS 远低于 RSS,说明 Go runtime 只读代码页在
|
||||
进程间共享。实测几乎不共享,因为 15 个插件是 15 个**不同**的二进制,
|
||||
没有共同的物理页可映射。
|
||||
|
||||
这是「每插件独立二进制」的固有代价,不是缺陷,但意味着实际内存开销
|
||||
高于评估文档(§4.3)的乐观估计。若日后需要压这一项,方向是让插件共享
|
||||
一个 launcher 二进制 + 各自的业务 plugin,而非各自静态链接整个 runtime。
|
||||
|
||||
## 冒烟测试
|
||||
|
||||
自动化部分在 `internal/plugins/real_plugin_smoke_test.go`(4 项):
|
||||
|
||||
- `ToolInvokeRoundTrip`:工具真实调用往返(不只是注册)
|
||||
- `StageRewriteTakesEffect`:sanitizer 改写型 stage 在真实内核装配下生效
|
||||
- `MultiPluginShareOneSegment`:多插件共享一段,只读插件不覆盖改写结果
|
||||
- `CrashDoesNotKillKernel`:SIGKILL 插件进程,homed 存活
|
||||
|
||||
这些测试用**真实 example 产物**而非 testdata 假插件,且 manifest 刻意写
|
||||
`"entry":"plugin.so"`——验证「业务代码零改动」这一承诺在完整内核装配下成立。
|
||||
未重编时 skip 而非 fail,CI 不强制先跑重编脚本。
|
||||
Reference in New Issue
Block a user