跟踪代码 Fedora 使用 bpftrace

bpftrace 是 一个新的基于 eBPF 的跟踪工具 最初包含在 Fedora 28. 它是由 Brendan Gregg、Alastair Robertson 和 Matheus Marchini 在网络上松散的黑客团队的帮助下开发的。 跟踪工具可让您分析系统在幕后所做的事情。 它告诉您代码中的哪些函数被调用、使用哪些参数、调用了多少次等等。

本文介绍了有关 bpftrace 的一些基础知识,以及它的工作原理。 继续阅读以获取更多信息和一些有用的示例。

eBPF(扩展伯克利包过滤器)

eBPF 是 Linux 内核中的一个微型虚拟机,或者更准确地说是一个虚拟 CPU。 eBPF 可以在内核空间中以安全可控的方式加载和运行小程序。 这使得它使用起来更安全,即使在生产系统中也是如此。 这个虚拟机有自己的指令集架构(ISA) 类似于现代处理器架构的一个子集。 ISA 使将这些程序转换为真实硬件变得容易。 内核对主要架构的本机代码执行即时转换,以提高性能。

eBPF 虚拟机允许以编程方式扩展内核。 现在有几个内核子系统利用了这种新的强大的 Linux 内核功能。 示例包括网络、seccomp、跟踪等。 主要思想是将 eBPF 程序附加到特定的代码点,从而扩展原始内核行为。

eBPF 机器语言非常强大。 但是直接用它写代码是非常痛苦的,因为它是一种低级语言。 这就是 bpftrace 的用武之地。它提供了一种高级语言来编写 eBPF 跟踪脚本。 然后,该工具在 clang/LLVM 库的帮助下将这些脚本转换为 eBPF,然后附加到指定的代码点。

安装和快速启动

要安装 bpftrace,请使用以下命令在终端中运行 sudo:

$ sudo dnf install bpftrace

试试“hello world” example:

$ sudo bpftrace -e 'BEGIN { printf("hello worldn"); }'

请注意,由于需要特权,您必须以 root 身份运行 bpftrace。 使用 -e 选项指定一个程序,并构造所谓的“单线”。 这 example 只打印hello world,然后等你按 Ctrl+C.

BEGIN 是一个特殊的探针名称,在执行开始时只触发一次。 每当探针被命中时,花括号 { } 内的每个动作都会触发——在这种情况下,它只是一个 printf。

现在让我们跳到一个更有用的 example:

$ sudo bpftrace -e 't:syscalls:sys_enter_execve { printf("%s called %sn", comm, str(args->filename)); }'

这 example 打印父进程名称 (comm) 和系统中正在创建的每个新进程的名称。 t:syscalls:sys_enter_execve 是内核跟踪点。 它是 tracepoint:syscalls:sys_enter_execve 的简写,但两种形式都可以使用。 下一节将向您展示如何列出所有可用的跟踪点。

comm 是一个表示进程名称的 bpftrace 内置函数。 文件名是 t:syscalls:sys_enter_execve 跟踪点的一个字段。 您可以通过 args 内置访问这些字段。

可以使用以下命令列出跟踪点的所有可用字段:

bpftrace -lv "t:syscalls:sys_enter_execve"

示例用法

列出探针

bpftrace 的核心概念是 探测点. 探测点是代码(内核或用户空间)中可以附加 eBPF 程序的检测点。 它们属于以下类别:

  • kprobe – 内核函数启动
  • kretprobe – 内核函数返回
  • uprobe – 用户级函数启动
  • uretprobe – 用户级函数返回
  • tracepoint – 内核静态跟踪点
  • usdt – 用户级静态跟踪点
  • profile – 定时采样
  • 间隔 – 定时输出
  • 软件——内核软件事件
  • 硬件 – 处理器级事件

所有可用的 kprobe/kretprobe、跟踪点、软件和硬件探针都可以使用以下命令列出:

$ sudo bpftrace -l

uprobe/uretprobe 和 usdt 探测器是特定于给定可执行文件的用户空间探测器。 要使用它们,请使用本文后面显示的特殊语法。

配置文件和间隔探测器以固定的时间间隔触发。 本文不涉及固定时间间隔。

计算系统调用

