我是靠谱客的博主 爱听歌果汁,最近开发中收集的这篇文章主要介绍SPDK的BPF Tracing,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

在v21.07 release发布版本中,SPDK提供了对BPF追踪(tracing)的支持。BPF追踪指一组定义在 SPDK 库的脚本和静态探针,为用户提供了检查 SPDK 应用程序的另一种方法。SPDK很久以前就建立过一个名为libtrace的追踪库。支持BPF追踪并不是要取代libtrace,而是要对它进行补充。

这两个库的功能略有不同:libtrace的开销很低,但灵活性较差。BPF追踪的成本较高(请求CPU核trap陷阱),但功能更全面,甚至可以动态链接,不必在代码中定义探针。

用户态追踪

首先,我们来探讨什么是BPF追踪,以及SPDK是如何利用它的。

BPF,即Berkeley Packet Filter(伯克利包过滤器),是一项最初为分析和过滤网络流量而开发的技术。用户可以借此在专门的BPF虚拟机的操作系统内核运行用户提供的程序。随后BPF在Linux中得到扩展(扩展后的BPF简称为eBPF),可支持更多用例,其中也包括追踪。

用户能够利用Linux 追踪子系统在程序的特定地方附加探针,一旦触发后会运行附加在该探针的eBPF程序[1]。在用户态应用程序中,BPF会放置一条指令(x86的int3指令),生成SIGTRAP,被CPU核捕获后,即触发执行eBPF程序。

