bpftrace 全面介绍

最初发布于 https://opensource.com/article/19/8/introduction-bpftrace。
bpftrace 是一个新的 Linux 开源跟踪器,用于分析生产性能问题和故障排除软件。它被包括 Netfilx、Facebook、Red Hat、Shopify 等在内的许多公司使用并得到了贡献。它由 Alastair Robertson 创建,他是一位才华横溢的英国开发人员,此前曾赢得各种编码比赛。
Linux 已经有许多性能工具,但这些工具通常是基于计数器的,并且可见性有限。例如,iostat(1) 或监控代理可能会告诉您平均磁盘延迟,但不会告诉您该延迟的分布情况。分布可以揭示多种模式或异常值,其中任何一种都可能是性能问题的真正原因。bpftrace 适合这种分析:将指标分解为分布或每个事件的日志,并创建新指标以查看盲点。
您可以通过单行或脚本使用 bpftrace,它附带了许多预先编写的工具。这是一个示例屏幕截图:跟踪 PID 181 的读取延迟分布,并将其显示为二次方直方图:

bpftrace -e 'kprobe:vfs_read /pid == 30153/ { @start[tid] = nsecs; }

kretprobe:vfs_read /@start[time]/ { @ns = hist(nsecs - @start[time]); 删除(@开始[时间]);}’
连接 2 个探头…
^C

@ns
[256, 512) 10900 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ |
[512, 1k) 18291 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ @@@@@@@@|
[1k, 2k) 4998 |@@@@@@@@@@@@@@@ |
[2k, 4k) 57 | |
[4k, 8k) 117 | |
[8k, 16k) 48 | |
[16k, 32k) 109 | |
[32k, 64k) 3 | |
此示例检测了数千个可用事件之一。如果你有一些奇怪的性能问题,可能有一些 bpftrace one-liner 可以阐明它。对于大型环境,此功能可以帮助您节省数百万美元。对于较小的环境,它可能更有用,有助于消除延迟异常值。
我之前写过关于 bpftrace 与其他跟踪器的对比,包括BCC(BPF 编译器集合)。BCC 非常适合罐装复杂工具和代理。bpftrace 最适合短脚本和临时调查。在这篇文章中,我将总结 bpftrace 语言、变量类型、探针和工具。
bpftrace 使用 BPF(伯克利包过滤器),这是一种处理虚拟指令集的内核执行引擎。近年来,BPF 得到了扩展(又名 eBPF),以提供一种扩展内核功能的安全方式,并已成为系统工程的热门话题,在上一次 Linux Plumber 会议上至少有 24 场关于 BPF 的演讲。BPF 在 Linux 内核中,而 bpftrace 是开始使用 BPF 进行可观察性的最佳方式。
有关如何安装它的信息,请参阅 bpftrace INSTALL指南,并获取最新版本: 0.9.1刚刚发布。对于 Kubernetes 集群,还有kubectl-trace用于运行它。
句法
探针[,探针,…] /filter/ { 动作 }
The probe specifies what events to instrument, the filter is optional and can filter down the events based on a boolean expression, and the action is the mini program that runs.
Here’s hello world:

bpftrace -e ‘BEGIN { printf(“Hello eBPF!n”); }’

The probe is BEGIN, a special probe that runs at the beginning of the program (like awk). There’s no filter. The action is a printf() statement.
Now a real example:

bpftrace -e ‘kretprobe:sys_read /pid == 181/ { @bytes = hist(retval); }’

This uses a kretprobe to instrument the return of the sys_read() kernel function. If the PID is 181, a special map variable @bytes is populated with a log2 histogram function with the return value retval of sys_read(). This produces a histogram of the returned read size for PID 181. Is your app doing lots of 1 byte reads? Maybe that can be optimized.
Probe Types
These are libraries of probes which are related. The currently supported types are (more will be added):
Type Description
tracepoint Kernel static instrumentation points
usdt User-level statically defined tracing
kprobe Kernel dynamic function instrumentation
kretprobe Kernel dynamic function return instrumentation
uprobe User-level dynamic function instrumentation
uretprobe User-level dynamic function return instrumentation
software 基于内核软件的事件
硬件 基于硬件计数器的仪器
观察点 内存观察点事件(开发中)
轮廓 跨所有 CPU 的定时采样
间隔 定时报告(来自一个 CPU)
开始 bpftrace 的开始
结尾 bpftrace 结束
如果您不熟悉这个术语:动态检测(也称为动态跟踪)是一种超级强大的功能,可让您在运行的二进制文件中跟踪任何软件功能,而无需重新启动它。这可以让您查明任何问题的根源。但是,它公开的功能不被视为稳定的 API,因为它们可以从一个软件版本更改为另一个版本。因此,静态检测,其中事件点被硬编码并成为一个稳定的 API。在编写 bpftrace 程序时,请尝试先使用静态类型,然后再使用动态类型,这样您的程序会更稳定。
变量类型
多变的 描述
@姓名 全球的
@名称[键] 哈希
@姓名[时间] 线程本地
$名称 刮
带有“@”前缀的变量使用 BPF 映射,其行为类似于关联数组。它们可以通过以下两种方式之一填充:
变量赋值:@name = x;
函数赋值:@name = hist(x);
有各种地图填充函数作为内置函数,可提供快速汇总数据的方法。
内置变量和函数
我会在这里提到一些来帮助介绍 bpftrace,但还有更多。
内置变量包括:
多变的 描述
PID 进程号
通讯 进程或命令名称
纳秒 当前时间(以纳秒为单位)
堆栈 内核堆栈跟踪
ustack 用户级堆栈跟踪
arg0…argN 函数参数
参数 跟踪点参数
逆转录 函数返回值
姓名 完整的探针名称
内置函数包括:
功能 描述
printf(“…”) 打印格式化字符串
时间(”…”) 打印格式化时间
系统(”…”) 运行外壳命令
@ = 计数() 计数事件
@ = 历史 (x) x 的 2 次方直方图
@ = lhist(x, min, max, step) x 的线性直方图
有关所有内容,请参阅参考指南。
单线教程
学习 bpftrace 的一个好方法是通过 one-liners,我把它变成了one-liners 教程。该教程涵盖以下单行:

  1. 列出探测
    bpftrace -l ‘tracepoint:syscalls:sys_enter_*’

2.你好世界
bpftrace -e ‘BEGIN { printf(“hello worldn”) }’

  1. 文件打开
    bpftrace -e ‘tracepoint:syscalls:sys_enter_open { printf(“%s %sn”, comm, str(args->filename)) }’

  2. 系统调用按进程计数
    bpftrace -e ‘tracepoint:raw_syscalls:sys_enter { @[comm] = count() }’

  3. read() 字节的分布
    bpftrace -e ‘tracepoint:syscalls:sys_exit_read /pid == 18644/ { @bytes = hist(args->retval) }’

  4. read() 字节的内核动态跟踪
    bpftrace -e ‘kretprobe:vfs_read { @bytes = lhist(retval, 0, 2000, 200) }’

  5. 定时 read()s
    bpftrace -e ‘kprobe:vfs_read { @start[tid] = nsecs }
    kretprobe:vfs_read /@start[tid]/ { @ns[comm] = hist(nsecs - @start[tid]); 删除(@start[tid])}’

  6. 统计进程级事件
    bpftrace -e ‘tracepoint:sched:sched* { @[name] = count() } interval:s:5 { exit() }’

  7. 分析 CPU 内核堆栈
    bpftrace -e ‘profile:hz:99 { @[stack] = count() }’

10.调度器跟踪
bpftrace -e ‘tracepoint:sched:sched_switch { @[stack] = count() }’

  1. 块 I/O 跟踪
    bpftrace -e ‘tracepoint:block:block_rq_complete { @ = hist(args->nr_sector * 512) }’
    请参阅教程以获取对这些内容的解释。
    提供的工具
    除了单行,bpftrace 程序还可以是多行脚本。bpftrace 附带了其中的 28 个工具:
    这些工具可以在 /tools 目录中找到:
    工具#ls .bt
    bashreadline.bt dcsnoop.bt oomkill.bt syncsnoop.bt vfscount.bt
    biolatency.bt execsnoop.bt opensnoop.bt syscount.bt vfsstat.bt
    biosnoop.bt gethostlatency.bt pidpersec.bt tcpaccept.bt writeback.bt
    bitesize.bt killsnoop.bt runqlat.bt tcpconnect.bt xfsdist.bt
    capable.bt loads.bt runqlen.bt tcpdrop.bt
    cpuwalk.bt mdflush.bt statsnoop.bt tcpretrans.bt
    除了用于诊断性能问题和一般故障排除之外,它们还提供了另一种学习 bpftrace 的方法:通过示例。
    资源
    这是 biolatency.bt 的代码:
    工具# cat -n biolatency.bt
    1 /

    2 * biolatency.bt 块 I/O 延迟作为直方图。
    3 * 对于 Linux,使用 bpftrace、eBPF。
    4 *
    5 * 这是同名密件抄送工具的bpftrace版本。
    6 *
    7 * 版权所有 2018 Netflix, Inc.
    8 * 根据 Apache 许可证 2.0 版(“许可证”)获得许可
    9 *
    10 * 2018 年 9 月 13 日 Brendan Gregg 创建了这个。
    11 */
    12
    13 开始
    14 {
    15 printf(“跟踪块设备 I/O… 按 Ctrl-C 结束。n”);
    16 }
    17
    18 kprobe:blk_account_io_start
    19 {
    20 @start[arg0] = nsecs;
    21 }
    22
    23 kprobe:blk_account_io_done
    24 /@开始[arg0]/
    25
    26 {
    27 @usecs = hist((nsecs - @start[arg0]) / 1000);
    28 删除(@start[arg0]);
    29 }
    30
    31 结束
    32 {
    33 清除(@开始);
    34 }
    它简单易读,并且足够短,可以包含在幻灯片中。此版本使用内核动态跟踪来检测 blk_account_io_start() 和 blk_account_io_done() 函数,并将它们之间的时间戳传递给每个函数。kprobe 上的 arg0 是该函数的第一个参数,对于这些是 struct request *,它的内存地址用作唯一标识符。
    例子
    *_example.txt 文件中还提供了这些工具的屏幕截图,我在其中准确解释了我们所看到的内容。例如:
    工具#更多 biolatency_example.txt
    生物延迟演示,Linux BPF/bpftrace 版本。

