systemtap是一个强大的工具,笔者本次主要是发现其能力,为后续操作使用做下积淀积累。请见下文。
probe
“probe” \<=> “探测”, 是SystemTap进行具体地收集数据的关键字。
“probe point” 是probe动作的时机,也称探测点。也就是probe程序监视的某事件点,一旦侦测的事件触发了,则probe将从此处插入内核或者用户进程中。
探测点语法
1 | kernel.function(PATTERN) |
- return 返回点探测
- return.maxactive(VALUE)修饰return,控制同时探测多少个实例,默认足够一搬不用,如果出现了跳过探测现象且很多,可以使用此参数,提升探测效果
- .call 函数被调用时触发此调用点
- .inline 内联函数需要展示时候用此参数
- .label 内核常常用到goto函数,用此标签可以探测出具体的goto返回点
1 | module(MPATTERN).function(PATTERN) |
1 | kernel.statement(PATTERN) |
- statement定位到具体的line或者函数,将这些定位点作为跟踪点
1 | moudle(MPATTERN).statement(PATTERN) |
1 | process(PROCESSPATH).function(PATTERN) |
PATTERN
func[@file]
func@file:linenumber
eg:
1 | kernel.function("*int*") |
我当前内核有哪些函数?
1 | root@ubuntu:~# stap -l 'kernel.function("*")'|grep __netif_receive_skb_core |
我当前内核有哪些变量?
1 | root@ubuntu:~# stap -L 'kernel.function("__netif_receive_skb_core")' |
我想知道__netif_receive_skb_core被调用了几次?
1 | root@ubuntu:~# cat tanche.stp |
1 | global count=0 |
每收5个数据包,打印一次,如果没有收到5个数据包,且时间过了约10s,也打印一次
执行结果:
1 | root@ubuntu:~# stap tanche.stp |
如何打印内核函数的返回值?
1 | root@ubuntu:~# stap -e 'probe kernel.function("__netif_receive_skb_core").return { printf("__netif_receive_skb_core return: :%d\n",$return) exit() }' |
如何使用stap知晓当前函数在哪丢包的?
1 | static int__netif_receive_skb_core(struct sk_buff *skb, bool pfmemalloc) |
1 | stap -e 'probe kernel.function("__netif_receive_skb_core").label("drop") { printf("__netif_receive_skb_core drop\n") }’ |
linux函数使用goto处理函数返回点再正常不过了,此时如果你使用return探测丢包点,难达预期效果。只有将丢包点精准到goto语句的label标签,才可以发现丢包位置。
(之前对stap 的probe理解不深刻,也不会这样用,也不知曾为找具体丢包点挠过多少回头皮)
谁调用了__netif_receive_skb_core 收了我的数据包?
首先列出__netif_receive_skb_core所在的代码位置
1 | root@ubuntu:~# stap -L 'kernel.function("__netif_receive_skb_core")' |
1 | stap -e 'probe kernel.statement("*@net/core/dev.c:3829") {printf(" : %s\n", execname()) }' |
qume虚拟机进程创建出了错,该怎么办?
1 | root@compute-001:~# cat qumetanche.stp |
进程在创建时候,会调用sys_clone,通过过滤调用该函数的执行者,可以定位到qemu-system-x86服务建立的进程,然后跟踪建立过程中返回值确认创建失败原因
1 | root@compute-001:~# stap qumetanche.stp |
我想研究一个内核(信号处理过程)过程该怎么做?
步骤1:找到sys_kill函数所在的源文件
1 | root@ubuntu:~# stap -l 'kernel.function("sys_kill")' |
得知信号处理函数所在的内核源代码路径为:/build/linux-W6HB68/linux-4.4.0/kernel/signal.c
因为不同平台会有不同的内核路径,首先要做的事情就是,先找到具体内核路径
步骤2:根据步骤1找到的内核路径,探测该路径下所有的函数的调用和返回并打出结果
-x -x PID sets target() to PID, 脚本里会用到此参数
1 | root@ubuntu:~# stap -x 15365 signal.stp |
有人用stap分析内存泄漏和重复释放,记录下来吧
步骤1:分析要要用的c语言源码,并编译
1 | root@ubuntu:~# cat mem_test.c |
编译:
1 | gcc -g mem_test.c -o main |
步骤2: 探测内存泄漏和重复释放的脚本
1 | root@ubuntu:~# cat mem.stp |
脚本分析:脚本主要记录下内存申请点和释放点,这样就可以很容易找到内存重复释放和泄漏点了
步骤3: 探测过程
1 | root@ubuntu:~# vim mem.stp |
步骤4:结果分析:
a. 申请内存地址如下
1 | p1: 0xa28010, p2: 0xa28210, p3: 0xa282e0, p4: 0xa28420 |
b. 0xa28210 p2 重复释放,重复释放位置 0x40069c [/root/main+0x69c/0x1000] ,因为它已经在0x400684 [main+0x684] 释放过了
1 | [0xa28210] has been freed : -1 |
c. 0xa28420 p4内存泄漏,你在0x400643 [main+0x643] 申请了它,但是没有释放
1 | [0xa28420] is not free ,but you malloc it in 0x400643 [main+0x643] ,so This is memory Loss!!!! |
我可以获取哪些函数和系统状态并打印出来?
1 | root@ubuntu:~# cat test_all_func.stp |
执行结果如下:具体含义自己体会和尝试
1 | root@ubuntu:~# stap -d e1000 -x 1000 test_all_func.stp |
获取当前调用栈,probe的探测点代码位置,触发进程名称、进程id和线程id、执行cpu id,当然时间相关函数也可以打出来;
这些函数基本可以帮助你了解定位该probe点所处于位置和系统进程运行和调用情况;可谓强大之极。
我想了解用户态程序成员变量在某一行的值,我该怎么做?
使用statement定位到具体某一行,然后打印出你关注的变量即可。
eg: 用户态程序如下
root@ubuntu:~# cat test.c
1 | #include <stdio.h> |
gcc -Wall -g -o test ./test.c
stap 脚本如下:
root@ubuntu:~# cat test.stp
1 | probe process("./test").statement("main@./test.c:20") |
如何探测:
步骤1:开启脚本监听
1 | root@ubuntu:~# stap test.stp |
步骤2:运行用户态进程
1 | root@ubuntu:~# stap test.stp |
我用户态的函数是void * 类型,脚本一直报类型不对,我想通过void *指针拿出具体成员,该怎么做?
eg:
如下: 我定义了一个void * 类型的变量q,想在stap脚本中通过q打印出成员变量的值,该怎么做?
root@ubuntu:~# cat test.c
1 | #include <stdio.h> |
脚本:
1 | root@ubuntu:~# cat test.stp |
脚本解释:d变量为转换后的指针,是脚本的局部变量 ;@cast($q,”policy_t”) 方法可以转换指针类型
探测脚本运行:
1 | root@ubuntu:~# stap test.stp |
编译运行:
1 | gcc -Wall -g -o test ./test.c |
怎么做到的? 通过@cast($q,”policy_t”)将void * 类型的指针转换为policy_t *类型的指针,即可
如果存在二级指针,如果通过stap脚本监控其值?
1 | root@ubuntu:~# cat test1.c |
c用户程序
1 | #include <stdio.h> |
stap监控脚本
1 | root@ubuntu:~# cat test1.stp |
脚本运行:
1 | root@ubuntu:~# stap test1.stp |
程序运行:
1 | root@ubuntu:~# ./test1 |
总结:ppt是二级指针,(*ppt)->count 在stap脚本中表现形式是 $ppt[0]->count
如何嵌入c语言?
1 | root@ubuntu:~# cat copy_process.stp |
运行
窗口1:
1 | root@ubuntu:~# stap -g copy_process.stp |
窗口2
1 | ls |
- stap脚本要在花括号前加上% 号
- 获取参数STAP_ARG_前缀
- 返回值用STAP_RETVALUE ,其它情况使用snprintf or strncat将返回值拼进来
- 上述task是指针类型为long
如何修改进程中的变量?
root@ubuntu:~# cat test3.c
1 | #include<stdio.h> |
修改进程中结构变了policy_t中的id
1 | probe process("./test3").statement("main@./test3.c:13") |
执行stap脚本
1 | root@ubuntu:~# stap -g test3.stap |
运行进程程序
1 | root@ubuntu:~# ./test3 |