Skip to content

fix(desktop): 状态栏内存详情按进程归属统计并重新排版 - #1926

Merged
xiaoyumuxi merged 2 commits into
previewfrom
fix/memory-popover
Nov 14, 2026
Merged

xiaoyumuxi merged 2 commits into
previewfrom
fix/memory-popover

Conversation

@xiaoyumuxi

@xiaoyumuxi xiaoyumuxi commented Nov 14, 2026 •

Copy link
Copy Markdown
Collaborator

关联 Issue / Linked issue

PR 类型 / Type

  • feat:新功能或用户可见的改进
  • fix:缺陷修复
  • docs:文档、Agent Note、Skill 等规则文件
  • release:稳定版发布准备

改动说明 / What changes

背景 / Why

状态栏内存详情的统计和显示都不对。截图里是“总计 302 / Lithe 302 / 语言服务器 0 / 插件服务 0”,下面写着“没有运行中的语言服务器或插件服务。”,右边的数值被截断:

  • 统计:旧实现只读主进程自己的 RSS,再加上宿主登记了进程号的语言服务器和插件后端。主进程启动的终端、Git、构建、运行等子进程,插件后端自己启动的进程,以及 macOS 上替 Lithe 渲染网页的 WebKit 辅助进程(它们是 launchd 的子进程,不在 Lithe 的进程树里)都不计入。内存口径是 RSS,和活动监视器的“内存”列不同。
  • 显示:内容的 MinWidth(300) 比弹窗默认宽度 288 还宽,数值被截断;0 MB 的类别照样列出,只有主进程时提示文字也不准确。

这个 PR 改为读取原生进程表,由 Core 判定每个进程属于哪一部分并汇总,弹窗按类别重新排版。

改动 / Changes

  • 原生进程表(desktop/internal/sysinfo/process*.go,新增 sysinfo.Processes()):
    • macOS:proc_listallpids 加 proc_pid_rusage 的 ri_phys_footprint(与活动监视器的“内存”列同一口径),负责进程通过 responsibility_get_pid_responsible_for_pid(dlsym 取得,取不到时为 0)。
    • Windows:Toolhelp32 快照加 PROCESS_MEMORY_COUNTERS_EX2 的专用工作集(与任务管理器的“内存”列同一口径)。
    • Linux:/proc/<pid>/statm 的常驻减共享页。
    • 三端都不再启动 ps 或 PowerShell。sysinfo.Resident() 和各平台的 residentBytes 已删除。
  • Core 汇总(desktop/internal/core/process_memory.go):Client.SummarizeProcessMemory 调用 processMemory.summarize,传入进程表和宿主登记的角色(语言服务器、插件服务及其名称)。归属规则在 Core 契约里:主进程、语言服务器、插件服务、子进程(按主进程的直接子进程分组)、系统辅助进程(macOS 负责进程是 Lithe 的进程,按最上层分组);进程号复用、父链成环都有处理。process_memory_test.go 回放 Core 的共享 fixture memory-summary-v1.json。
  • 采样(desktop/internal/workbench/memory_monitor.go):状态栏项绘制时检查,距上次读取超过 10 秒才读;详情打开时用 RequestFrameIn 每 2 秒刷新一次,关闭后不安排定时帧,没有空闲轮询。采样从 beginPaint 移到状态栏项自己的绘制里。读取失败时隐藏数值,不显示过时的数字。插件服务和语言服务器用插件名和语言命名(如“Quill Tools · Quill”)。
  • 弹窗重排:
    • 宽度 340,数值不再截断。
    • 顶部是总量和 ⓘ 说明(口径是物理占用/专用工作集,系统辅助进程的含义)。
    • 按类别堆叠的条,图例中多个进程时显示进程数,0 MB 的类别不显示。
    • 占用最多的进程列表(可滚动),其余项给出合计。
    • 只有主进程时提示“Lithe 目前没有启动其他进程。”。
    • 颜色:主进程用强调色,语言服务器绿色,插件服务橙色,系统辅助进程紫色(不用红色,以免像错误),其他子进程灰色。
  • 删除无用代码:只为旧状态栏服务的 pluginservice.SampleProcessMemory、processTable、treeMemory 和 i18n 词条 Status.MemoryValue。SampleProcessTree 仍被运行窗口曲线使用,迁移见 [Feature] 运行窗口 CPU/内存曲线改用原生进程表与 Core 汇总,删除 ps/PowerShell 采样 #1925。
  • i18n:新增内存详情的中英词条(类别、说明、进程数、其余项、空状态),已重新生成。
  • 功能矩阵:memory-monitor.json 更新证据路径和验证动作(对照活动监视器/任务管理器;从 Finder 或 Dock 启动,见下方限制)。
  • 启动时背景图偶发不显示(tasks.go、root.go):New 里的 restoreBackground 在窗口绑定唤醒之前就开始解码。解码先完成时,结果在 Viva 丢弃的那次唤醒里排队,要等别的唤醒才应用。现在首帧发现已有排队结果时主动请求一次唤醒。这也是 TestMissingBackgroundCopyShowsNoImageQuietly 在完整测试里约 1/6 概率失败的原因(preview 上同样会失败)。
  • Core 固定版本:PINNED_CORE_REVISION 升到 Lithe-core-rust#43 的 5e2a580(在 main f88bc49 之上)。

