ubuntu16.04.2 systemtap安装以及工作原理和协议栈调用栈不全解决方法

openstack环境常常也使用ubuntu16.04.2作为host主机镜像构建私有云。systemtap可以帮助笔者探测host主机内核态的真实运行状态,外加内核代码阅读及时定位看是系统底层的问题。解决一些系统难题、扩展思路、排除一些因为不了解内核而造成的不必要的猜测。下面是笔者使用systemtap环境安装过程和基本原理描述。

关于跨模块和kernel 符号显示不全是笔者遇到的第一个问题,找到了解决方法,也记录下来,请看下文。

如何安装systemtap?

步骤1:下载ubuntu16.04.2调试数据包

1
linux-image-4.4.0-62-generic-dbgsym_4.4.0-62_amd64.ddeb

调试目录数据包下载路径 http://ddebs.ubuntu.com/pool/main/l/linux/?C=M;O=D

步骤2:安装ubuntu16.04.2调试包

1
dpkg -i linux-image-4.4.0-62-generic-dbgsym_4.4.0-62_amd64.ddeb

步骤3: 编译安装systemtap程序

1
2
3
4
5
6
7
8
9
10
apt-get update && \
apt-get install -y build-essential gettext elfutils libdw-dev python wget tar && \

wget https://sourceware.org/systemtap/ftp/releases/systemtap-3.1.tar.gz
tar xzvf systemtap-3.1.tar.gz

cd systemtap-3.1/ && \
./configure && \
make all && \
make install ;

systemtap 测试

1
2
3
4
5
6
7
8
root@ubuntu:~# stap -ve 'probe begin { log("hello systemtap!") exit() }'
Pass 1: parsed user script and 465 library scripts using 110724virt/47744res/6432shr/41544data kb, in 120usr/40sys/374real ms.
Pass 2: analyzed script: 1 probe, 2 functions, 0 embeds, 0 globals using 111648virt/48956res/6620shr/42468data kb, in 0usr/0sys/7real ms.
Pass 3: translated to C into "/tmp/staps7ieJA/stap_294bb69fb17d36571b970bddbffa5bd9_1172_src.c" using 111648virt/48956res/6620shr/42468data kb, in 0usr/0sys/0real ms.
Pass 4: compiled C into "stap_294bb69fb17d36571b970bddbffa5bd9_1172.ko" in 990usr/220sys/2162real ms.
Pass 5: starting run.
hello systemtap!
Pass 5: run completed in 0usr/10sys/392real ms.

打印4s内所有open系统调用的信息

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
root@ubuntu:~# cat test2.stp
#!/usr/bin/stap

probe begin
{
log("begin to probe")
}

probe syscall.open
{
printf ("%s(%d) open (%s)\n", execname(), pid(), argstr)
}

probe timer.ms(4000) # after 4 seconds
{
exit ()
}

probe end
{
log("end to probe")
}
1
2
3
4
5
6
root@ubuntu:~# cp /root/systemtap/systemtap-3.1/stap /usr/bin/
root@ubuntu:~# ./test2.stp
begin to probe
irqbalance(1686) open ("/proc/interrupts", O_RDONLY)
irqbalance(1686) open ("/proc/stat", O_RDONLY)
end to probe

systemtap 工作原理

systemtap 的核心思想是定义一个事件(event),以及给出处理该事件的句柄(Handler)。当一个特定的事件发生时,内核运行该处理句柄,就像快速调用一个子函数一样,处理完之后恢复到内核原始状态。这里有两个概念:

  • 事件(Event):systemtap 定义了很多种事件,例如进入或退出某个内核函数、定时器时间到、整个systemtap会话启动或退出等等。
  • 句柄(Handler):就是一些脚本语句,描述了当事件发生时要完成的工作,通常是从事件的上下文提取数据,将它们存入内部变量中,或者打印出来。

    Systemtap 工作原理是通过将脚本语句翻译成C语句,编译成内核模块。模块加载之后,将所有探测的事件以钩子的方式挂到内核上,当任何处理器上的某个事件发生时,相应钩子上句柄就会被执行。最后,当systemtap会话结束之后,钩子从内核上取下,移除模块。整个过程用一个命令 stap 就可以完成。