SPDK使用BPF trace (https://github.com/iovisor/bpftrace) 来定义和附加探针。BPF追踪工具使用高级脚本语言(作者称是awk和C语言的结合)来描述特定探针被触发时的操作。

[1] eBPF介绍:https://lwn.net/Articles/740157/

探针可以分为静态定义和动态定义两种。静态探针也称为USDT(User Statically-Defined Tracing用户静态定义的探针),由程序员放置在代码的各个关键位置(使用一种SPDK_DTRACE_PROBE*宏)。动态探针可以在任何函数开始时启用,不需要更改代码或重新编译应用程序。但有一点需要注意:一些静态函数调用可能会使编译器产生inline,在这种情况下,不会照常执行探针追踪。

追踪工具和案例

为了编译静态探针,SPDK必须配置--with-usdt。我们在Ubuntu 20.04和Fedora 33的数据包版本追踪时都遇到了一些问题,因此建议从源码构建最新版本的bpftrace。可以使用scripts/bpftrace.sh来附加和显示追踪。运行scripts/bpftrace.sh需要两个参数:支持追踪的进程参数PID和支持附加的bpftrace脚本。这些脚本能够收集和显示各种不同的信息和统计数据。例如,可以输出某个函数每次被调用时的参数,计算某个代码路径被执行的次数,甚至可以创建并显示一组数据的直方图。SPDK 提供了几个可用脚本,位于 scripts/bpf 目录。

接下来我们来讨论scripts/bpf/send_msg.bt脚本,应该如何用它来检查SPDK应用程序呢?该脚本通过spdk_thread_send_msg()和spdk_for_each_channel()计算某函数的执行次数。脚本的内容简单明了:

uprobe:__EXE__:spdk_thread_send_msg {

                       @send_msg[usym(arg1)] = count();

}



uprobe:__EXE__:spdk_for_each_channel {

                      @for_each_channel[usym(arg1)] = count();

}

scripts/bpf/send_msg.bt脚本通过uprobe关键字定义了两个动态探针,分别为send_msg和for_each_channel,将每个函数的调用次数存在映射。第一个参数arg1是映射的密钥之一,在这两种探针下arg1指的都是待执行函数的指针。该脚本使用了两个辅助函数:usym()函数负责返回给定地址的符号名称,count()函数负责计算某个函数被调用的次数。有关可用函数和该脚本通用语法的更多信息,请参阅bpftrace手册[2]。

还有一个特殊变量是SPDK 特有的__EXE__。 scripts/bpftrace.sh 以可执行文件的名称来代替该变量(即符合 bpftrace预期)。__PID__变量由追踪进程的PID参数代替。这两个变量都提供了更便捷的方法,在不同的应用程序中可使用相同的脚本。更多细节请参阅SPDK文档(https://spdk.io/doc/usdt.html)。

脚本连接后就会开始运行,直到应用程序退出(或脚本停止)。这时,脚本会输出每个函数的名称,以及它被调用的次数。通过连接到一个NVMe/TCP目标,收集并输出了如下样本:

for_each_channel[nvmf_ctrlr_disconnect_io_qpairs_on_pg]: 1

@for_each_channel[_nvmf_tgt_add_transport]: 1

@for_each_channel[subsystem_state_change_on_pg]: 5



@send_msg[_nvmf_ctrlr_add_admin_qpair]: 1

@send_msg[_nvmf_subsystem_add_ctrlr]: 1

@send_msg[_nvmf_ctrlr_destruct]: 1

@send_msg[_finish_unregister]: 1

@send_msg[_nvmf_ctrlr_destruct]: 1

@send_msg[put_io_channel]: 4

@send_msg[_call_completion]: 7

@send_msg[_call_channel]: 7

@send_msg[nvmf_ctrlr_add_io_qpair]: 24

@send_msg[_nvmf_ctrlr_add_io_qpair]: 24

@send_msg[_nvmf_ctrlr_free_from_qpair]: 25

@send_msg[_nvmf_poll_group_add]: 25

@send_msg[_nvmf_transport_qpair_fini]: 25

[2]:https://github.com/iovisor/bpftrace/blob/master/man/adoc/bpftrace.adoc

其他可用的脚本可以用来追踪系统调用(syscalls.bt)、NVMeoF子系统的状态转换(nvmf.bt)、以及读取字节数的readv调用(readv.bt)。这些功能本身就提供了很多信息,同时也可以作为编写脚本的指南,追踪应用程序的不同属性。

SPDK和BPF追踪

如前所述,SPDK追踪库其中一个目的是将开销减至最小。这意味着每个追踪点只能记录最关键的数据。做到在I/O路径中放置追踪点,同时不对性能产生重大影响。但有了BPF追踪,我们可以把bpftrace探针放在对象创建的位置,收集其属性,然后用这些数据来辅助SPDK追踪。

例如,在NVMe/RDMA I/O路径中记录指针,指向可执行请求的qpair。然后可以用bpftrace来记录qpair的信息,比如它的队列ID、线程ID、子系统NQN和主机NQN,SPDK显示追踪时与指针一同显示。这就是scripts/bpf/trace.py脚本的功能。

实际操作步骤如下:首先,必须记录bpftraces(假设我们追踪的是基于NVMe/RDMA目标下的spdk_tgt应用程序)。

随后将其用于注解追踪,

将如下输出结果:

 0  39100254.090     RDMA_REQ_COMPLETING     id: r10  time: 49.692   qpair: 0x1178af0

 0  39100320.354     RDMA_REQ_COMPLETED      id: r10  time: 115.956  qpair: 0x1178af0

 0  39100494.100     RDMA_REQ_NEW            id: r11  qpair: 0x11790a0

 0  39100494.454     RDMA_REQ_NEED_BUFFER    id: r11  time: 0.354    qpair: 0x11790a0

 0  39100494.684     RDMA_REQ_RDY_TO_EXECUTE id: r11  time: 0.584    qpair: 0x11790a0

 0  39100500.546     RDMA_REQ_EXECUTING      id: r11  time: 6.446    qpair: 0x11790a0

 0  39100516.727     RDMA_REQ_EXECUTED       id: r11  time: 22.627   qpair: 0x11790a0

 0  39100516.903     RDMA_REQ_RDY_TO_COMPL   id: r11  time: 22.803   qpair: 0x11790a0

 0  39100517.333     RDMA_REQ_COMPLETING     id: r11  time: 23.233   qpair: 0x11790a0

 0  39100550.895     RDMA_REQ_COMPLETED      id: r11  time: 56.795   qpair: 0x11790a0

转换到:

 0  39100254.090     RDMA_REQ_COMPLETING     id: r10  time: 49.692   qpair(ptr=0x1178af0, thread=2, qid=1, subnqn=nqn.2016-06.io.spdk:cnode0, hostnqn=nqn.2014-08.org.nvmexpress:uuid:8da29bed555a45e8b9bc378e115f4c2)

 0  39100320.354     RDMA_REQ_COMPLETED      id: r10  time: 115.956  qpair(ptr=0x1178af0, thread=2, qid=1, subnqn=nqn.2016-06.io.spdk:cnode0, hostnqn=nqn.2014-08.org.nvmexpress:uuid:8da29bed555a45e8b9bc378e115f4c2)

 0  39100494.100     RDMA_REQ_NEW            id: r11  qpair(ptr=0x11790a0, thread=2, qid=2, subnqn=nqn.2016-06.io.spdk:cnode0, hostnqn=nqn.2014-08.org.nvmexpress:uuid:8da29bed555a45e8b9bc378e115f4c2)

 0  39100494.454     RDMA_REQ_NEED_BUFFER    id: r11  time: 0.354    qpair(ptr=0x11790a0, thread=2, qid=2, subnqn=nqn.2016-06.io.spdk:cnode0, hostnqn=nqn.2014-08.org.nvmexpress:uuid:8da29bed555a45e8b9bc378e115f4c2)

 0  39100494.684     RDMA_REQ_RDY_TO_EXECUTE id: r11  time: 0.584    qpair(ptr=0x11790a0, thread=2, qid=2, subnqn=nqn.2016-06.io.spdk:cnode0, hostnqn=nqn.2014-08.org.nvmexpress:uuid:8da29bed555a45e8b9bc378e115f4c2)

 0  39100500.546     RDMA_REQ_EXECUTING      id: r11  time: 6.446    qpair(ptr=0x11790a0, thread=2, qid=2, subnqn=nqn.2016-06.io.spdk:cnode0, hostnqn=nqn.2014-08.org.nvmexpress:uuid:8da29bed555a45e8b9bc378e115f4c2)

 0  39100516.727     RDMA_REQ_EXECUTED       id: r11  time: 22.627   qpair(ptr=0x11790a0, thread=2, qid=2, subnqn=nqn.2016-06.io.spdk:cnode0, hostnqn=nqn.2014-08.org.nvmexpress:uuid:8da29bed555a45e8b9bc378e115f4c2)

 0  39100516.903     RDMA_REQ_RDY_TO_COMPL   id: r11  time: 22.803   qpair(ptr=0x11790a0, thread=2, qid=2, subnqn=nqn.2016-06.io.spdk:cnode0, hostnqn=nqn.2014-08.org.nvmexpress:uuid:8da29bed555a45e8b9bc378e115f4c2)

 0  39100517.333     RDMA_REQ_COMPLETING     id: r11  time: 23.233   qpair(ptr=0x11790a0, thread=2, qid=2, subnqn=nqn.2016-06.io.spdk:cnode0, hostnqn=nqn.2014-08.org.nvmexpress:uuid:8da29bed555a45e8b9bc378e115f4c2)

 0  39100550.895     RDMA_REQ_COMPLETED      id: r11  time: 56.795   qpair(ptr=0x11790a0, thread=2, qid=2, subnqn=nqn.2016-06.io.spdk:cnode0, hostnqn=nqn.2014-08.org.nvmexpress:uuid:8da29bed555a45e8b9bc378e115f4c2)

目前,只有RDMA qpairs可供注解,但预计今后会添加更多注解。

结论

总而言之,现在SPDK可以使用更全面更有效的方法实现BPF追踪以检查应用程序。BPF追踪由一组bpftrace程序、定义在SPDK库的USDT探针和几个脚本组成,这些脚本能够简化BPF追踪,和常规的SPDK追踪代码结合使用。

原文链接如下:

https://spdk.io/news/2021/09/28/bpf_tracing_with_spdk/

学习更多dpdk视频
DPDK 学习资料、教学视频和学习路线图 :https://space.bilibili.com/1600631218
Dpdk/网络协议栈/ vpp /OvS/DDos/NFV/虚拟化/高性能专家 上课地址: https://ke.qq.com/course/5066203?flowToken=1043799
DPDK开发学习资料、教学视频和学习路线图分享有需要的可以自行添加学习交流q 君羊909332607备注(XMG) 获取
 

最后

以上就是爱听歌果汁为你收集整理的SPDK的BPF Tracing的全部内容,希望文章能够帮你解决SPDK的BPF Tracing所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(45)

评论列表共有 0 条评论

立即
投稿
返回
顶部