【调试】SystemTap调试网卡状态一例-阿里云开发者社区

开发者社区> 江冉> 正文

【调试】SystemTap调试网卡状态一例

简介: 调试其实不仅仅是针对内核或者进程崩溃的情况,很多时候我们需要跟踪的问题并不是通过分析一个core dump能够解决的,比如类似一些状态信息输出不对,或者内核或程序行为不符合预期。此时我们经常需要依赖于日志,尤其是内核层面的问题。
+关注继续查看

调试其实不仅仅是针对内核或者进程崩溃的情况,很多时候我们需要跟踪的问题并不是通过分析一个core dump能够解决的,比如类似一些状态信息输出不对,或者内核或程序行为不符合预期。此时我们经常需要依赖于日志,尤其是内核层面的问题。但是日志往往并不不如我们期望的那样包罗万象,常常要面临的窘境是日志中空空如也。原因也很容易理解,打印日志需要代码中实现的,而发生问题这部分代码逻辑中没有相关实现,自然也就没有任何日志了。此时我们也可以考虑gdb,但是在云上做gdb kernel调试代价极大,基本我们不会考虑。

 

那么今天我们就来了解一下SystemTap这样一个轻量的调试工具,该工具堪称Linux上内核调试的神器,笔者之前有多年的Windows调试经验,在开始使用SystemTap之后也不得不感叹其强大。他的优点在于自由度高,并且可以在live的系统上运行,因此相当方便和高效。

 

首先我们简单了解一下SystemTap的原理:

 

SystemTap的基本工作原理是将脚本编译成内核模块,内核模块加载以后用于检查运行的内核的两种方法是Kprobes和Kretprobe,两种服务都集成在Linux内核中,Kprobes的原理相对简单,他在需要探测的执行指令处加上特定指令,这部分原理其实和调试器是类似的,因此一旦执行被探测的函数就会转入SystemTap的脚本逻辑中。Kretprobe相对复杂,需要理解堆栈机制的工作原理,简单来讲它通过修改堆栈上函数返回地址来达到嵌入指令的目的。

 

 

 

为了更快了解SystemTap的使用方法,我们还是利用一个实例来逐步讲解。

 

问题现象:

这也是一个比较有趣的问题,用户在云上实例中使用ip link命令后发现他的eth0状态显示为Unknown,如下图:

 

但是如果我们创建一个相同规格并且在同一个可用区的实例是无法复现的。升级内核后问题依然存在。

 

研究步骤:

研究疑难问题的时候思路往往大同小异,依然是不断对自己提问的过程,很显然第一个问题自然就是这个state是从哪里获取的,或者说数据源是什么。

 

1. 数据源在哪里?

我自己是从ip link的代码出发来寻找数据源:

 

 

显然是来自内核网络设备对象中的operstate

 

 

事实上源数据是可以从如下文件获得:

 

/sys/devices/pci0000:00/0000:00:03.0/virtio0/net/eth0/operstate

 

2. 调试什么?

有了数据源,接下去一个问题是,我们虽然知道错误的状态是从哪里来的,可是这只是一个静态的数据,对我们似乎没有意义。可以继续我们研究的关键在于 - 这个数据是什么时候被设上的。知道了这一点我们至少可以知道我们去调试哪个过程。这个部分和调试技术本身关联就不大了,我们完全可以充分发散思路。我自己最后是挑选了这样一个过程作为我的调试对象:

 

rmmod virtio_net

modprobe virtio_net

 

重新加载虚拟网卡驱动,驱动被重新加载了,自然所有的网络设备的状态也会重新设,那么我们就可以重点研究这个过程中为什么把operstate设成了unknown

 

3. 阅读代码:

阅读代码永远是调试的核心步骤,我们现在寻找一下内核中哪里会设置operstate

 

 

我们看到在上面这部分代码是总是会设置operstate,无论是IF_OPER_LOWERLAYERDOWN,IF_OPER_DOWN或者IF_OPER_UP,至少不会是IF_OPER_UNKNOWN。也就是很有可能在非正常情况并没有调用到default_operstate()。那么如果确认呢?那就该轮到SystemTap登场了。

 

SystemTap登场:

 

SystemTap安装比较简单:

 

yum install kernel-devel

yum install systemtap

 

接下去是安装符号文件,centos的话可以从debuginfo.centos.org下载到对应的符号文件,rpm安装即可。

 

建立一个stp脚本如:

 

probe begin

{

prinf("stap begin\n");

}

 

probe kernel.function("default_operstate")

{

printf("calling default_operstate\n")

}

 

运行stap -g setlink.stp即可。探测开始会打印"stap begin",然后我们就可以开始运行rmmod virtio_net;modprobe virtio_net,观察是否有输出default_operstate,当然最好的方法是准备一台正常的机器进行对比。对比结果当然是正如预期,正常的情况下能够输出"calling default_operstate",而非正常情况却没有输出。

 

4. 体力活:

真正的体力活开始了,接下去的思路非常简单:

 

  1. 阅读代码,看每一层的调用情况。
  2. 一旦有不确认的情况,使用systemtap确认调用路径。

 

目的只有一个找到代码源头上的区别。举一个例子,确认调用路径如下:

 

rfc2863_policy->default_operstate

 

但是有两处代码会调用rfc2863_policy,linkwatch_do_dev和linkwatch_init_dev,于是我们不想动脑的分析的话,直接修改stp脚本如:

 