hah.png

实际使用

linux basic kernl和加载进去的ko文件共同组成linux 内核态,支撑起linux操作系统的运行;linux有不同的驱动选择,这些驱动往往走不同内核分支,它们很相像,在梳理协议栈时候,往往很费解,stap可以帮助解决此问题,很简单的一个运用是打印调用关系栈,来最终总结出linux运行函数路径。分为打印kernel函数调用栈和加载ko模块的调用栈,来最终实现所有linux运行路径拓扑图。(这里只提讲解调用栈相关使用)

https://zhuanlan.zhihu.com/p/28680568

kenel调用栈

步骤1:列出kernel符号

1
2
root@ubuntu:~/openvswitch-2.7.0# stap -l 'kernel.function("*")'|grep __netif_receive_skb_core
kernel.function("__netif_receive_skb_core@/build/linux-W6HB68/linux-4.4.0/net/core/dev.c:3828")

内核ko模块调用栈

步骤1:列出模块函数符号

stap -l ‘module(“openvswitch”).function(“*“)’|grep ovs_vport_receive

1
2
0xffffffffc0586ac0    br_handle_frame
0xffffffffc05864a0 br_handle_frame_finish

解决stap函数栈解析不全问题

问题举例1:vethpair口调用函数调用栈打印

打印协议栈脚本

1
2
3
4
5
6
7
root@ubuntu:~# cat btveth.stp
probe module("veth").function(@1){
print("----------------START-------------------------\n")
printf("In process [%s]\n", execname())
print_backtrace()
print("----------------END-------------------------\n")
}

执行上述脚本后,出现资源无法解析到情况,可以看到stack函数打印时只有指针没有函数名称,且出现inexact字样

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
root@ubuntu:~# stap  btveth.stp veth_xmit
WARNING: Missing unwind data for a module, rerun with 'stap -d kernel'
----------------START-------------------------
In process [ping]
0xffffffffc046c550 : veth_xmit+0x0/0x70 [veth]
0xffffffff8172e959
0xffffffff8172f0e6 (inexact)
0xffffffff8172f160 (inexact)
0xffffffff817383f8 (inexact)
0xffffffff8176fab6 (inexact)
0xffffffff81770a56 (inexact)
0xffffffff81764133 (inexact)
0xffffffff8177145e (inexact)
0xffffffff81770920 (inexact)
0xffffffff81770c25 (inexact)
0xffffffff81771e29 (inexact)
0xffffffff81771e83 (inexact)
0xffffffff817983a9 (inexact)
0xffffffff8139ebc1 (inexact)
0xffffffff817a8175 (inexact)
0xffffffff8170fae8 (inexact)
0xffffffff81710591 (inexact)
0xffffffff8106f31f (inexact)
0xffffffff810caeb1 (inexact)
----------------END-------------------------

解决办法1:stap探测时候加上-d kernel选项,这样在解析时候就会调用kernel的符号了,将指针变成我们能认识的函数符号

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
root@ubuntu:~# stap  -d kernel btveth.stp veth_xmit
WARNING: Missing unwind data for a module, rerun with 'stap -d openvswitch'
WARNING: Missing unwind data for a module, rerun with 'stap -d bridge'
----------------START-------------------------
In process [ping]
0xffffffffc046c550 : veth_xmit+0x0/0x70 [veth]
0xffffffff8172e959 : dev_hard_start_xmit+0x249/0x3d0 [kernel]
0xffffffff8172f0e6 : __dev_queue_xmit+0x526/0x590 [kernel]
0xffffffff8172f160 : dev_queue_xmit+0x10/0x20 [kernel]
0xffffffff8176fc02 : ip_finish_output2+0x292/0x380 [kernel]
0xffffffff81770a56 : ip_finish_output+0x136/0x1f0 [kernel]
0xffffffff8177145e : ip_output+0x6e/0xe0 [kernel]
0xffffffff81770c25 : ip_local_out+0x35/0x40 [kernel]
0xffffffff81771e29 : ip_send_skb+0x19/0x40 [kernel]
0xffffffff81771e83 : ip_push_pending_frames+0x33/0x40 [kernel]
0xffffffff817983a9 : raw_sendmsg+0x7e9/0xb20 [kernel]
0xffffffff817a8175 : inet_sendmsg+0x65/0xa0 [kernel]
0xffffffff8170fae8 : sock_sendmsg+0x38/0x50 [kernel]
0xffffffff81710591 : ___sys_sendmsg+0x281/0x290 [kernel]
0xffffffff81710ee1 : __sys_sendmsg+0x51/0x90 [kernel]
0xffffffff81710f32 : sys_sendmsg+0x12/0x20 [kernel]
0xffffffff818385f2 : entry_SYSCALL_64_fastpath+0x16/0x71 [kernel]
----------------END-------------------------

