今天我就和技术伙伴们分享一下BCC和Ftrace的用法,以及在开发实践中会遇到哪些问题。这篇文章就一起来~
BCC是跟踪内核和操作程序的工具集。它的软件包包含了一些有用的工具和例子,是eBPF技术的前端。它集成了ebpf/kprobe/uprobe等工具,可以用Python封装,集成了一些常用的功能。

EBPF是近年来的热门技术。它的前身是BPF,即tcpdump用来过滤数据包的技术。EBPF扩展了BPF,它可以在内核中运行自定义程序,而无需修改内核源代码或加载内核模块。
Ftrace有两个概念,一个是广义的Ftrace框架,一个是狭义的跟踪技术。广义Ftrace框架包含了krpobe trace事件的多种跟踪技术,狭义Ftrace是本期的重点内容。今天,我们将重点介绍其中一种的用法。
Tracer的概念字面翻译为tracker,可以直观的理解为内核提供的一些追踪技术,如函数追踪、函数图追踪等。今天我们就重点介绍一下函数图追踪器,因为函数追踪器的很多功能都可以用BCC来实现,但是函数图是BCC不具备的功能。
功能图跟踪器的效果如下:
密件抄送效果如图所示:
如何使用BCC Ftrace?
> > > >安装
BCC可以用yum install bcc -y y安装,Ftrace不需要安装,由内核提供,打开CONFIG_DYNAMIC_FTRACE即可,默认开启该选项。
内核越高,BCC和Ftrace可以使用的函数就越多。也就是说,在低内核版本上,有些功能可能没有集成所以你不能使用这个功能。
密件抄送安装完成后,如图所示:
Ftrace需要手动打开一些内核选项:
跟踪语法:
Or name p:name:钉住内核函数名。
R::name:跟踪内核函数名的返回值。
Lib:name或p:Lib:name:在用户模式lib库中标记函数名。
R:lib:name:在用户模式lib库中跟踪函数名。
Path::name:跟踪位于path path下的用户状态函数名。
R:path:name:追溯位于path path下的用户状态函数名的返回值。
t:系统:名称:标桩名为系统:名称的跟踪点。
U:lib:name:在lib库中添加名为name的USDT探测器。
*:用于匹配任何字符的通配符。r选项允许使用正则表达式。
请参阅详细的“bpf之巅——深入了解linux系统和应用程序性能”
开发过程中的实际案例
> > > >安装
您可以看到环境中的同一个网段中有两个ip地址。这个环境将消息发送到作为客户端的服务器,服务器的地址是100.1.1.21。
> > > > ifdown测试
客户端和服务器正常通信时网卡通过ifdown宕机会发生什么?
我们可以看到,网卡宕机后客户端的返回值仍然正常,但是服务器在读取时被阻塞,所以无法再接收数据。

> > > > > ip链路设置测试
在客户端和服务器的正常通信中,通过ip link set ens192 down掉线会发生什么情况?
如你所见,客户端和服务器都可以正常发送和接收!
这里有两个问题:
为什么在ifdown后客户端仍然可以发送成功?
为什么在ifdown之后服务器接收不到消息,但是在ip link set down测试中服务器可以接收到消息?
接下来,我们将通过BCC和Ftrace跟踪工具来回答这两个问题。
Bctcrace trace _ _ sk _ dst _ check
根据调查和定位,我们确定了路线。路由是在ip层完成的,所以让我们看看ip层代码:
Ip _ queue _ xmit-> IP _ queue _ xmit IP _ queue _ xmit是IP层的入口函数。
如你所见,ip层的第一步是检查,所以我们需要看看检查的结果是什么,两个测试的区别是什么?
我们需要用BCC自带的Trace工具检查sk_dst_check的返回值。
> > > > > ip链路设置测试
> > > > ifdown
可以看到,在ifdown测试中,后面所有的返回值都变成NULL,而在ip link set down测试中,只有一个返回值变成NULL,然后又恢复正常。
经过了解,变成NULL的原因很简单。因为上一次成功的路由缓存在struct sock中,这个sk_dst_check检查路由缓存是否还可用,所以ifdown和ip link set down肯定是不可用的。
梳理代码逻辑后可以观察到,如果路由缓存不可用,则通过IP _ route _ output _ ports-> IP _ route _ output _ flow搜索路由表,搜索成功后会再次缓存。所以这里发生的是ifdown路由缓存失效后,路由表找不到有效路由。在ip链路建立之后,你可以通过查找路由表找到一条有效的路由。
所以我们通过Ftrace来看一下。两个测试中ip_route_output_flow的逻辑有什么区别?
跟踪ip_route_output_flow
Ip链接设置后的路由查找过程:
在ifdown之后,路由查找的过程:
对比两个输出和内核代码的区别,可以发现ifdown之后,设备会直接查找失败,然后直接返回上层——EHOSTUNREAD。并且在ip链路断开后,设备会被成功找到,然后路由会被成功找到。
对比内核代码中的查找路由过程,发现在fib_table_lookup中,有如下逻辑:
然后找到选项/proc/sys/net/IP v4/conf/ens 40/ignore _ routes _ with _ link down,该选项控制link down后原路由是否仍然生效,该值默认为0,即不忽略原路由项。
至此,我们终于可以回答前面的两个问题了:
为什么在ifdown后客户端仍然可以发送成功?
因为ip层将eOstunreach返回给tcp,而tcp重试,再次接收eOstunreach,然后重试/proc/sys/net/IP v4/tcp _ retries 2这么多次,TCP不会返回失败。

为什么在ifdown之后服务器接收不到消息,但是在ip link set down测试中服务器可以接收到消息?
因为ifdown之后设备被删除,EHOSTUNREACH直接返回tcp层,下一次重试会失败。于是消息发布了,sever收不到。
但是ip链路设置下来后,设备并没有被删除,默认情况下/proc/sys/net/IP v4/conf/ens 40/ignore _ routes _ with _ link down为0,即没有忽略已经宕机且链路已经掉线的路由,所以路由会被成功找到,然后发送成功,服务器会成功接收。
脚本
通过本期文章的分享,大家应该对BCC和Ftrace的用法有了一定的了解。后续会分享更多技术文章,可以继续关注。


