Systemtap: Generating Instrumentation module(.ko) for Other Computers

2 minute read

背景

在生产环境中, 如果服务器比较多, 同时都需要运行一样的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

Flag Counter

digoal’s 大量PostgreSQL文章入口