解决办法2:加上–all-modules选项 (非常适合懒人)

1
2
3
4
5
6
7
8
9
10
11
12
root@ubuntu:~# stap --all-modules btbr.stp br_handle_frame

----------------START-------------------------
In process [ping]
0xffffffffc0583ac0 : br_handle_frame+0x0/0x2b0 [bridge]
0xffffffff8172c294 : __netif_receive_skb_core+0x364/0xa60 [kernel]
0xffffffff8172c9a8 : __netif_receive_skb+0x18/0x60 [kernel]
0xffffffff8172d7a8 : process_backlog+0xa8/0x150 [kernel]
0xffffffff8172ceee : net_rx_action+0x21e/0x360 [kernel]
0xffffffff81085db1 : __do_softirq+0x101/0x290 [kernel]
0xffffffff8183a30c : do_softirq_own_stack+0x1c/0x30 [kernel]
----------------END-------------------------

问题举例2: linux bridge发包函数调用栈打印

1
2
3
4
5
6
probe   module("bridge").function(@1){
print("----------------START-------------------------\n")
printf("In process [%s]\n", execname())
print_backtrace()
print("----------------END-------------------------\n")
}

执行上述脚本后,出现资源无法解析到情况,可以看到stack函数打印时只有指针没有函数名称,很可能也漏了一部分关键函数

1
2
3
4
5
6
7
root@ubuntu:~# stap btbr.stp br_handle_frame
WARNING: Missing unwind data for a module, rerun with 'stap -d kernel'
----------------START-------------------------
In process [ping]
0xffffffffc0583ac0 : br_handle_frame+0x0/0x2b0 [bridge]
0xffffffff8172c294
----------------END-------------------------

解决办法1:加上-d kernel 如下即可以看见调用栈全貌

1
2
3
4
5
6
7
8
9
10
11
root@ubuntu:~# stap -d kernel btbr.stp br_handle_frame
----------------START-------------------------
In process [ping]
0xffffffffc0583ac0 : br_handle_frame+0x0/0x2b0 [bridge]
0xffffffff8172c294 : __netif_receive_skb_core+0x364/0xa60 [kernel]
0xffffffff8172c9a8 : __netif_receive_skb+0x18/0x60 [kernel]
0xffffffff8172d7a8 : process_backlog+0xa8/0x150 [kernel]
0xffffffff8172ceee : net_rx_action+0x21e/0x360 [kernel]
0xffffffff81085db1 : __do_softirq+0x101/0x290 [kernel]
0xffffffff8183a30c : do_softirq_own_stack+0x1c/0x30 [kernel]
----------------END-------------------------

解决办法2: 加上–all-modules选项(非常适合懒人)

1
2
3
4
5
6
7
8
9
10
11
12
root@ubuntu:~# stap --all-modules btbr.stp br_handle_frame

----------------START-------------------------
In process [ping]
0xffffffffc0583ac0 : br_handle_frame+0x0/0x2b0 [bridge]
0xffffffff8172c294 : __netif_receive_skb_core+0x364/0xa60 [kernel]
0xffffffff8172c9a8 : __netif_receive_skb+0x18/0x60 [kernel]
0xffffffff8172d7a8 : process_backlog+0xa8/0x150 [kernel]
0xffffffff8172ceee : net_rx_action+0x21e/0x360 [kernel]
0xffffffff81085db1 : __do_softirq+0x101/0x290 [kernel]
0xffffffff8183a30c : do_softirq_own_stack+0x1c/0x30 [kernel]
----------------END-------------------------