Omarchy 崩溃诊断实战:基于 systemd-coredump 的证据链、符号化与 AI 技能化归因
Omarchy 将“崩溃为什么发生”这件事做成了一个可复用、可执行、可被 AI Agent 直接遵循的技能:default/agents/skills/diagnose-crash/SKILL.md 定义了从 coredumpctl 取证、排除资源耗尽、时间线关联、core dump 符号化,到最终撰写归因报告与静音通知的完整方法论。读完本文,你将掌握在 Omarchy(Arch Linux 之上的配置层)上排查 segfault / SIGABRT / OOM 类崩溃的标准流程,并理解其背后 omarchy-crash-watch 通知管线与 omarchy crash mute 静音机制的实现细节。
1. 这套“崩溃诊断”在 Omarchy 中是什么
diagnose-crash 不是人读的运维手册,而是给编码 Agent(编码代理)执行的方法论文件,由 YAML frontmatter 声明触发条件:
name: diagnose-crash
description: >
Diagnose why a program crashed on this machine, from a systemd-coredump core dump.
Use when a process has segfaulted, aborted, or otherwise dumped core, ...
Triggers: crash, segfault, SIGSEGV, SIGABRT, core dump, coredumpctl,
"why did X crash", "X keeps crashing", backtrace symbolization.
(见 SKILL.md)
它有两个入口,都最终汇聚到同一份方法论:
- 通知入口:
omarchy-crash-watch守护进程监听 systemd-coredump 的 journal 事件,进程崩溃后弹出 "Process crashed:" 通知,点击即调用omarchy agent crash <pid>; - 手动入口:对
coredumpctl list中的任意 PID 直接运行omarchy agent crash <pid>。
bin/omarchy-agent-crash 负责“收集事实 + 指向技能”:它校验 PID 为纯数字,用 coredumpctl list "$pid" 补一个时间戳,然后把 process / PID / binary / signal / time 五要素拼成一段 prompt,exec omarchy-agent --prompt "$prompt" 交给默认 Agent 执行。其注释明确说明设计意图:“方法论住在 diagnose-crash 技能里,这样只需在一处编辑,且对任何默认 Agent 都有效;本脚本只负责采集事实并指向技能”。
官方手册 manual/17-ai.md 对这套机制的表述是:watching 默认开启,可在 Trigger > Toggle > Crash Capture(等价于 omarchy toggle crash-capture)整体关闭,关闭后手动入口仍然可用。
2. 诊断方法论:从证据出发,而不是编一个像样的故事
技能开篇的总原则只有两句话:“Work from evidence. The goal is an honest account of what happened, not a plausible-sounding story.”(基于证据工作;目标是诚实地交代发生了什么,而不是讲一个听起来合理的故事。)以下逐节展开其要求。
2.1 确立事实:coredumpctl 是起点
coredumpctl info <pid>是第一个要跑的命令。除了调用栈(backtrace),必须注意进程的命令行(command line)——它通常揭示了程序死亡时在做什么,而“它在做什么”往往就是全部答案。coredumpctl list用于判断这次崩溃是孤立事件还是模式。同一个程序反复崩溃、或多个程序同时死亡,指向的方向与单次失败完全不同(例如系统级资源问题而非某程序缺陷)。
2.2 先排除“无聊的原因”
技能要求在指责程序之前先检查资源耗尽:
free -h # 内存是否见底
# 再到 journal 中查 OOM kill 记录
核心结论:被 OOM killer 杀掉的进程不是该进程的 bug。这是整篇方法论中优先级最高的排除项。
2.3 与时间线关联:最被低估的证据
崩溃时间戳要与三类外部证据对照:
- 文件系统 mtime:某个目录或文件的 mtime 与崩溃落在同一秒,强烈暗示它就是触发源;
- journal:崩溃时刻前后、来自同进程或相邻进程的相关告警;
- 近期软件包更新:崩溃恰好始于一次更新之后,矛头就指向该更新。
2.4 读整个 core,而不只是 frame 0
崩溃线程之外的线程栈展示了哪些工作正在进行中(in flight)——缩略图生成器(thumbnailer)、图像加载器、IPC 读取器、GPU 队列等。这种上下文常常能在崩溃帧本身无法符号化时解释触发原因。
同时记录地址空间中的第三方代码:文件管理器或浏览器扩展、插件、树外驱动。进程内第三方代码是常见的崩溃来源,值得标记——但没有证据表明它被牵连时,不得把锅钉死在它头上。
2.5 能符号化就符号化:Arch 的 debuginfod 公开服务器
Omarchy 基于 Arch Linux,而 Arch 运行着公开的 debuginfod 服务器。技能给出的完整符号化流程:
core=$(mktemp -t crash-XXXXXX.core)
trap 'rm -f "$core"' EXIT
coredumpctl dump <pid> --output="$core"
DEBUGINFOD_URLS="https://debuginfod.archlinux.org" \
gdb -q <executable> "$core" \
-batch -ex 'set debuginfod enabled on' -ex 'bt'
这段命令有三个值得注意的工程细节:
mktemp+trap ... EXIT:core dump 是进程内存的逐字节拷贝,可能包含口令、token 与私密文档。写入全新的临时路径而非可预测的共享路径,并在退出时自动删除——绝不留在/tmp过夜。DEBUGINFOD_URLS环境变量:让 GDB 在线从 Arch 的 debuginfod 拉取调试信息,无需本地安装 debug 包。-batch -ex 'bt':非交互模式,只取回溯,适合脚本化。
技能同时强调了诚实边界:很多包不发布调试符号,帧无法解析时就要明说,绝不允许编造函数名来填空。即使无法符号化,堆栈仍有“形状”:每帧属于哪个库、崩溃来自信号处理器 / 主循环 / 工作线程中的哪一层,这些仍可陈述。
2.6 报告四要素与“系统原样离开”
诊断完成后,报告必须回答四点:
- 什么东西崩溃了,崩溃时它在做什么;
- 最可能的机制——明确区分证据“证明”了什么与你在“推断”什么;
- 是否有用户数据丢失,以及从哪里可恢复——在下结论之前先检查回收站;
- 是否可能复发,以及什么能避免或修复它。
如果原因确实含糊,就说含糊,不要用猜测拼凑出自信的语气。
技能还立了一条硬约束:“Leave the system as you found it.”(把系统留成你发现它的样子。)诊断只读,不修复、不清理、不重配置。唯一需要清理的是自己产生的东西——即上面抽取出来的 core 副本;诊断唯一允许做的“变更”是用户主动要求时的那次静音(mute)。
3. 静音机制:omarchy crash mute 的语义与实现
诊断解释了崩溃,但崩溃可能继续发生。技能的收尾动作是主动提议为该程序单独关闭通知——且绝不允许 Agent 未经请求就执行静音,并要在同一句话里说明如何恢复,避免成为单行道:
omarchy-crash-mute '<program>' # 静音
omarchy-crash-mute '<program>' off # 恢复通知
omarchy-crash-mute # 列出当前静音了谁
bin/omarchy-crash-mute 的实现印证了技能中的三条告诫:
- 名称归约:
program=${program##*/}把传入的二进制路径(如/usr/bin/hyprland)归约为 basename,与 watcher 读取的键保持一致; - 注入防护:拒绝空名、
.、..、纯路径成分,防止静音标记写到crash-ignore/目录之外; - 真实状态回报:执行
omarchy-toggle "crash-ignore/$program"后再用omarchy-toggle-enabled复查,报告的是“现在真实如此”而非“你要求如此”; - 状态落盘为每程序一个标记文件:
$HOME/.local/state/omarchy/toggles/crash-ignore/<program>(readonly MUTES=...),每程序一个文件而非一份列表,是为了让取消某一个时无需读改写并重新解析其余条目。
技能中针对静音的三个陷阱,均可在源码或测试中找到对应:
| 陷阱 | 技能告诫 | 仓库佐证 |
|---|---|---|
| 传截断名 | 进程名被截断到 15 字符,而 basename 不会;用截断名静音将“看起来生效”却永远匹配不到 | omarchy-crash-watch 注释 “comm is truncated to 15 characters, so prefer the executable's basename” |
| 不引用 | 程序名里一个单引号会闭合你的引号并把剩余部分当 shell 执行 | crash-capture-test.sh 对路径穿越、-h 等边界名的成组断言 |
| 解释器前缀 | 键是裸名,经解释器运行的程序以解释器为键——静音 python3.13 会静音本机所有 Python 程序,必须明说 |
测试 “a mute that could not be written is not reported as one” 等对语义边界的覆盖 |
另外两点整体性说明:对全部程序(而非某一个)的开关在 Trigger > Toggle > Crash Capture;而且“用静音替代本可达成的修复”本身就是错误答案。
4. 底层管线:omarchy-crash-watch 如何把崩溃变成通知
bin/omarchy-crash-watch 是通知侧的完整实现,读懂它能验证技能所依赖的每一条“事实”从哪来:
- 事件源:
journalctl -f -n 0 -o json "MESSAGE_ID=fc2e22bc6ee647b6b90729ab34a250b1"——systemd-coredump 用固定 MESSAGE_ID 在 journal 中记录每个 core dump,携带结构化的COREDUMP_*字段(比文件名信息更多);-n 0保证重启不会重复播报已处理的崩溃。 - 字段解析防坑:
jq把空字段与缺失字段统一替换为-,再按制表符切出uid/comm/pid/exe/signal——注释指出 tab 是 IFS 空白,空字段会折叠导致后续字段整体错位,曾有进程自设空 comm 导致崩溃被误读为别的进程而被丢弃。 - 安全过滤链:只播报当前用户(
uid == UID)的崩溃;名字归约后必须是单一成分(防止 prctl 伪造含斜杠的 comm 把静音标记写到目录外);omarchy-crash-*/omarchy-agent-*自身永不播报;OMARCHY_CRASH_IGNORE可扩展正则黑名单。 - 去重窗口:每个程序在
OMARCHY_CRASH_DEDUPE_SECONDS(默认 60 秒)内至多播报一次,防止崩溃循环刷屏;且只有成功送达的通知才启动窗口,发送失败不会抑制后续一分钟的循环。 - shell 崩溃的投递补偿:通知服务器(org.freedesktop.Notifications)由 shell 持有,shell 自己崩溃时 toast 无处可送——watcher 会等重启后的 shell 重新认领 D-Bus 名称再发送,因为“最难送达的崩溃恰恰最值得报告”。
- 点击动作:
--exec omarchy-agent-crash "$pid" "$comm" "$exe" "$signal",崩溃详情作为独立的 argv 词传递,恶意进程名无法被重新解析为命令。
5. 何时这是 Omarchy 的 bug:上报边界
技能末节把“向 Omarchy 上游报告”收窄为少数情形,细节写在同目录的 reporting.md:
- 责任域判定:Omarchy 是 Arch Linux 之上的配置层,第三方应用(文件管理器、浏览器、GNOME/Qt 库)内部的崩溃几乎总是那个项目的上游 bug。Omarchy 的控制域大致是:
omarchy-*命令、Quickshell shell 及其插件、随附的 Hyprland 与终端配置、主题、安装与迁移脚本,以及它对所装之物的打包与配置方式。“Omarchy 只是安装了一个程序”不构成 Omarchy bug,除非 Omarchy 自身的打包或配置被牵连。 - 三条件缺一不可:是 Omarchy 责任域内、有证据验证的 bug;用户已明确同意(先展示确切的标题与正文并等待确认,绝不代用户提交);机器具备提交能力(
gh auth status必须通过;gh缺失或未认证时,把写好的文本交给用户自行提交,不代为安装或认证)。 - 先搜后报:按崩溃程序、信号、回溯中的特征符号(而非你即将写的标题措辞)搜索,且要包含 closed issue——已按 fixed 关闭但当前系统仍复现的,是回归,比再开一个重复 issue 更有价值。
- 签名:issue 或评论结尾署明产生它的模型与 agent harness,让人类读者知道这是机器撰写的内容。
6. 上手路径与验证
- 查看方法论全文:default/agents/skills/diagnose-crash/SKILL.md;上游报告流程见 reporting.md。
- 实际排查:
coredumpctl list找到 PID →coredumpctl info <pid>→ 按 2.2–2.5 节取证 →omarchy agent crash <pid>让默认 Agent 走完整技能。 - 行为回归验证:test/shell.d/crash-capture-test.sh 用 fixture 驱动
omarchy-crash-watch与omarchy-crash-mute全链路,断言涵盖:路径归约到与 watcher 相同的键、off可撤销(“不是单行道”)、重复静音不反转为恢复、点号名可见可撤销、拒绝越界写入与未知 action、--可静音名为-h的程序、toggle双向翻转、只统计 watcher 真正承认的常规文件标记,以及“技能文本必须仍写明静音命令”这类跨文件一致性检查。 - 相关文档:manual/17-ai.md(AI 章节,含手动运行与静音说明)、manual/13-toggles-idle-screensaver.md(Crash Capture 开关表项)。
适用前提与限制:符号化依赖 Arch 公开 debuginfod 与目标包发布调试符号,帧无法解析属预期现象而非流程失败;整套通知管线只在已选择默认 Agent 时投递(watcher 逐条检查 omarchy-default-agent),且仅覆盖当前用户的崩溃;core dump 含敏感数据,临时文件必须即用即删。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0626
Hy4-previewHy4 preview 是由腾讯混元团队研发的新一代混合专家(MoE)旗舰模型。模型总参数量 770B,每个 token 激活 49B,主干共包含78层,第一层采用标准 FFN,其余 77 层均为 MoE 结构,每层包含 256 个路由专家与 1 个共享专家,每个 token 激活 top-8 路由专家及共享专家。主干之外原生内置 1 层 MTP(总参数量 10B,激活 0.7B)以支持投机解码。Python00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
GLM-5.3-FlashGLM-5.3-Flash (320B-A18B),是GLM-5系列的首个原生多模态模型。320B总参数,能力超过GLM-5.2Jinja00
Spark-X2.5-4BSpark-X2.5-4B 旨在让强大的 AI 更实用、更高效、更易获得。在广泛日常任务中表现强劲,涵盖对话、写作、翻译、推理、编码、工具调用以及智能体工作流,并在同等规模的开源模型中取得领先成绩。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00