本节摘要:eBPF 现场最常见的三句话是“加载失败”“没数据”“变慢了”。它们对应验证闸、挂载/触发、开销三条线。排错顺序固定:先确认对象还在、再确认钩子在计数、再看丢失、最后才读业务逻辑。随机改代码和开跟踪打印,会把现场变得更脏。
阅读完本节,你应当能够:
第 2.2 节给过两道闸。现场要把那张图当检查单,而不是凭直觉重编。
编译期。 头文件、BTF 生成、段名、许可证字符串。内核还没看见你。
验证拒绝。 有验证日志。按 5.1 从最后指令往回。不要在这一步猜挂载点。
创建 Map / 额度。 errno 像权限或内存。查 memlock、cgroup 记账、max_entries。
挂载失败。 程序类型与接口不符、无权限、网卡不支持、cgroup 路径错、与平台抢挂钩。程序对象可能已经存在,只是没干活。
挂上但计数为零。 事件没来、过滤太狠、挂错 CPU/网卡/cgroup、uprobe 符号已变。
计数在涨但用户态没数据。 读错 CPU 汇总、读错 Map、环形缓冲丢了、消费者太慢。
有数据但不对。 CO-RE 字段、PID 复用、字节序、时间源。这才是逻辑。
生产上少用跟踪打印。它限速、污染公共缓冲、改变时序。用 Map 里的错误码直方图:lookup 失败、读内核失败、插入失败、尾调用失败,各记一桶。用户态展示这些桶,比 SSH 上看打印更接近产品。
无条件计数永远留着,用开关关掉出栈,不要删掉计数。排“没数据”时先看它。它不涨,别改解析代码。
验证失败在 CI 里就该失败。生产节点上才第一次验证,说明矩阵测试缺了这条内核。把日志带回去复现,不要在生产上循环试 pragma。
| 症状 | 先看 | 不要先做 |
|---|---|---|
| invalid argument | 验证日志还是挂载 errno | 改业务字段 |
| 计数为零 | 挂载点、过滤、符号 | 加大环形缓冲 |
| 计数涨、事件无 | 丢失计数、消费者 | 重写解析 |
| 字段像垃圾 | BTF/CO-RE、读失败桶 | 换框架 |
| 节点变慢 | 8.1 对照开关 | 加更多探针找原因 |
⚠️ 常见坑:同时改挂载点、过滤条件和事件结构。成功了你也不知道哪一刀有效,失败了无法回退。一次只改一个变量。
💡 关键直觉:把程序当成仪器。仪器坏了先看电源和电极,不先改算法。计数是电源指示灯。
加载器被杀、pin 还在,探针成幽灵:没人认领,还在吃 CPU。清单必须能回答“这条程序的所有者、版本、卸载命令”。bpf 文件系统和当前程序列表进巡检,和进程巡检同等重要。
多人叠加时,排障先看清单总频率,再看你的程序。否则你会优化自己那 5%,无视别人的 95%。
权限问题在开发机被 sudo 掩盖,生产按能力位收紧后爆发。把最小能力集写进部署清单:多数观测不需要覆盖返回值那种危险能力。
检查单 1. 对象存在吗,属于谁 2. 无条件计数涨吗 3. 失败桶与丢失计数 4. 开关对照对 P99 的影响 5. 才读字段与业务逻辑
把字节码 dump 出来,对着指令号看。或在 CI 用带调试信息的构建。不要在生产开最重的调试构建常驻。
看丢失计数是否与高峰对齐;看 pin 是否被调和循环摘掉又挂上;看 uprobe 是否随发布失效。间歇往往是寿命和管道,不是解析偶发错误。
把本节的分类压成值班手册一页。十分钟内必须走完:查清单该程序是否应存在;查 link 与 pin;查无条件计数近五分钟是否非零;查失败桶与丢失;查业务 P99 是否与开关时间对齐。五步之后才允许打开更重的跟踪打印或上实验 kprobe。超时还没走完五步就去改字段,几乎一定在延长故障。
手册里预置命令的意图,不预置具体文件路径:列出当前程序、列出 Map 内存、读取某计数键、切换热开关、销毁 link。意图写清,具体工具名随发行版变化。新人按意图找平台封装,而不是复制过期命令。
升级窗口的排障略有不同。升级后零数据,优先怀疑 BTF 和跟踪点改名,而不是业务流量消失。对照心跳探针若也零,就是挂钩层问题。只业务探针为零、心跳仍在,才是过滤或符号。把升级和日常分成两页手册,能减少套错模板。
和内核组协作时,带上已经分类的证据:这是验证拒绝不是 oops,这是额度不是死锁。分类错误会让内核组以为机器在崩,实际只是加载失败。尊重对方时间的方式,就是先把自己的闸门走完。
事后把这十分钟的实际耗时记下来。若经常卡在“找不到所有者”,去补清单;卡在“没有热开关”,去补 8.3;卡在“计数在涨但没人懂字段”,去补文档。排错路径是在暴露工程欠债,不只是在救当场。
值班直接改字段,想把没数据修好。改了三处,数据仍没有,P99 却动了。事后发现是挂错 cgroup,无条件计数一直为零,改字段根本不该发生。十分钟路径的第一步就能拦住。后来把路径做成加载器子命令:一条命令跑完清单、link、计数、丢失、开关。人在慌张时会跳过手册,较少跳过一条现成命令。
分类错误也曾把内核组叫起来:加载失败被说成节点要崩。内核组看到的是验证日志,不是 oops。先分类再升级,是尊重别人的时间。分类卡片就贴在值班页顶部。
间歇性没数据对齐了发布窗口:uprobe 随二进制失效。手册把升级窗口和日常拆成两页之后,套错模板的次数下降。模板错了,再认真也走错楼。
人慌时跳过手册,较少跳过现成子命令。子命令跑完清单、link、计数、丢失、开关。计数为零时禁止改字段。升级窗口优先怀疑 BTF 和跟踪点改名。间歇对齐发布窗口时优先怀疑 uprobe 和 pin 被摘挂。分类错误不要把内核组当 oops 叫起来。事后记录卡在哪一步:找不到所有者就补清单,没有热开关就补发布,计数涨但不懂字段就补文档。排错路径是在暴露欠债。欠债不暴露,会在下一班继续吃人。吃人表现为重复踩同一块石头。石头有名字:无心跳、无清单、无开关、一次改三处。名字写进子命令的输出里,比写进培训幻灯片里更常被看见。看见才会改。改完才配说我们会排 eBPF 的错。会排的标志是十分钟内能分类,不是十分钟内能改代码。改代码是分类之后的事。之前改,叫乱动。乱动会制造第二现场。第二现场比第一现场难修。
排错像分诊。分诊错了,后面的专家再贵也没用。eBPF 的分诊是五问:该不该在、link 在不在、心跳涨不涨、丢失涨不涨、开关是否动了 P99。五问之前改字段,等于没量体温就开刀。开刀有时碰巧对。碰巧会教会团队错误的速度感。速度感应来自分诊快,不来自改得快。改得快会制造第二现场。第二现场需要另一轮五问。两轮五问加起来,比一轮五问再改一次字段更慢。更慢还更脏。脏了对照就死。对照死了,你甚至无法证明刚才那一刀有没有用。没有证明的刀,不要吹。吹了会变成规范。规范里不该有碰巧。
\n\n## 课堂补充\n\n分诊五问在改字段之前。电源灯不亮不查解析。失败桶替代打印海。一次一个变量。路径做成命令。升级先怀疑符号。间歇对齐窗口。加载失败不是oops。卡哪补哪。生产不开最重调试。对账信内核。分类快才是真快。\n\n\n\n## 生产验收条\n\n1. 围绕「调试与故障排查路径」,生产验收只认能关掉、能计数、能对账,不认口头保证。\n2. 围绕「调试与故障排查路径」,把所有者、版本、卸载方式写成清单三件套,缺一视为幽灵。\n3. 围绕「调试与故障排查路径」,对照实验必须能回答开关前后业务指标动了没有。\n4. 围绕「调试与故障排查路径」,失败要分类到验证、额度、挂载、未触发、丢失,禁止只丢一句笼统错误。\n5. 围绕「调试与故障排查路径」,热路径默认克制,阈值之后才出栈,出栈之前先证明钩子活着。\n6. 围绕「调试与故障排查路径」,和平台已有挂钩点冲突时先登记再加载,禁止手工抢挂。\n7. 围绕「调试与故障排查路径」,内核版本矩阵没跑绿就不能把功能写成必成功路径。\n8. 围绕「调试与故障排查路径」,回滚必须碰到内核对象,心跳停止才算撤回成功。\n\n## 本节速览
下一节把能停、能签、能灰度写成发布步骤,让排错时的开关真正存在。