probe begin

{

prinf("stap begin\n");

}

 

probe kernel.function("linkwatch_do_dev")

{

printf("calling linkwatch_do_dev\n")

}

 

probe kernel.function("linkwatch_init_dev")

{

printf("calling linkwatch_init_dev\n")

}

 

在正常和非正常的机器运行stap对比输出即可知道我们下一步的方向了。那么中间的步骤我们就不赘述了,直奔主题:

 

正常机器:首先调用netif_carrier_off然后再call netif_carrier_on->linkwatch_fire_event->linkwatch_do_dev->rfc2863_policy->default_operstate

非正常机器:直接call netif_carrier_on,因此以下逻辑导致无法触发event:

 

if (test_and_clear_bit(__LINK_STATE_NOCARRIER, &dev->state)) {

if (dev->reg_state == NETREG_UNINITIALIZED)

return;

atomic_inc(&dev->carrier_changes);

linkwatch_fire_event(dev);

 

上面的逻辑简单理解为,如果先调用netif_carrier_off,那么设备会被标记为__LINK_STATE_NOCARRIER,之后内核网络栈监测到网络链路是通的,就会调用netif_carrier_on,此时会判断__LINK_STATE_NOCARRIER是否已经标记上了,如果是说明之前的链路是不通的,那么需要改变状态就会发送event触发operstate的改变。但是如果直接调用netif_carrier_on,设备并没有被标记上__LINK_STATE_NOCARRIER,也就是链路直接就是通,不没有必要发送event触发后面关于operstate的逻辑了,自然operstate就停留在unknown的状态了。

 

netif_carrier_off是在virtio_net驱动中调用的。

 

 

这里有一个逻辑判断后端有无设上VIRTIO_NET_F_STATUS,如果是那么我们会调用netif_carrier_off,如果不是那么直接调用netif_carrier_on,导致问题。如果想进一步确认在这个逻辑里的问题,很简单,修改stap探测响应的代码行就可以了,仔细研究还会发现stap很多功能,比如打印参数,探测代码行,打印堆栈,都可以根据具体情况灵活应用。

 

 

问题结论:

 VIRTIO_NET_F_STATUS是在后端qemu中设置的,于是我们据此就可以区分问题是否是前端还是后端产生的。

版权声明:本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《阿里云开发者社区用户服务协议》和《阿里云开发者社区知识产权保护指引》。如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。

相关文章
阿里云服务器怎么设置密码?怎么停机?怎么重启服务器?
如果在创建实例时没有设置密码,或者密码丢失,您可以在控制台上重新设置实例的登录密码。本文仅描述如何在 ECS 管理控制台上修改实例登录密码。
10069 0
Android开发学习之使用Toolbar实现不同的Fragment使用不同颜色的标题栏与状态栏
Android开发学习之使用Toolbar实现不同的Fragment使用不同颜色的标题栏与状态栏                                            先看效果图。
2307 0
阿里云服务器端口号设置
阿里云服务器初级使用者可能面临的问题之一. 使用tomcat或者其他服务器软件设置端口号后,比如 一些不是默认的, mysql的 3306, mssql的1433,有时候打不开网页, 原因是没有在ecs安全组去设置这个端口号. 解决: 点击ecs下网络和安全下的安全组 在弹出的安全组中,如果没有就新建安全组,然后点击配置规则 最后如上图点击添加...或快速创建.   have fun!  将编程看作是一门艺术,而不单单是个技术。
10882 0
TCP的几个状态对于我们分析所起的作用SYN, FIN, ACK, PSH,
TCP的几个状态对于我们分析所起的作用。 在TCP层,有个FLAGS字段,这个字段有以下几个标识:SYN, FIN, ACK, PSH, RST, URG. 其中,对 于我们日常的分析有用的就是前面的五个字段。
627 0
通过HTTP/2通道实时获取IoT设备状态和数据
通过HTTP/2通道实时获取IoT设备状态和数据 1.服务端订阅 1.1 服务端订阅流程 在IoT场景,有时候我们期望业务服务器能接收到设备状态和设备采集的数据,而不是通过云产品中转, 这时我们可以开启服务端订阅,IoT平台会把设备产生的消息通过HTTP/2通道推送到业务服务器,以便根据自身业务场景消费。
2239 0
阿里云服务器如何登录?阿里云服务器的三种登录方法
购买阿里云ECS云服务器后如何登录?场景不同,阿里云优惠总结大概有三种登录方式: 登录到ECS云服务器控制台 在ECS云服务器控制台用户可以更改密码、更换系.
13879 0
状态模式(State)
结构 UML.png 模式的组成 环境类(Context): 定义客户感兴趣的接口。维护一个ConcreteState子类的实例,这个实例定义当前状态。
618 0
+关注
江冉
本人在阿里云技术中台工作,有多年的系统和应用调试的经验。希望能和大家多多分享源码和汇编调试的技巧。
9
文章
0
问答
来源圈子
更多
作为全球云计算的领先者,阿里云为全球230万企业提供着云计算服务,服务范围覆盖200多个国家和地区。我们致力于为企业、政府等组织机构提供安全可靠的云计算服务,给用户带来极速愉悦的服务体验。
+ 订阅
文章排行榜
最热
最新
相关电子书
更多
《2021云上架构与运维峰会演讲合集》
立即下载
《零基础CSS入门教程》
立即下载
《零基础HTML入门教程》
立即下载