mirror of
https://gitcode.com/JianFeeeee/HomeAgent.git
synced 2026-09-21 09:28:14 +00:00
## 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
83 lines
3.0 KiB
Bash
Executable File
83 lines
3.0 KiB
Bash
Executable File
#!/usr/bin/env bash
|
||
# 子进程插件常驻开销实测(Part 6.3 验收项)。
|
||
#
|
||
# 对照基线:docs/zh/experiments/plugin-arch 实验 5 实测 17 子进程
|
||
# PSS=12.9MB / RSS=29.1MB / 线程=84(原文档估计 50-70MB 偏高)。
|
||
#
|
||
# 用法:./measure-plugin-overhead.sh <homed-pid>
|
||
set -uo pipefail
|
||
|
||
pid=${1:-}
|
||
if [ -z "$pid" ]; then
|
||
echo "用法: $0 <homed-pid>" >&2
|
||
exit 1
|
||
fi
|
||
if [ ! -d "/proc/$pid" ]; then
|
||
echo "进程 $pid 不存在" >&2
|
||
exit 1
|
||
fi
|
||
|
||
# homed 本体
|
||
homed_rss=$(awk '/^VmRSS:/ {print $2}' "/proc/$pid/status")
|
||
homed_thr=$(awk '/^Threads:/ {print $2}' "/proc/$pid/status")
|
||
|
||
echo "=== homed 本体 ==="
|
||
printf "RSS=%s kB 线程=%s\n" "$homed_rss" "$homed_thr"
|
||
|
||
# 插件子进程:homed 的直接子进程中执行 plugin.bin 的
|
||
echo
|
||
echo "=== 插件子进程 ==="
|
||
total_rss=0
|
||
total_pss=0
|
||
total_thr=0
|
||
count=0
|
||
|
||
for child in $(pgrep -P "$pid" 2>/dev/null); do
|
||
exe=$(readlink "/proc/$child/exe" 2>/dev/null || true)
|
||
case "$exe" in
|
||
*plugin.bin*) ;;
|
||
*) continue ;;
|
||
esac
|
||
|
||
thr=$(awk '/^Threads:/ {print $2}' "/proc/$child/status" 2>/dev/null || echo 0)
|
||
# RSS 与 PSS 统一从 smaps_rollup 读,保证口径一致。
|
||
# 混用 status 的 VmRSS 与 smaps 的 Pss 会得出 PSS > RSS 的荒谬结果——
|
||
# 两者对共享内存段(Pss_Shmem)的计入方式不同。
|
||
rss=$(awk '/^Rss:/ {print $2}' "/proc/$child/smaps_rollup" 2>/dev/null || echo 0)
|
||
pss=$(awk '/^Pss:/ {print $2}' "/proc/$child/smaps_rollup" 2>/dev/null || echo 0)
|
||
if [ -z "$rss" ] || [ "$rss" = "0" ]; then
|
||
rss=$(awk '/^VmRSS:/ {print $2}' "/proc/$child/status" 2>/dev/null || echo 0)
|
||
fi
|
||
binsz=$(stat -c%s "$(readlink "/proc/$child/exe" 2>/dev/null)" 2>/dev/null || echo 0)
|
||
name=$(basename "$(readlink "/proc/$child/cwd" 2>/dev/null || echo unknown)")
|
||
|
||
printf " %-16s pid=%-8s RSS=%-8s PSS=%-8s 线程=%-3s 二进制=%s MB\n" \
|
||
"$name" "$child" "$rss" "$pss" "$thr" \
|
||
"$(awk -v b="$binsz" 'BEGIN{printf "%.1f", b/1048576}')"
|
||
total_rss=$((total_rss + rss))
|
||
total_pss=$((total_pss + pss))
|
||
total_thr=$((total_thr + thr))
|
||
count=$((count + 1))
|
||
done
|
||
|
||
echo
|
||
echo "=== 合计($count 个插件进程)==="
|
||
awk -v rss="$total_rss" -v pss="$total_pss" -v thr="$total_thr" -v n="$count" '
|
||
BEGIN {
|
||
printf "RSS=%d kB (%.1f MB)\n", rss, rss/1024
|
||
printf "PSS=%d kB (%.1f MB)\n", pss, pss/1024
|
||
printf "线程=%d\n", thr
|
||
if (n > 0) printf "均摊 RSS=%.2f MB PSS=%.2f MB 线程=%.1f\n", rss/1024/n, pss/1024/n, thr/n
|
||
}'
|
||
|
||
echo
|
||
echo "注:RSS/PSS 均取自 smaps_rollup,口径一致(PSS ≤ RSS)。"
|
||
echo "PSS 低于 RSS 的部分即 Go runtime 只读代码页在进程间的共享收益。"
|
||
|
||
echo
|
||
echo "对照实验 5 基线:17 进程 RSS=29.1MB PSS=12.9MB 线程=84"
|
||
echo
|
||
echo "⚠️ 该基线用的是 2.68MB 的最小插件;真实插件 3.3~15.2MB(browser 依赖最多)。"
|
||
echo " RSS 随二进制体积线性增长,故不可直接与基线数字比较——"
|
||
echo " 要比的是「均摊线程数」与「PSS/RSS 比值(共享收益)」这两个结构性指标。"
|