
BCC eBPF 脚本贡献指南从短示例到生产级 Linux 性能排查工具的完整开发规范【免费下载链接】bccBCC - Tools for BPF-based Linux IO analysis, networking, monitoring, and more项目地址: https://gitcode.com/gh_mirrors/bc/bcc导读本文以 CONTRIBUTING-SCRIPTS.md由 Brendan Gregg 撰写为骨架系统讲解如何向 BCC 项目贡献 eBPF 脚本以及如何把自研的 BCC 程序打磨到生产可用。你将从「示例examples」与「工具tools」两类脚本的定位差异入手掌握一条包含 20 个检查项的工具开发清单并看到它们如何落实在仓库的真实源码、示例输出、man 手册与自动化测试中。读完本文你将具备独立开发、测试、文档化并提交一个 BCC 工具的完整方法论。一、两类脚本/examples 与 /tools 的定位差异BCC 仓库把脚本严格分成两类放在不同目录贡献时的要求也截然不同目录定位核心要求examples/bcc 与 eBPF 的短示例简短、整洁、注释充分代码注释提交可以只有示例代码本身tools/生产级性能与故障排查工具有用、经过测试、低开销、文档齐全含所有 caveats、易于使用提交应包含 4 项改动工具本体、man 手册、example 文件、README.md 条目文档特别强调tools 下的工具会以 root 身份运行在关键任务环境科技公司、金融机构、政府机构中。因此如果你没有耐心花数小时测试建议把想法作为 issue 提交或到 IRC#iovisorirc.oftc.net讨论而不是直接提交一个半成品工具。这两个目录在仓库中的配套结构也能印证这种划分tools 下的每个工具通常伴随*_example.txt输出示例文件man/man8/ 目录存放对应 ROFF 格式手册页而 examples/ 则按 networking、tracing 等子目录分组存放短小示例。二、编写示例脚本Examples短、净、有注释示例按子目录分组networking、tracing 等。示例脚本有两种组织形式仓库里都有现成范例2.1 Python 内嵌 C 的单一文件形式如 examples/tracing/strlen_count.py用BPF(text...)把 C 程序直接嵌入 Pythonfrom __future__ import print_function from bcc import BPF from bcc.utils import printb from time import sleep # load BPF program b BPF(text #include uapi/linux/ptrace.h struct key_t { char c[80]; }; BPF_HASH(counts, struct key_t); int count(struct pt_regs *ctx) { if (!PT_REGS_PARM1(ctx)) return 0; struct key_t key {}; u64 zero 0, *val; bpf_probe_read_user(key.c, sizeof(key.c), (void *)PT_REGS_PARM1(ctx)); // could also use counts.increment(key) val counts.lookup_or_try_init(key, zero); if (val) { (*val); } return 0; }; ) b.attach_uprobe(namec, symstrlen, fn_namecount)这个示例演示了 uprobe 的最基本用法attach_uprobe挂载strlenBPF_HASH做频次统计PT_REGS_PARM1(ctx)读取第一个参数。注释里还点出counts.increment(key)这种更简洁的等价写法——这就是「短、净、有注释」的范本。2.2 分离的 Python 与 C 文件形式如 examples/tracing/vfsreadlat.py 与 examples/tracing/vfsreadlat.c 成对出现。Python 侧只负责参数解析、挂载探针和输出b BPF(src_file vfsreadlat.c) b.attach_kprobe(eventvfs_read, fn_namedo_entry) b.attach_kretprobe(eventvfs_read, fn_namedo_return)C 侧用BPF_HASH(start, u32)记录每个 PID 的时间戳BPF_HISTOGRAM(dist)以 log2 分桶累积延迟分布kprobe/kretprobe 配对测函数耗时——这是 VFS 读延迟直方图的经典写法。写作要点示例不需要像工具那样面面俱到重点是用最少的代码讲清楚一个 bcc 编程概念uprobe、kprobe、哈希 map、直方图、perf 输出等。三、工具开发清单20 个检查项全解tools 工具的开发有一套完整清单下面逐条展开并对照仓库真实实现给出可验证的依据。1. 调研领域全貌Research the topic landscape先学习已有工具与指标包括 /proc 中的判断真实世界中存在哪些亟待解决的问题。文档的立场很鲜明我们不缺「我猜这个有用」的工具缺的是「啊哈以前我根本做不到这个」的工具。可以到 IRC #iovisor 频道、邮件列表见 README.md或 GitHub issue 征求其他开发者意见。2. 为测试构造已知负载Create a known workload写一个 10 行的 C 程序、用微基准工具、或直接在 shell 里模拟都可以。文档作者常用dd(1)从 /dev/urandom 或磁盘设备写往 /dev/null——既能设定 I/O 大小和次数又能得到吞吐量统计来交叉核对工具输出。不知道如何构造负载就去学这一步能提供大量容易被忽略的上下文细节。3. 只解决一个问题且解决到位Unix 哲学遵守 Unix 哲学一件事做到极致。netstat 不会提供 tcpdump 式的抓包选项因为那是另一个工具的事。BCC 工具同样遵循这个原则——每个工具聚焦单一场景如 biolatency 只管延迟分布biosnoop 只管逐请求详情。4. 验证工具能正确测量已知负载尽可能制造素数个事件如 23 个来测试确认数字完全吻合再尝试其他负载变体。素数负载的好处是难以被巧合整除便于发现少计/多计问题。5. 用其他可观测性工具做交叉验证例如一个 PCI 总线工具显示当前吞吐 28 Gbytes/sec如何 sanity check看看有哪些 PCI 设备磁盘、网卡用 iostat、nicstat、sar 测它们各自的吞吐核对是否与 28 Gbytes/sec 量级吻合还要考虑 PCI 帧开销。6. 测量工具开销运行微基准时对比工具开启前后的耗时差与 CPU 消耗。要测最坏情况先把 CPU 余量打满再运行 bcc 工具观察开销是否可接受、能否进一步降低。7. 反复测试与压力测试在别人踩坑之前把所有坏情况发现并修复掉。这也是 tests/python/test_tools_smoke.py 存在的意义——让核心库变更后工具仍能工作见第 18 项。8. 考虑命令行选项设计是否需要-p过滤 PID-T时间戳-i间隔参考其他工具的风格usage 消息末尾应列出示例用法argparse 的epilogexamples。若某选项是绝对常见场景可考虑直接作为首个位置参数而非开关无需-X*stat类工具iostat/vmstat 等的惯例是[interval [count]]。仓库实现可直接佐证tools/biolatency.py 使用 argparse 的RawDescriptionHelpFormatterepilog中列出全部 examplestools/tcpstates.py 同样在epilog里给出./tcpstates -t、./tcpstates -L 80等用法并用add_mutually_exclusive_group()实现-4/-6互斥。9. 输出简洁、直观、自解释默认输出要满足最常见需求次要字段留给选项如-v详细模式。建议包含自解释的启动消息例如Tracing block device I/O. Output every 1 seconds. Ctrl-C to end.biolatency 的真实输出即是「Tracing block device I/O... Hit Ctrl-C to end.」。10. 默认输出宽度 80 字符尽量把输出控制在 80 字符以内尤其是默认输出。这样输出不仅能放进最小尺寸的终端也能放进幻灯片、博客、文章和印刷材料——技术书籍的排版模板往往不允许缩小字体来适配超宽输出。11. 简短的工具名沿用其他工具及 /usr/bin 工具的命名风格短、易输入、不使用下划线。仓库中如 biolatency、biosnoop、tcpstates、runqlat 均符合此风格。12. 用 pep8 检查 Python 风格执行命令pep8 --show-source --ignoreE123,E125,E126,E127,E128,E302 filename注意 pep8 会漏掉一些东西如用法的一致性仍需人工复查脚本。仓库配套的 scripts/py-style-check.sh 即用pycodestyle -r --show-source --ignoreE123,E125,E126,E127,E128,E302检查 tools 下所有 .py并校验可执行脚本的 shebang 是否为#!/usr/bin/python。13. 确保脚本 Python3 就绪添加如下导入有助于 Python3 兼容from __future__ import absolute_import, division, print_function, unicode_literals仓库中大量工具在文件头保留from __future__ import print_function正是这一要求的体现新代码建议直接面向 Python3 编写如 tests/python/test_tools_smoke.py 的 shebang 为#!/usr/bin/env python3。14. 编写 _example.txt 文件模仿 tools/biolatency_example.txt 的样式开头一句介绍中间是示例结尾附上 USAGE 消息。每个示例都要解释——第一个示例要说明我们看到的是什么对有些人并不显然还要说明为什么运行该工具、它解决什么问题。写出好例子可能要花数小时但值得这些例子会被到处复制传播演示、文章。以 biolatency_example.txt 为例它先解释默认直方图每一行的含义usecs 区间、count、distribution再逐个演示-mT 1 5、-Q含内核排队时间、-D按磁盘分桶、-F按 I/O flags 分桶、-e总计/均值扩展、-jJSON 字典输出最后以完整 usage 收尾——这是所有工具 example 文件的标准模板。15. 通读你的 example.txt读一遍自己的示例文件是否过于小众或绕是否花了太多篇幅解释 caveats这些信号说明你应该回头修工具甚至放弃它——也许它更适合作为 /examples 示例而非工具。文档作者坦承很多工具是在这个阶段被放弃的。16. 编写 man 手册页格式可以是 ROFF.8、markdown.md或纯文本.txt关键是记录重要章节尤其是字段columns和caveats放在 man/man8/ 目录下。必须包含overhead 章节且不粉饰——让终端用户提前知道高开销总好过他们用惨痛代价自己发现。同时解释 caveats别假设用户能看出来。以 man/man8/biolatency.8 为例包含 NAME/SYNOPSIS/DESCRIPTION/OPTIONS/EXAMPLES/FIELDS/OVERHEAD 完整章节FIELDS 逐项解释 usecs、msecs、count、distribution 列的含义OVERHEAD 明确说明「通过内核函数追踪并在内核中维护时间戳与直方图异步拷贝到用户态对多数存储 I/O 率 10k IOPS开销可忽略」并要求高 IOPS 环境自行测试量化——这就是「pull no punches」的范本。17. 通读你的 man 页ROFF 格式用nroff -man filename渲染检查。就像把话说出口是否过于小众或绕这同样是「回头修或放弃」的信号。18. 拼写检查文档提交前用 aspell 之类的拼写检查器校对文档质量。19. 在 README.md 添加条目tools 工具需在根 README.md 的 tools 列表中加入一行格式如- tools/[biolatency](https://link.gitcode.com/i/818be673dff3ceb623b57586bacc8656): Summarize block device I/O latency as a histogram. [Examples](https://link.gitcode.com/i/657dd2e546001691494985b09abcced4).对应 README.md 第 186 行附近的实际条目格式。20. 添加冒烟测试并提交 PR向 tests/python/test_tools_smoke.py 添加一个冒烟测试作为「核心库变更后工具仍能工作」的基本检查。该文件用 unittest 组织run_with_duration工具自带超时如biolatency.py 1 1与run_with_int发送 SIGINT 模拟 Ctrl-C 退出如biosnoop.py两种辅助方法区分工具行为并用kernel_version_ge()跳过不满足内核版本要求的用例。完成以上所有步骤后提交 Pull Request。四、生产级工具解剖以 biolatency 为例的完整落地清单各条在仓库中的具体落地可以借 tools/biolatency.py 串起来看单一职责只做「块设备 I/O 延迟直方图」一件事参数与 usageargparse 定义-T时间戳、-Q含内核排队时间、-m毫秒直方图、-D按磁盘、-F按 I/O flags、-e总计/均值、-jJSON、-d DISK指定磁盘位置参数为[interval [count]]epilog列出全部 examples启动消息Tracing block device I/O... Hit Ctrl-C to end.低开销实现BPF_HASH(start, struct start_key)在内核存时间戳STORAGE宏展开为直方图 map事件仅在内核侧累积、输出时才拷贝到用户态man 页 OVERHEAD 章节对此有说明文档配套tools/biolatency_example.txt、man/man8/biolatency.8、README.md 条目、tests/python/test_tools_smoke.py 中的test_biolatencyself.run_with_duration(biolatency.py 1 1)冒烟测试——四个提交件齐备。从源码结构看biolatency.py 中__trace_req_start用bpf_ktime_get_ns()记录时间戳、start.update(key, ts)存入哈希表可以推断其延迟测量从请求下发设备起算、到完成时止而-Q通过更换 trace 点把起点前移到内核排队时刻——这正是 example 文件与 man 页所描述语义的实现支撑。五、测试方法论从已知负载到交叉验证清单第 2、4、5、6、7 项构成一条完整的测试链条值得单独强调构造已知负载dd、微基准或小型 C 程序得到可预期的真实结果素数事件验证用 23 个事件核对计数排除巧合整除交叉验证用 iostat/nicstat/sar 等既有工具对照量级开销量化CPU 余量打满时运行工具测最坏情况压力测试在发布前把所有坏路径暴露并修复。这套方法论确保工具在「无法用肉眼验证」的复杂环境下依然可信——尤其是被 root 运行在关键任务生产环境中时。六、常见信号与放弃时机文档在多处提到「回头修工具或放弃它」的判断信号汇总如下example.txt 需要花大量篇幅解释 caveatsman 页读起来过于小众或绕工具解决的问题本身站不住脚「我猜这个有用」型没有精力完成数小时测试投入。遇到这些情况把想法作为 issue 提交或到 IRC 讨论比强行提交一个不成熟的工具更符合项目利益——它也许更适合写成 /examples 示例。七、总结一份可执行的提交路线图把 20 项清单浓缩成提交前的最终路线图选题调研 → 确认「啊哈以前做不到」的价值构造已知负载并实现最小可用的工具素数事件 交叉验证 开销测量 压力测试打磨 CLIargparse、epilog examples、[interval [count]]惯例与输出80 字符、自解释启动消息、短工具名通过 pep8/pycodestyle忽略 E123,E125,E126,E127,E128,E302并确保 Python3 兼容编写_example.txt与 man 手册含字段说明与不粉饰的 overhead 章节通读并拼写检查在 README.md 登记条目向 tests/python/test_tools_smoke.py 添加冒烟测试Pull Request。遵循这份清单提交的 BCC 工具将同时具备正确性、低开销、易用性与完备文档——这正是 BCC 生态中生产级工具得以长期维护、被广泛复制传播的质量基础。【免费下载链接】bccBCC - Tools for BPF-based Linux IO analysis, networking, monitoring, and more项目地址: https://gitcode.com/gh_mirrors/bc/bcc创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考