地图 是存储计数、统计和直方图的特殊 BPF 数据类型。 您可以使用映射来总结每个系统调用被调用的次数:

$ sudo bpftrace -e 't:syscalls:sys_enter_* { @[probe] = count(); }'

某些探测类型允许通配符匹配多个探测。 您还可以使用逗号分隔列表为操作块指定多个附加点。 在这个 example,操作块附加到名称以 t:syscalls:sys_enter_ 开头的所有跟踪点,这意味着所有可用的系统调用。

bpftrace 内置函数 count() 计算调用此函数的次数。 @[] 表示一个映射(一个关联数组)。 该映射的键是probe,它是另一个bpftrace 内置函数,表示完整的probe 名称。

在这里,每个系统调用都附加了相同的操作块。 然后,每次调用系统调用时,都会更新映射,并且映射中的条目相对于同一系统调用递增。 当程序终止时,它会自动打印出所有声明的映射。

这 example 计算全局调用的系统调用,也可以使用 bpftrace 过滤器语法通过 PID 过滤特定进程:

$ sudo bpftrace -e 't:syscalls:sys_enter_* / pid == 1234 / { @[probe] = count(); }'

按进程写入字节

使用这些概念,让我们分析每个进程正在写入多少字节:

$ sudo bpftrace -e 't:syscalls:sys_exit_write /args->ret > 0/ { @[comm] = sum(args->ret); }'

bpftrace 将操作块附加到写入系统调用返回探针 (t:syscalls:sys_exit_write)。 然后,它使用过滤器丢弃负值,即错误代码 (/args->ret > 0/)。

映射键 comm 表示调用系统调用的进程名称。 sum() 内置函数累积为每个映射条目或进程写入的字节数。 args 是一个 bpftrace 内置函数,用于访问跟踪点的参数和返回值。 最后,如果成功,write 系统调用会返回写入的字节数。 args->ret 提供对字节的访问。

按进程读取大小分布(直方图):

bpftrace 支持创建直方图。 我们来分析一个 example 按进程创建读取大小分布的直方图:

$ sudo bpftrace -e 't:syscalls:sys_exit_read { @[comm] = hist(args->ret); }'

直方图是 BPF 映射,因此它们必须始终归属于映射 (@)。 在这个 example映射键为 comm。

这 example 使 bpftrace 为调用 read 系统调用的每个进程生成一个直方图。 要仅生成一个全局直方图,只需将 hist() 函数归因于“@”(没有任何键)。

当程序终止时,bpftrace 会自动打印出声明的直方图。 用作创建直方图的基础的值是读取字节数,通过 args->ret 找到。

跟踪用户空间程序

您还可以使用 uprobes/uretprobes 和 USDT(用户级静态定义跟踪)跟踪用户空间程序。 下一个 example 使用 uretprobe,它探测到用户级函数的末尾。 它获取在每个 bash 在系统中运行:

$ sudo bpftrace -e 'uretprobe:/bin/bash:readline { printf("readline: "%s"n", str(retval)); }'

列出所有可用的 uprobes/uretprobes bash 可执行文件,运行以下命令:

$ sudo bpftrace -l "uprobe:/bin/bash"

uprobe 检测用户级函数执行的开始,而 uretprobe 检测结束(返回)。 readline() 是 /bin/ 的函数bash,并返回键入的命令行。 retval 是检测函数的返回值,只能在 uretprobe 上访问。

使用 uprobes 时,您可以使用 arg0..argN 访问参数。 必须调用 str() 才能将 char * 指针转换为字符串。

交付的脚本

bpftrace 包附带了许多有用的脚本。 您可以在 /usr/share/bpftrace/tools/ 目录中找到它们。

其中,您可以找到:

  • killsnoop.bt – 由 kill() 系统调用发出的跟踪信号。
  • tcpconnect.bt – 跟踪所有 TCP 网络连接。
  • pidpersec.bt – 每秒计算新进程(通过 fork)。
  • opensnoop.bt – 跟踪 open() 系统调用。
  • vfsstat.bt – 统计一些 VFS 调用,并带有每秒摘要。

您可以直接使用脚本。 为了 example:

$ sudo /usr/share/bpftrace/tools/killsnoop.bt

您还可以在创建新工具时研究这些脚本。

链接

摄影者 罗曼·罗马绍夫不飞溅.