Systemtap系列之语法专辑

1.   常用技巧

systemtap可以实现交叉编译:

编译可执行模块如下:

stap -r kernel_version script -m module_name

运行命令如下:

staprun module_name.ko

stap命令会读取脚本的指令,并翻译成C代码,编译成内核模块加载到内核。

Staprun命令运行指令,并不会去翻译或编译。

使用-v参数可以输出会话很多信息。使用-vvv可以输出更加详细信息。

其他常用的参数有如下:

-o可以将输出指定到文件

-S可以指定日志大小

-x可以绑定到某个进程,这个比较好用。

-e使用脚本而不是文件

-d可以指定模块,例如文件系统模块,这样就可以输出文件系统相关函数,也可以使用--all-modules,加载所需要的映射信息。

-F使用flight recoreder模式,后台运行,只记录最进输出。记录方式可以是内存和文件方式。内存默认是1MB,可以通过-s选项来设置。例如-s2表示设置2MB。如果是文件通过-S来设置,第一个参数是大小,第二该参数是最近保存的文件数量,老的文件会被删除。

例如:

stap -F -o /tmp/pfaults.log -S 1,2  pfaults.stp

2.   事件

systemtap中事件分为同步的和异步的。

同步事件包括:

syscall.system_call 系统调用

vfs.file_operation 虚拟文件系统的操作

kernel.function(“function”) 内核函数

kernel.trace(“tracepoint”)  静态探针tracepoint

module(“module”).function(“function”) 模块中函数

异步事件是没有绑定到代码中的指令,有计数器,定时器等,包括如下:

begin 会话开始

end 会话结束

timer 定时器事件

            关于支持的事件可以使用命令man stapprobes。

3.   函数

tid()当前线程id

uid()当前用户id

cpu()当前cpu号

gettimeofday_s()从1970开始的秒

ctime() 转换时间从秒到日期

pp()描述当前探针的字符串

thread_indent()当一个探针的处理没结束又触发下一个探针的时候,可以进行缩进打印,非常便于观察。

name 只能用于系统调用

target()当使用-x参数绑定进程的时候,可使用来判断pid()==target(),从而过滤其他进程触发的事件。

4.   变量

通常变量是局部的,如果要使用全局的变量,需要使用global。

目标可用变量可以通过-L参数获取,例如:stap -L ‘kernel.function(“vfs_read”)’

出现如下,表示vfs_read函数的可以用变量或结构。

kernel.function("vfs_read@fs/read_write.c:448") $file:struct file* $buf:char* $count:size_t $pos:loff_t*

如果变量定义在其他文件中,可以通过如下方式来引用

@var(“varname@src/file.c”)

如果是数据结构,可以通过->来获取其中的域值,例如

@var(“files_stat@fs/file_table.c”)->max_files

获取指定地址的字节:kernel_char(address)

kernel_short(address)

kernel_int(address)

kernel_long(address)

kernel_string(address)

kernel_string_n(address,n)

为了方便获取参数,systemtap提供了一个方法

直接使用$$var就可以打印函数参数和变量。

如果使用$$locals,就是打印局部变量

$$parms就只打印函数阐述

$$return 打印返回值,只在返回探针能用。

例如:

#stap -e 'probe kernel.function("vfs_read") {printf("%s\n",$$parms);exit();}'

file=0xffff880006761e00 buf=0x7fff4bb16e70 count=0x2004 pos=0xffff8800004cff48

这个得到的如file本来就是一个数据结构,意义并不是很大,如果能得到该数据结构中的域,那将更好。可以通过在变量后面增加一个$来实现。

#stap -e 'probe kernel.function("vfs_read") {printf("%s\n",$$parms$);exit();}'

如果域中还有指针,要继续解锁,那么就增加一个$来实现,如:

#stap -e 'probe kernel.function("vfs_read") {printf("%s\n",$$parms$$);exit();}'

有些结构类型可能不能确定,那么可以使用@cast来进行类型转化。

另外可以使用@define来确定是否有可用的变量

5.   参数

脚本支持参数输入的,类似shell脚本,不过在脚本中通过$和@两个来获取参数,其中$表示整数,@表示字符串。

6.   组合数组

例如:foo[“tom”]=23

foo[“dick”]=24

其实就是python中的字典,一个key对应一个value。

不过key可以是多个,最多可以是9个,相互用逗号隔开。

组合数组必须是global的。

也可以如下,将线程ID和时间关联起来。

foo[tid()] = gettimeofday_s()

l   访问的时候可以直接通过foo[tid()]来进行访问,甚至能实现++计算。

如果要遍历数组中的元素,可以使用foreach ( a in foo),这个语法是python简直了。

甚至可以按升序或降序处理如下:

foreach (a in foo- limit 10),其中-表示降序,升序可以用+,limit 10表示前10个。

l   删除可以使用delete命令。

l   判断某个key是否在组合数组中,可以使用[“a”] in foo来判断。

l   还可以进行累加例如foo[“a”]<<<$count,使用<<<来实现。

l   组合数据还支持聚合操作,@extractor(variable/array index expression).extractor.其中extractor可以是count,sum,min,max,avg。例如@count(reads[execname()]),@sum(reads[execname()].

7.   tapsets

tapsets是预先写好的探针和函数库,当用户运行systemtap脚本时,systemtap会检测这个库,然后再去翻译成C。

            这个库位于/usr/share/systemtap/tapset目录中,tapsets是不能直接执行的,它是一个抽象层让用户更加方便的来定义事件和函数。例如thread_indent(),execname()等。

8.   嵌入C代码

可以通过%{和%}来嵌入C代码。嵌入C代码后,运行可以使用stap -g script,以专家模式运行。

猜你喜欢

转载自blog.csdn.net/notbaron/article/details/80696651