截图(无头渲染,测试注入虚构的语言服务器和系统辅助进程):

场景 深色中文 浅色英文
语言服务器、系统辅助进程和子进程
只有主进程

不在本 PR 范围内 / Out of scope


类型专属:fix

复现与根因

  • 复现步骤、实际结果、期望结果:打开一个项目,点状态栏的内存项。实际:总计等于 Lithe 主进程,语言服务器和插件服务为 0,写着“没有运行中的语言服务器或插件服务。”,数值被截断。期望:总计包含 Lithe 启动的所有进程和系统替它运行的辅助进程,按类别列出,数值完整显示。
  • 根因:Epic(0.7.0): 基于 Viva 框架的跨平台方案的迁移(#1794) #1799(0faecba49)的 memory_monitor.go 只读主进程 RSS 和登记了进程号的会话(见“背景”);详情内容 MinWidth(300) 超出弹窗默认宽度 288。背景图问题的根因是 restoreBackground 在窗口绑定 Invalidator 之前投递结果(Viva 文档写明此时的 Invalidate 不生效)。
  • 影响平台:macOS、Windows(统计和显示);背景图问题两端都有。

检查项

  • 已先写回归测试,确认它在修复前失败、修复后通过;测试路径与修复前的失败输出:
    • desktop/internal/workbench/memory_monitor_test.go(4 个)、desktop/internal/core/process_memory_test.go、desktop/internal/sysinfo/process_test.go 针对新接口编写(Processes、SummarizeProcessMemory、新的弹窗内容),在旧代码上无法编译,所以没有修复前失败的输出;它们断言总量包含子进程和系统辅助进程、类别和名称、读取失败时隐藏数值、0 MB 类别不显示。
    • TestBackgroundRestoredBeforeTheWindowOpensIsApplied(workbench_background_test.go):去掉修复后 5/5 失败,输出 background restored before the window opened = {Set:true Name:gone.png ... Loading:true ... Status:正在载入背景图片…};修复后连续 30 次通过。
  • [N/A] 无法自动化时,已写明手动复现与验证步骤:已自动化;真机对照步骤写在功能矩阵的验证动作里。
  • 已搜索被修改函数、类型、契约字段的全部调用方(含另一平台、插件、SDK、脚本和按字符串分发的调用):SampleProcessMemory、Resident、residentBytes、Status.MemoryValue 已无调用方;SampleProcessTree 仍由 run_performance.go 使用,保留。
  • 已检查另一平台是否有同类缺陷(有则本 PR 一并修复,或已提交 Issue):Windows 用同一份工作台代码,本 PR 一并修复,并加了 Windows 的原生进程表。
  • 只改了修复所需的代码,没有夹带重构;修复过程中发现的其他问题已提交 Issue:[Feature] 运行窗口 CPU/内存曲线改用原生进程表与 Core 汇总,删除 ps/PowerShell 采样 #1925。背景图唤醒问题是让本 PR 的完整测试稳定通过所需的修复,已单列在“改动”里。
  • [N/A] 修复改变了行为约束或容易复发,已在 .agents/notes/implemented/bug-fix/ 记录:归属规则写在 Core 契约和 fixture 里,回归测试覆盖了两个问题。

范围检查 / Scope checks

功能矩阵 / Feature matrix

  • 已更新 shared/platform-feature-matrix/features/memory-monitor.json:两端实现状态 implemented、验证状态 pending(未在真机整机验收),证据路径和验证动作已更新
  • 已运行 ./scripts/platform-feature-matrix/verify-platform-feature-matrix.sh 和 ./scripts/platform-feature-matrix/verify-platform-feature-matrix-change.sh origin/preview HEAD
  • 没有提交 .artifacts/ 下生成的矩阵视图
  • [N/A] 没有更新矩阵的,理由:已更新

插件归属 / Plugin ownership

  • 已在仓库根目录运行 develop-lithe 第三节“插件归属与宿主零依赖”的自查命令,结果为空
  • 新增或修改的测试只使用测试内联的合成插件、Core 契约 fixture 或插件市场索引快照:只用虚构的 “Quill Tools” 和 Core fixture
  • 没有为具体插件提供的能力新建或更新宿主功能矩阵条目
  • 插件文案没有写进 shared/i18n/;插件构建步骤和资源没有写进宿主
  • [N/A] 修改了通用扩展点、可能影响已发布插件的:没有改扩展点,只读取宿主已登记的会话进程号

统一日志系统 / Structured logging

  • [N/A] 没有新增生命周期里程碑或失败路径的日志;采样失败只隐藏数值,属于高频路径,不逐次记录

界面文案 / i18n

  • 宿主文案都走共享 i18n:在 shared/i18n/ 补齐中英词条并运行 node scripts/i18n/generate-i18n.mjs,没有手改生成文件
  • [N/A] 插件提供的文字:没有插件文案;插件名和语言来自插件清单,原样显示
  • 已运行 node scripts/i18n/verify-app-language-localization.mjs 和 node scripts/i18n/generate-i18n.mjs --check,没有放宽脚本或扩大豁免名单

逻辑归属 / Logic placement

性能与内存 / Performance and memory

  • 没有新增空闲轮询:详情关闭时只在状态栏项本来就要绘制时按 10 秒间隔读取,只有打开详情时才每 2 秒请求一帧;读取在后台任务里,有 5 秒超时;进程列表用 Core 默认上限 20 项
  • 主线程没有文件读写和进程启动:进程表读取和 Core 调用都在后台任务里;不再启动 ps/PowerShell 子进程
  • [N/A] 性能基线:不涉及启动、打开项目、输入、滚动、大文件、长输出或搜索

设置 / Settings

  • [N/A] 没有新增或修改设置;弹窗的 ⓘ 说明了统计口径

插件契约 / Plugin contract

N/A:不涉及插件契约、能力版本、插件函数协议和宿主方法;Core PR 只新增应用层命令 processMemory.summarize。

发行包与资源 / Bundle and resources

  • 运行时不向 app bundle 或安装目录写入;没有新增资源路径;已运行 ./scripts/desktop/verify/verify-runtime-bundle-immutability.sh
  • [N/A] 没有新增下载、解压、生成或缓存资源;截图在 docs/assets/memory-details/,是文档资源

无用代码与重构 / Dead code and refactoring


验证 / Verification

命令或操作 平台 结果
Core:cargo test -p lithe-process -p lithe-core macOS 通过
Core:cargo fmt --all -- --check、cargo clippy -p lithe-process -p lithe-core --all-targets -- -D warnings、scripts/rust-core/verify-rust-core-comments.sh macOS 通过
LITHE_CORE_RUST_DIR=<Core 分支> scripts/desktop/desktop-go.sh test ./... -count=1 macOS 55 个包全部通过
scripts/desktop/desktop-go.sh test ./internal/workbench -count=4 macOS 通过(原来约 1/6 概率失败的背景图测试未再失败)
TestBackgroundRestoredBeforeTheWindowOpensIsApplied 去掉修复 / 加上修复 macOS 5/5 失败 / 30/30 通过
合并最新 preview 后 scripts/desktop/desktop-go.sh test ./... -count=1(固定到 5e2a580) macOS 通过:55 个包全部 ok,无 FAIL(workbench 157.9s)
LITHE_VISUAL_OUTPUT_DIR=... scripts/desktop/desktop-go.sh test -tags vivahead,lithevisual ./internal/workbench -run '^TestVisualMemoryDetails$' macOS 无头 生成上方 4 张截图并人工检查
GOOS=windows、GOOS=linux 对 ./internal/sysinfo 运行 go vet macOS 交叉 通过
node scripts/i18n/verify-app-language-localization.mjs、node scripts/i18n/generate-i18n.mjs --check — 通过
node .agents/skills/write-stable-tests/scripts/verify-test-stability.mjs --base origin/preview — 通过
./scripts/platform-feature-matrix/verify-platform-feature-matrix-change.sh origin/preview HEAD — 通过
./scripts/desktop/verify/verify-runtime-bundle-immutability.sh — 通过
插件归属自查命令 — 结果为空

未能执行的检查和原因:

  • Windows:没有在 Windows 上编译和运行(pluginservice 依赖 cgo,无法交叉 vet,只交叉 vet 了 sysinfo 并人工核对了 Windows 文件的导入);Toolhelp32 和专用工作集的读数需要在 Windows 真机对照任务管理器验证。
  • macOS 整机:截图来自无头渲染,没有在打包的应用里对照活动监视器做整机验收,所以功能矩阵保持 pending。

提交前确认 / Final checks

  • 分支从最新的 preview 创建,并已合并最新 preview;目标分支 preview
  • 以 Draft 提交,本地验证通过后才标记 ready
  • 没有无关改动,没有提交 .build/、target/、dist/、.artifacts/ 等产物,没有凭据、个人路径和未脱敏的日志或截图
  • 本次构建、测试或预览启动的 Lithe 应用和子进程已全部关闭,没有进程残留(运行中的 ../.artifacts/desktop/bin/lithe 来自主检出,不是本次启动的)

- 原生进程表:macOS 读 proc_pid_rusage 物理占用和负责进程,Windows 读专用工作集,
  Linux 读 /proc;不再每次启动 ps 或 PowerShell。
- 归属与汇总交给 Core processMemory.summarize:主进程、语言服务器、插件服务、
  子进程和 macOS 的 WebKit 等系统辅助进程分开统计。
- 弹窗重排:总量和 ⓘ 说明、按类别堆叠的条、图例、占用最多的进程和其余项合计;
  加宽到 340 不再截断数值,0 MB 的类别不显示,只有主进程时给出说明。
- 删除只为旧状态栏服务的 SampleProcessMemory、processTable、treeMemory 和 Status.MemoryValue。
- 修复启动时背景图恢复结果在窗口绑定唤醒前投递而丢失唤醒的问题:首帧发现已排队的结果时
  主动请求一次唤醒(TestMissingBackgroundCopyShowsNoImageQuietly 偶发失败的根因)。
- Core 固定到 Lithe-core-rust#43 的 5e2a580。
# Conflicts:
#	scripts/desktop/build-desktop-core.sh
@ghfind-review ghfind-review Bot added the review: high ghfind author score; see https://ghfind.com label Nov 14, 2026
@xiaoyumuxi
xiaoyumuxi marked this pull request as ready for review November 14, 2026 13:04
@xiaoyumuxi
xiaoyumuxi merged commit 0f95f47 into preview Nov 14, 2026
4 of 7 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

review: high ghfind author score; see https://ghfind.com

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant