Linux bpftrace 简介

新的 Linux 跟踪器分析生产环境性能问题并排除软件故障。
145 位读者喜欢这篇文章。
Linux keys on the keyboard for a desktop computer

Bpftrace 是一款新的 Linux 开源跟踪器,用于分析生产环境性能问题和排除软件故障。它的用户和贡献者包括 Netflix、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[tid]/ { @ns = hist(nsecs - @start[tid]); delete(@start[tid]); }'
Attaching 2 probes...
^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 单行命令可以帮助你了解问题所在。对于大型环境,此功能可以帮助你节省数百万美元。对于较小的环境,它可以更有效地帮助消除延迟异常值。

我之前写过关于 bpftrace 与其他跟踪器的比较,包括 BCC (BPF Compiler Collection)。BCC 非常适合预定义的复杂工具和代理。Bpftrace 最适合短脚本和临时调查。在本文中,我将总结 bpftrace 语言、变量类型、探针和工具。

Bpftrace 使用 BPF (Berkeley Packet Filter),这是一种内核执行引擎,可处理虚拟指令集。近年来,BPF 已得到扩展(又名 eBPF),以提供一种安全的方式来扩展内核功能。它也已成为系统工程中的热门话题,在最近的 Linux Plumber's Conference 上至少有 24 场关于 BPF 的演讲。BPF 在 Linux 内核中,而 bpftrace 是开始使用 BPF 进行可观测性的最佳方式。

请参阅 bpftrace INSTALL 指南了解如何安装它,并获取最新版本;0.9.2 刚刚发布。对于 Kubernetes 集群,还有 kubectl-trace 用于运行它。

语法

probe[,probe,...] /filter/ { action }

探针指定要检测的事件。过滤器是可选的,可以根据布尔表达式过滤事件,而操作是运行的迷你程序。

这是 hello world

# bpftrace -e 'BEGIN { printf("Hello eBPF!\n"); }'

探针是 BEGIN,这是一个特殊的探针,在程序开始时运行(如 awk)。没有过滤器。操作是 printf() 语句。

现在看一个真实的例子

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

这使用 kretprobe 来检测 sys_read() 内核函数的返回。如果 PID 为 181,则使用 sys_read() 的返回值 retval 通过 log2 直方图函数填充特殊映射变量 @bytes。这将生成 PID 181 返回的读取大小的直方图。你的应用程序是否执行大量单字节读取?也许可以对此进行优化。

探针类型

这些是相关探针的库。当前支持的类型是(将添加更多)

类型 描述
tracepoint 内核静态检测点
usdt 用户级静态定义的跟踪
kprobe 内核动态函数检测
kretprobe 内核动态函数返回检测
uprobe 用户级动态函数检测
uretprobe 用户级动态函数返回检测
software 基于内核软件的事件
hardware 基于硬件计数器的检测
watchpoint 内存监视点事件(开发中)
profile 跨所有 CPU 的定时采样
interval 定时报告(来自一个 CPU)
BEGIN bpftrace 开始
END bpftrace 结束

动态检测(又名动态跟踪)是一种超能力,可让你在运行的二进制文件中跟踪任何软件功能,而无需重新启动它。这使你可以彻底解决几乎所有问题。但是,它公开的函数不被视为稳定的 API,因为它们可能会在一个软件版本更改为另一个软件版本时发生变化。因此,静态检测,其中事件点被硬编码并成为稳定的 API。当你编写 bpftrace 程序时,请尝试先使用静态类型,然后再使用动态类型,以便你的程序更稳定。

变量类型

变量 描述
@name 全局
@name[key] 哈希
@name[tid] 线程本地
$name scratch

带有 @ 前缀的变量使用 BPF 映射,其行为类似于关联数组。它们可以通过两种方式填充

  • 变量赋值:@name = x;
  • 函数赋值:@name = hist(x);

内置了各种映射填充函数,以提供快速汇总数据的方法。

内置变量和函数

这里列出了一些内置变量和函数,但还有更多。

内置变量

变量 描述
pid 进程 ID
comm 进程或命令名称
nsecs 当前时间(纳秒)
kstack 内核堆栈跟踪
ustack 用户级堆栈跟踪
arg0...argN 函数参数
args Tracepoint 参数
retval 函数返回值
name 完整探针名称

内置函数

函数 描述
printf("...") 打印格式化字符串
time("...") 打印格式化时间
system("...") 运行 shell 命令
@ = count() 计数事件
@ = hist(x) x 的二次幂直方图
@ = lhist(x, min, max, step) x 的线性直方图

有关详细信息,请参阅 参考指南

单行命令教程

学习 bpftrace 的一个好方法是通过单行命令,我将其变成了一个 单行命令教程,其中涵盖了以下内容

列出探针 bpftrace -l 'tracepoint:syscalls:sys_enter_*'
Hello world bpftrace -e 'BEGIN { printf("hello world\n") }'
文件打开 bpftrace -e 'tracepoint:syscalls:sys_enter_open { printf("%s %s\n", comm, str(args->filename)) }'
按进程的系统调用计数 bpftrace -e 'tracepoint:raw_syscalls:sys_enter { @[comm] = count() }'
read() 字节的分布 bpftrace -e 'tracepoint:syscalls:sys_exit_read /pid == 18644/ { @bytes = hist(args->retval) }'
read() 字节的内核动态跟踪 bpftrace -e 'kretprobe:vfs_read { @bytes = lhist(retval, 0, 2000, 200) }'
计时 read() bpftrace -e 'kprobe:vfs_read { @start[tid] = nsecs } kretprobe:vfs_read /@start[tid]/ { @ns[comm] = hist(nsecs - @start[tid]); delete(@start[tid]) }'
计数进程级事件 bpftrace -e 'tracepoint:sched:sched* { @[name] = count() } interval:s:5 { exit() }'
分析 CPU 内核堆栈 bpftrace -e 'profile:hz:99 { @[stack] = count() }'
调度器跟踪 bpftrace -e 'tracepoint:sched:sched_switch { @[stack] = count() }'
块 I/O 跟踪 bpftrace -e 'tracepoint:block:block_rq_issue { @ = hist(args->bytes); }
内核结构跟踪(一个脚本,不是单行命令) 命令: bpftrace path.bt, 其中 path.bt 文件是



#include <linux/path.h>

#include <linux/dcache.h>




kprobe:vfs_open { printf("open path: %s\n", str(((path *)arg0)->dentry->d_name.name)); }

有关每个命令的说明,请参阅教程。

提供的工具

除了单行命令外,bpftrace 程序还可以是多行脚本。Bpftrace 附带了 28 个作为工具

bpftrace/eBPF tools

这些可以在 /tools 目录中找到

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 的方法。以下是一些示例。

源代码

这是 biolatility.bt 的代码

tools# cat -n biolatency.bt
     1	/*
     2	 * biolatency.bt    Block I/O latency as a histogram.
     3	 *                  For Linux, uses bpftrace, eBPF.
     4	 *
     5	 * This is a bpftrace version of the bcc tool of the same name.
     6	 *
     7	 * Copyright 2018 Netflix, Inc.
     8	 * Licensed under the Apache License, Version 2.0 (the "License")
     9	 *
    10	 * 13-Sep-2018	Brendan Gregg	Created this.
    11	 */
    12
    13	BEGIN
    14	{
    15		printf("Tracing block device I/O... Hit Ctrl-C to end.\n");
    16	}
    17
    18	kprobe:blk_account_io_start
    19	{
    20		@start[arg0] = nsecs;
    21	}
    22
    23	kprobe:blk_account_io_done
    24	/@start[arg0]/
    25
    26	{
    27		@usecs = hist((nsecs - @start[arg0]) / 1000);
    28		delete(@start[arg0]);
    29	}
    30	
    31	END
    32	{
    33		clear(@start);
    34	}

它非常简单、易于阅读且足够短,可以包含在幻灯片中。此版本使用内核动态跟踪来检测 blk_account_io_start()blk_account_io_done() 函数,并在它们之间传递一个时间戳,该时间戳以 arg0 为键。kprobe 上的 arg0 是该函数的第一个参数,它是 struct request *,其内存地址用作唯一标识符。

示例文件

你可以在 GitHub 存储库 中以 *_example.txt 文件形式查看这些工具的屏幕截图和说明。例如 example

tools# more biolatency_example.txt
Demonstrations of biolatency, the Linux BPF/bpftrace version.


This traces block I/O, and shows latency as a power-of-2 histogram. For example:

# biolatency.bt
Attaching 3 probes...
Tracing block device I/O... Hit Ctrl-C to end.
^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 |@                                                   |

While tracing, this shows that 426 block I/O had a latency of between 1K and 2K
usecs (1024 and 2048 microseconds), which is between 1 and 2 milliseconds.
There are also two modes visible, one between 1 and 2 milliseconds, and another
between 8 and 16 milliseconds: this sounds like cache hits and cache misses.
There were also 10 I/O with latency 128 to 256 ms: outliers. Other tools and
instrumentation, like biosnoop.bt, can shed more light on those outliers.
[...]

有时,在尝试理解这些工具时,直接切换到示例文件可能是最有效的,因为输出可能是显而易见的(按设计!)。

手册页

GitHub 存储库的 /man/man8 下的每个工具都有手册页。它们包括有关输出字段和工具的预期开销的部分。

# nroff -man man/man8/biolatency.8
biolatency(8)               System Manager's Manual              biolatency(8)



NAME
       biolatency.bt - Block I/O latency as a histogram. Uses bpftrace/eBPF.

SYNOPSIS
       biolatency.bt

DESCRIPTION
       This  tool  summarizes  time  (latency) spent in block device I/O (disk
       I/O) as a power-of-2 histogram. This  allows  the  distribution  to  be
       studied,  including  modes and outliers. There are often two modes, one
       for device cache hits and one for cache misses, which can be  shown  by
       this tool. Latency outliers will also be shown.
[...]

编写所有这些手册页是开发这些工具中最无趣的部分,有些手册页的编写时间比工具的开发时间还长,但很高兴看到最终结果。

bpftrace vs. BCC

由于 eBPF 已合并到内核中,因此大部分精力都放在 BCC 前端,它提供了用于编写程序的 BPF 库以及 Python、C++ 和 Lua 接口。我已经在 BCC/Python 中开发了很多 工具;它工作得很好,尽管 BCC 中的编码很冗长。如果你正在解决性能问题,bpftrace 更适合你的一次性自定义查询。如果你正在编写一个具有许多命令行选项或使用 Python 库的代理的工具,你将需要考虑使用 BCC。

在 Netflix 性能团队中,我们同时使用两者:BCC 用于开发其他人可以轻松使用的预定义工具和开发代理;bpftrace 用于临时分析。网络工程团队一直在使用 BCC 开发满足其需求的代理。安全团队对 bpftrace 最感兴趣,因为它可用于快速临时检测零日漏洞。我预计开发团队将在不知情的情况下同时使用两者,通过我们正在构建的自助服务 GUI (Vector),并且偶尔可能会 SSH 到实例中并运行预定义工具或临时 bpftrace 单行命令。

了解更多

我今年还将出版一本关于 bpftrace 的书:BPF Performance Tools: Linux System and Application Observability,将由 Addison Wesley 出版,其中包含许多新的 bpftrace 工具。


感谢 Alastair Robertson 创建了 bpftrace,并感谢 bpftrace、BCC 和 BPF 社区在过去五年中所做的所有工作。

标签
User profile image.
Brendan Gregg 是 Netflix 的高级性能架构师,他在那里从事大规模计算机性能设计、分析和调优。

1 条评论

我喜欢你的文章,谢谢分享!

Creative Commons License本作品根据 Creative Commons Attribution-Share Alike 4.0 International License 获得许可。
© . All rights reserved.