云网牛站
所在位置:首页 > Linux编程 > 在Fedora中安装和使用bpftrace来跟踪代码

在Fedora中安装和使用bpftrace来跟踪代码

2019-08-09 15:38:27作者:戴进稿源:云网牛站

bpftrace是一个新的基于eBPF的跟踪工具,最初包含在Fedora 28中,通过跟踪工具,您可以分析系统幕后的功能,它告诉您调用代码中的哪些函数、使用哪些参数、多少次等等。本文介绍了有关bpftrace的一些基础知识及其工作原理,请继续阅读以获取更多信息和一些有用的示例。

在Fedora中安装和使用bpftrace来跟踪代码

 

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

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

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

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

 

安装和快速启动

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

$ sudo dnf install bpftrace

参考:配置Fedora系统以使用sudo的方法

尝试使用“hello world”示例:

$ sudo bpftrace -e 'BEGIN { printf("hello world\n"); }'

请注意,由于所需的权限,您必须以root身份运行bpftrace,使用-e选项指定程序,并构造所谓的“one-liners”,此示例仅打印hello world,然后等待您按Ctrl+C。

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

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

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

此示例打印父进程名称(comm)以及系统中正在创建的每个新进程的名称,t:syscalls:sys_enter_execve是内核跟踪点,它是tracepoint的简写:syscalls:sys_enter_execve,但可以使用这两种形式。

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

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

bpftrace -lv "t:syscalls:sys_enter_execve"

 

用法示例

1、列出探针

bpftrace的核心是探测点,探测点是可以附加eBPF程序的代码(内核或用户空间)中的检测点,它们符合以下类别:

kprobe  - 内核函数启动。

kretprobe  - 内核函数返回。

uprobe  - 用户级功能启动。

uretprobe  - 用户级函数返回。

tracepoint  - 内核静态跟踪点。

usdt  - 用户级静态跟踪点。

profile - 定时抽样。

interval - 定时输出。

software - 内核软件事件。

hardware - 处理器级事件。

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

$ sudo bpftrace -l

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

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

2、计算系统调用

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

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

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

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

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

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

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

3、按进程写入字节

使用这些概念,让我们分析每个进程写入的字节数:

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

bpftrace将操作块附加到write syscall return probe(t:syscalls:sys_exit_write),然后,它使用过滤器来丢弃负值,这些值是错误代码(/args->ret > 0/)。

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

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

bpftrace支持直方图的创建,让我们分析一个通过进程创建读取大小分布直方图的示例:

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

直方图是BPF贴图,因此它们必须始终归因于贴图(@),在此示例中,映射键是comm。

该示例使bpftrace为调用read syscall的每个进程生成一个直方图,要生成一个全局直方图,将hist()函数归属为'@'(不带任何键)。

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

5、跟踪用户空间程序

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

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

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

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

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

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

6、使用脚本

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

其中,你可以找到:

killsnoop.bt  - 由kill()系统调用发出的跟踪信号。

tcpconnect.bt  - 跟踪所有TCP网络连接。

pidpersec.bt  - 每秒计算新的过程(通过fork)。

opensnoop.bt  - 跟踪open()系统调用。

vfsstat.bt  - 使用每秒摘要计算一些VFS调用。

您可以直接使用脚本,例如:

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

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

 

相关主题

在Ubuntu18.04系统中向GitHub提交代码

精选文章
热门文章