Systemtap 几乎是目前所知的最强大的内核调试工具,甚至有人说它无所不能,接下来,我们就看看 SystemTAP 。
简介
通过 Kprobe 可以动态地收集调试和性能信息,是一种非破坏性的工具,用户可以用它跟踪运行中内核任何函数或执行的指令等。但 Kprobe 并没有提供一种易用的框架,用户需要自己去写模块,然后安装,所以在使用时非常麻烦。
而 systemtap 是利用 Kprobe 提供的 API 来实现动态地监控和跟踪运行中的 Linux 内核的工具,相比于 Kprobe,systemtap 更加简单,提供给用户简单的命令行接口,以及编写内核指令的脚本语言。
工作原理
systemtap 的核心思想是定义一个事件 (event),并给出处理该事件的句柄 (Handler),当一个特定的事件发生时,内核运行该处理句柄,就像快速调用一个子函数一样,处理完之后恢复到内核原始状态。
- 事件
定义了很多种事件,例如进入或退出某个内核函数、定时器时间到、整个 systemtap 会话启动或退出等等。 - 句柄
描述了当事件发生时要完成的工作,通常是从事件的上下文提取数据,将它们存入内部变量中,或者打印出来。
如下是 systemtap 的工作原理。
其原理是,systemtap 通过将脚本语句翻译成 C 语言,编译成内核模块;模块加载之后,将所有探测的事件以钩子的方式挂到内核上,当任何处理器上的某个事件发生时,相应钩子上句柄就会被执行。
最后,当 systemtap 会话结束之后,钩子从内核上取下,然后移除模块;而整个过程只需要用一个命令 stap 就可以完成。
安装
可以通过 YUM 进行安装,详细的内容在下面;对于 CentOS 来说,此时会在 /usr/share/systemtap/tapset/
目录下安装 tapset 库。
0. 判断内核是否支持 systemtap
在 /boot 目录下查看当前版本的配置信息,是否包含如下的编译选项,CentOS 中通常都会包含。
# cat /boot/config-`uname -r` | egrep 'CONFIG_(DEBUG_INFO|KPROBES|DEBUG_FS|RELAY)='
CONFIG_RELAY=y
CONFIG_KPROBES=y
CONFIG_DEBUG_FS=y
CONFIG_DEBUG_INFO=y
如果没有上述的配置项,则需要重新编译,需要打开调试信息、Kprobe 和 debugfs ,确保 .config 文件中看到上面的四个选项。
1. 安装
使用 systemtap 需要安装几个依赖包:kernel-devel (一般会安装在 /usr/src/kernels/`uname -r` 目录下),kernel-debuginfo (包比较大,下载很慢);对于 systemtap 直接通过 yum 安装即可。
# yum install kernel-devel kernel-debuginfo kernel-debuginfo-common
# yum install systemtap systemtap-server
除了上述的包之外,如果需要编译还可以安装 devel,还有测试示例 testsuite 等包。另外,需要注意是,安装时一定要注意 systemtap 版本要与内核的版本一致。
2. 简单测试
如下是一个简单的示例,会输出 hello world。
# stap -v -e 'probe begin { printf("Hello, World!\n"); exit() }'
源码安装
上述的方式只能安装 repository 中的包,通常都比较老,如果需要安装最新的版本可以从源码编译安装。可以从 SystemTap 下载相应的版本,而且需要 elfutils 的支持。
./configure --with-elfutils=/local/elfutils-0.166 --with-prefix=/usr
远程使用
如果有很多相同类型的机器(内核版本、机型),那么只需要在一台机器上做上述的完整安装,然后在其它机器安装 systemtap-runtime 即可。
# stap -r `uname -r` -e 'probe vfs.read {exit()}' -m simple # 生成simple.ko
# staprun simple.ko
除了上述的标准使用、远程使用,还可以使用 Flight Recorder Mode,简单说就是会在内核中申请一块内存,在需要的时候链接上去。
UEFI Secure Boot Support
这是 2.5 引入的新特性,对安全的支持,需要添加一个认证方式到 MOK(Machine Owner Keys) 数据库中,详细的可以参考 Systemtap UEFI Secure Boot Support。
上述文章没有提及需要安装 systemtap-server 并启动,实际上,在 CentOS 7 中可以通过如下的方式安装,简单记录如下。
# yum install systemtap-server # 安装服务端
# systemctl start stap-server # 启动服务
然后,通过如下的方式执行,详细的内容可以参考上述的示例。
----- 查看当前所有的server
# stap --list-servers=all
----- 生成相应的signing_key.x509
# stap --use-server=192.168.2.108:38557 -e 'probe begin { exit() }'
----- 导入
# mokutil --import signing_key.x509
----- 重启,需要验证输入密码
# shutdown -r now
----- 查看是否生效
# stap --list-servers
----- 测试
# stap -ve 'probe begin { printf("hello\n"); exit() }'
详细使用
还可以参考 /usr/share/doc/systemtap-client-X.X/examples 中的示例,可以简单使用如下文件。
# cat hello-world.stp
probe begin
{
print ("hello world\n")
exit ()
}
# stap hello-world.stp
探测点描述包含三个部分,function@filename:lineno ,可以使用 * 表示任意字符,例如,指定文件名为 net/socket.c,探测函数的入口和返回。
probe kernel.function("*@net/socket.c") {}
probe kernel.function("*@net/socket.c").return {}
可以通过如下方式查找匹配的内核函数和变量。
# stap -l 'kernel.function("*nit*")' // 查看函数
# stap -L 'kernel.function("*nit*")' // 查找函数以及变量
执行步骤
脚本的执行总共分为了 5 步:
1. 解析脚本
解析 stap 输入脚本,或者 -e 中指定的内容,以及 tapset 库,也可以通过 -I 指定检索路径中的 *.stp 文件。
# stap --vp 50000 -I /tmp -e 'probe syscall.read {printf("hello i am digoal.\n"); exit()}'
2. 匹配库
解析脚本,对库进行匹配,并选择对应的 stp(探针库)、stpm(宏库) 文件,直到所有的符号都分析匹配完。如果没有使用 -u 选项,则优化脚本,移除未使用的变量、表达式、函数等。
# stap --vp 05000 -I /tmp -e 'probe syscall.read {printf("hello i am digoal.\n"); exit()}'
3. 转换成C
将以上解析好的 stap 脚本转换成 C 代码。
# stap --vp 00500 -I /tmp -e 'probe syscall.read {printf("hello i am digoal.\n"); exit()}'
4. 编译成.ko文件
使用上述生成的 C 文件创建 Linux 内核对象文件 (.ko)。
# stap --vp 00050 -I /tmp -e 'probe syscall.read {printf("hello i am digoal.\n"); exit()}'
5. 加载的内核运行
调用 staprun 将 ko 文件加载到内核中,并与之通讯,直到运行结束或受到中断请求信号;最后,staprun 从内核中卸载这个 ko 模块,并且清除整个 stap 过程产生的临时文件。
# stap --vp 00005 -I /tmp -e 'probe syscall.read {printf("hello i am digoal.\n"); exit()}'
配置选项
常用的配置选项有:
-p NUM
运行至某个步骤停止,一般可用于生成可移动模块。--vp ABCDE
指定各步骤的输出详细级别。
常见错误排查
简单列举如下。
semantic error
详细报错信息如下,主要原因是缺乏 debuginfo 包:
semantic error: missing x86_64 kernel/module debuginfo [man warning::debuginfo] under '/lib/modules/xxx/build'
可以参考 systemtap: a linux trace/probe tool ,其中包含了如下内容:
- By default, systemtap looks for the debug info in these locations:
/boot/vmlinux-`uname -r`
/usr/lib/debug/lib/modules/`uname -r`/vmlinux
/lib/modules/`uname -r`/vmlinux
/lib/modules/`uname -r`/build/vmlinux
也就是说,依据这个默认搜索路径,可以将 debug 的 vmlinux 链接到 /lib/modules 下。
# ln -s /usr/lib/debug/lib/modules/`uname -r`/vmlinux /lib/modules/`uname -r`
参考
可以参考 systemtap官网 ;也可以参考 systemtap Documentation,也就是 systemtap 的参考文档;以及相关的示例 WarStories 。
另外,SystemTap Beginners Guide 是一篇不错的入门文档,包括了详细的介绍以及示例。