Systemtap: Generating Instrumentation module(.ko) for Other Computers
背景
在生产环境中, 如果服务器比较多, 同时都需要运行一样的stap脚本的话, 有几个弊端,
1. 每台机器都需要相应的内核开发包, debug包等.
2. 每次运行stap 脚本时都需要经历5步骤, 消耗资源.
http://blog.163.com/digoal@126/blog/static/163877040201391434530674/
有没有办法只在1台机器上产生内核模块, 在其他机器上直接加载这个模块呢?
systemtap提供了这样的便捷. 但是需要符合一定的要求 :
1. 产生内核模块的机器必须包含所有stap所需的包.
参考 :
https://sourceware.org/systemtap/SystemTap_Beginners_Guide/using-systemtap.html#install-kinfo
例如主机器的操作系统为CentOS 5.x
[root@db-172-16-3-39 ~]# uname -r
2.6.18-348.12.1.el5
[root@db-172-16-3-39 ~]# rpm -qa|grep kernel
kernel-2.6.18-348.12.1.el5
kernel-debuginfo-common-2.6.18-348.12.1.el5
kernel-devel-2.6.18-348.12.1.el5
kernel-debuginfo-2.6.18-348.12.1.el5
kernel-doc-2.6.18-348.12.1.el5
kernel-headers-2.6.18-348.12.1.el5
[root@db-172-16-3-39 ~]# rpm -qa|grep systemtap
systemtap-sdt-devel-1.8-6.el5
systemtap-devel-1.8-6.el5
systemtap-1.8-6.el5
systemtap-client-1.8-6.el5
systemtap-runtime-1.8-6.el5
systemtap-sdt-devel-1.8-6.el5
2. 产生内核模块的机器和运行内核模块的其他机器必须架构相同, 例如都是x86_64架构的.
[root@db-172-16-3-39 ~]# uname -m
x86_64
[root@db-172-16-3-33 ~]# uname -m
x86_64
3. 产生内核模块的机器还需要包含目标机内核同版本的相关kernel-devel, kernel-debuginfo, kernel-debuginfo-common包.
主机器正在运行的内核版本最好和目标机正在运行的内核版本一致, 那么就不需要安装与运行版本不一致的debuginfo了.
本例主机器和目标机运行的内核版本一致, 所以不需要额外安装其他版本的kernel-devel, kernel-debuginfo, kernel-debuginfo-common.
4. 目标机上要正常运行模块, 必须有systemtap-runtime包.
[root@db-172-16-3-33 ~]# rpm -qa|grep systemtap
systemtap-runtime-1.8-6.el5
但是不需要kernel-devel, kernel-debuginfo, kernel-debuginfo-common包
[root@db-172-16-3-33 ~]# rpm -qa|grep kernel
kernel-2.6.18-348.12.1.el5
kernel-headers-2.6.18-348.12.1.el5
接下来在主机上写一个stp脚本.
[root@db-172-16-3-39 ~]# cat io.stp
global var1%[60000], var2%[60000]
probe syscall.read {
var1[pid(),execname()] <<< $count
var2[pid(),execname()] <<< $count
}
probe timer.s($1) {
print("**********\n")
foreach([x,y] in var1- limit 5)
printdln("---", x, y, @count(var1[x,y]), @sum(var1[x,y]))
delete var1
}
probe timer.s($2) {
print("END:**********\n")
foreach([x,y] in var2- limit 5)
printdln("---", x, y, @count(var2[x,y]), @sum(var2[x,y]))
delete var1
delete var2
exit()
}
测试这个脚本是否使用正常
[root@db-172-16-3-39 ~]# stap io.stp 1 5
**********
3377---pcscd---27---444
24917---stapio---7---671752
19947---stapio---5---655360
3391---hald---4---8192
8969---postgres---4---64
**********
3377---pcscd---27---444
24917---stapio---6---786432
19947---stapio---5---655360
8969---postgres---4---64
8968---postgres---4---64
**********
3377---pcscd---27---444
24917---stapio---6---786432
19947---stapio---5---655360
3391---hald---4---8192
8969---postgres---4---64
**********
3377---pcscd---27---444
24917---stapio---6---786432
19947---stapio---5---655360
8969---postgres---4---64
8968---postgres---4---64
**********
3377---pcscd---27---444
3173---irqbalance---6---6144
24917---stapio---6---786432
19947---stapio---5---655360
3391---hald---4---8192
END:**********
3377---pcscd---135---2220
24917---stapio---31---3817480
19947---stapio---25---3276800
8969---postgres---20---320
8968---postgres---20---320
WARNING: Number of errors: 0, skipped probes: 1
运行正常, 产生模块, :
[root@db-172-16-3-39 ~]# stap -p 4 -r 2.6.18-348.12.1.el5 -m ioread_top5 io.stp 1 10
ioread_top5.ko
[root@db-172-16-3-39 ~]# ll ioread_top5.ko
-rw-r--r-- 1 root root 107960 Oct 15 09:42 ioread_top5.ko
将ioread_top5.ko拷贝到目标机运行.
[root@db-172-16-3-33 ~]# staprun ioread_top5.ko
**********
3431---pcscd---24---378
19556---stapio---7---671752
**********
3431---pcscd---24---378
19556---stapio---6---786432
4068---sshd---1---16384
**********
3431---pcscd---24---378
19556---stapio---6---786432
3762---sendmail---1---1024
4068---sshd---1---16384
**********
3431---pcscd---24---378
19556---stapio---6---786432
4068---sshd---1---16384
**********
3431---pcscd---24---378
19556---stapio---6---786432
3229---irqbalance---5---5120
4068---sshd---1---16384
**********
3431---pcscd---24---378
19556---stapio---6---786432
4068---sshd---1---16384
**********
3431---pcscd---24---378
19556---stapio---6---786432
4068---sshd---1---16384
**********
3431---pcscd---24---378
19556---stapio---6---786432
3762---sendmail---1---1024
4068---sshd---1---16384
**********
3431---pcscd---24---378
19556---stapio---6---786432
4068---sshd---1---16384
END:**********
3431---pcscd---240---3780
19556---stapio---61---7749640
4068---sshd---9---147456
3229---irqbalance---5---5120
3762---sendmail---2---2048
注意本文的例子中, 用到了两个stap传入参数$1, $2, 但是在目标机上不需要输入这两个参数, 原因是模块编译好后就无法改变.
目前还无法生成带参数的module, 期待systemtap以后版本中改进.
但是可以通过全局变量来取代传入参数, 只是全局变量不能放在探针入口例如timer.s(global_var)是不行的.
[root@db-172-16-3-39 ~]# stap --vp 50000 -e 'global var=1; probe timer.s(var) {println("hello")}'
Parsed kernel "/lib/modules/2.6.18-348.12.1.el5/build/.config", containing 1977 tuples
Parsed kernel /lib/modules/2.6.18-348.12.1.el5/build/Module.symvers, which contained 3546 vmlinux exports
parse error: expected literal string or number
saw: identifier 'var' at <input>:1:29
source: global var=1; probe timer.s(var) {println("hello")}
^
1 parse error.
Searched: " /usr/share/systemtap/tapset/x86_64/*.stp ", found: 4, processed: 4
Searched: " /usr/share/systemtap/tapset/*.stp ", found: 81, processed: 81
Pass 1: parsed user script and 85 library script(s) using 146792virt/23704res/3020shr/21388data kb, in 190usr/10sys/206real ms.
Pass 1: parse failed. Try again with another '--vp 1' option.
Running rm -rf /tmp/stap4b1okP
Spawn waitpid result (0x0): 0
Removed temporary directory "/tmp/stap4b1okP"
把io.stp脚本改成如下, 使用全局变量interval来控制输出间隔.
注意不要在begin中初始化interval, 否则staprun传入的global 变量会被begin中的覆盖. 初始化在global定义时指定就不会覆盖staprun指定的全局变量值.
global var1%[60000], var2%[60000], ts, interval=1
probe begin {
ts = gettimeofday_s()
}
probe syscall.read {
var1[pid(),execname()] <<< $count
var2[pid(),execname()] <<< $count
}
probe timer.s(1) {
if ((gettimeofday_s() - ts) >= interval) {
ts = gettimeofday_s()
print("**********\n")
foreach([x,y] in var1- limit 5)
printdln("---", x, y, @count(var1[x,y]), @sum(var1[x,y]))
delete var1
}
}
probe end {
print("END:**********\n")
foreach([x,y] in var2- limit 5)
printdln("---", x, y, @count(var2[x,y]), @sum(var2[x,y]))
delete var1
delete var2
exit()
}
运行io.stp脚本, 测试是否正常 .
[root@db-172-16-3-39 ~]# stap io.stp
**********
3377---pcscd---27---444
25393---stapio---7---671752
19947---stapio---5---655360
8969---postgres---4---64
8968---postgres---4---64
**********
3377---pcscd---27---444
25393---stapio---6---786432
19947---stapio---5---655360
3391---hald---4---8192
8969---postgres---4---64
**********
3377---pcscd---27---444
25393---stapio---6---786432
19947---stapio---5---655360
8969---postgres---4---64
8968---postgres---4---64
**********
25410---perl---250---946396
25405---nrpe---43---48502
25406---nrpe---43---48502
25415---psql---36---56640
25413---psql---36---56640
**********
3377---pcscd---27---444
25393---stapio---6---786432
19947---stapio---5---655360
8969---postgres---4---64
8968---postgres---4---64
END:**********
25410---perl---250---946396
3377---pcscd---135---2220
25405---nrpe---43---48502
25406---nrpe---43---48502
25415---psql---36---56640
生成模块 :
[root@db-172-16-3-39 ~]# stap -p 4 -r 2.6.18-348.12.1.el5 -m ioread_top5 io.stp
ioread_top5.ko
将ioread_top5拷贝到目标机器运行, 使用全局变量interval控制输出频率.
[root@db-172-16-3-33 ~]# staprun ioread_top5.ko interval=5
这样就可以在目标机器上实现每5秒输出1次了.
**********
3431---pcscd---120---1890
19940---check_nrpe---41---42422
19927---stapio---28---3424264
19940---sh---8---16640
3229---irqbalance---5---5120
END:**********
3431---pcscd---168---2646
19940---check_nrpe---41---42422
19927---stapio---38---4734984
19940---sh---8---16640
3229---irqbalance---5---5120
参考
1. https://sourceware.org/systemtap/SystemTap_Beginners_Guide/cross-compiling.html
2. https://sourceware.org/systemtap/SystemTap_Beginners_Guide/using-systemtap.html#install-kinfo
3. http://blog.163.com/digoal@126/blog/static/163877040201391434530674/
4. man stap , man staprun