这会跟踪块 I/O,并将延迟显示为 2 的幂直方图。例如:

biolatency.bt

连接 3 个探头…
跟踪块设备 I/O… 按 Ctrl-C 结束。
^C

@usecs
[256, 512) 2 | |
[512, 1K) 10 |@ |
[1K, 2K) 426 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ @@@@@@@@@|
[2K, 4K) 230 |@@@@@@@@@@@@@@@@@@@@@@@@@@@|
[4K、8K) 9 |@ |
[8K, 16K) 128 |@@@@@@@@@@@@@@@@ |
[16K, 32K) 68 |@@@@@@@@@|
[32K, 64K) 0 | |
[64K, 128K) 0 | |
[128K, 256K) 10 |@ |

在跟踪时,这表明 426 块 I/O 的延迟在 1K 到 2K 之间
usecs(1024 和 2048 微秒),介于 1 到 2 毫秒之间。
还有两种可见的模式,一种在 1 到 2 毫秒之间,另一种
8 到 16 毫秒之间:这听起来像是缓存命中和缓存未命中。
还有 10 个 I/O 延迟为 128 到 256 毫秒:异常值。其他工具和
仪器,如 biosnoop.bt,可以更多地了解这些异常值。
[…]
有时,在尝试理解这些工具时直接切换到示例文件可能是最有效的,因为输出可能是不言而喻的(设计使然!)。
手册页
在 /man/man8 下也有每个工具的手册页。它们包括关于输出字段的部分,以及工具的预期开销。

nroff -man man/man8/biolatency.8

biolatency(8) 系统管理员手册 biolatency(8)

姓名
biolatency.bt - 块 I/O 延迟作为直方图。使用 bpftrace/eBPF。

概要
biolatency.bt

描述
该工具总结了在块设备 I/O(磁盘
I/O) 作为 2 的幂直方图。这允许分布是
研究,包括模式和异常值。通常有两种模式,一种
用于设备缓存命中和一个用于缓存未命中,可以显示为
这个工具。延迟异常值也将显示。
[…]
编写所有这些手册页是开发这些工具最不有趣的部分,在某些情况下,编写工具的时间比开发工具所用的时间长,但很高兴看到最终结果。
bpftrace 与密件抄送
自从 eBPF 合并到内核中以来,大部分精力都集中在BCC前端,它提供了 BPF 库和 Python、C++ 和 lua 接口来编写程序。我在 BCC/python 中开发了很多工具,并且效果很好,尽管 BCC 中的编码很冗长。如果您正在解决性能问题,那么 bpftrace 更适合您拥有的所有一次性自定义查询。如果您正在编写具有许多命令行选项的工具或使用 Python 库的代理,您将需要考虑使用 BCC。
以下是 Netflix 将如何使用这些:在性能团队中,我同时使用:BCC 用于开发其他人可以轻松使用的罐装工具,以及用于开发代理;和 bpftrace 用于临时分析。例如,在网络工程团队中,他们一直在使用 BCC 来开发满足其需求的代理。安全团队最感兴趣的是 bpftrace 用于快速检测零日漏洞的临时工具。对于开发人员团队:我希望他们会通过我们正在构建的自助式 GUI(Vector)在不知情的情况下使用这两者,并且偶尔可能会通过 ssh 连接到一个实例并运行一个罐装工具或临时 bpftrace 单线。
更多 bpftrace 阅读
github 上的bpftrace存储库
bpftrace单行教程
bpftrace参考指南
用于更复杂的基于 BPF 的工具的BCC存储库
今年我还出版了一本书,涵盖了 bpftrace:BPF 性能工具:Linux 系统和应用程序可观察性,由 Addison Wesley 出版,其中包含许多新的 bpftrace 工具。
感谢 Alastair Robertson 创建 bpftrace,以及 bpftrace、BCC 和 BPF 社区在过去五年中所做的所有工作。

转载翻译自: https://www.brendangregg.com