systemtap能做什么?第一篇

systemtap是一个强大的工具,笔者本次主要是发现其能力,为后续操作使用做下积淀积累。请见下文。

probe

“probe” \<=> “探测”, 是SystemTap进行具体地收集数据的关键字。
“probe point” 是probe动作的时机,也称探测点。也就是probe程序监视的某事件点,一旦侦测的事件触发了,则probe将从此处插入内核或者用户进程中。

探测点语法

1
2
3
4
5
6
kernel.function(PATTERN)
kernel.function(PATTERN).call
kernel.function(PATTERN).return
kernel.function(PATTERN).retutrn.maxactive(VALUE)
kernel.function(PATTERN).inline
kernel.function(PATTERN).label(PATTERN)
  • return 返回点探测
  • return.maxactive(VALUE)修饰return,控制同时探测多少个实例,默认足够一搬不用,如果出现了跳过探测现象且很多,可以使用此参数,提升探测效果
  • .call 函数被调用时触发此调用点
  • .inline 内联函数需要展示时候用此参数
  • .label 内核常常用到goto函数,用此标签可以探测出具体的goto返回点
1
2
3
4
module(MPATTERN).function(PATTERN)
moudle(MPATTERN).function(PATTERN).call
moudle(MPATTERN).function(PATTERN).return.maxactive(VALUE)
moudle(MPATTERN).function(PATTERN).inline
1
2
kernel.statement(PATTERN)
kernel.statement(ADDRESS).absolute
  • statement定位到具体的line或者函数,将这些定位点作为跟踪点
1
moudle(MPATTERN).statement(PATTERN)
1
2
3
4
5
process(PROCESSPATH).function(PATTERN)
process(PROCESSPATH).function(PATTERN).call
process(PROCESSPATH).function(PATTERN).return
process(PROCESSPATH).function(PATTERN).inline
process(PROCESSPATH).statement(PATTERN)

PATTERN

func[@file]

func@file:linenumber

eg:

1
2
3
kernel.function("*int*")
kernel.function("*")
kernel.function("__netif_receive_skb_core")

我当前内核有哪些函数?

1
2
root@ubuntu:~# stap -l 'kernel.function("*")'|grep __netif_receive_skb_core
kernel.function("__netif_receive_skb_core@/build/linux-W6HB68/linux-4.4.0/net/core/dev.c:3828")

我当前内核有哪些变量?

1
2
root@ubuntu:~# stap -L 'kernel.function("__netif_receive_skb_core")'
kernel.function("__netif_receive_skb_core@/build/linux-W6HB68/linux-4.4.0/net/core/dev.c:3828") $skb:struct sk_buff* $pfmemalloc:bool

我想知道__netif_receive_skb_core被调用了几次?

1
root@ubuntu:~# cat tanche.stp
1
2
3
4
5
6
7
8
9
10
11
global count=0

probe kernel.function("__netif_receive_skb_core") {
count++
if (count % 5 == 0)
printf( "sys_sync called %d times\n", count);
}

probe timer.ms(10000){
printf(" %d times\n\n",count);
}

每收5个数据包,打印一次,如果没有收到5个数据包,且时间过了约10s,也打印一次

执行结果:

1
2
3
4
5
6
root@ubuntu:~# stap tanche.stp
0 times
sys_sync called 5 times
sys_sync called 10 times
sys_sync called 15 times
^Croot@ubuntu:~#

如何打印内核函数的返回值?

1
2
root@ubuntu:~# stap -e 'probe kernel.function("__netif_receive_skb_core").return { printf("__netif_receive_skb_core return: :%d\n",$return) exit() }'
__netif_receive_skb_core return: :0

如何使用stap知晓当前函数在哪丢包的?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
static int__netif_receive_skb_core(struct sk_buff *skb, bool pfmemalloc)
{
struct packet_type *ptype, *pt_prev;
...
goto out;
}

#ifdef CONFIG_NET_CLS_ACT
if (skb->tc_verd & TC_NCLS) {
skb->tc_verd = CLR_TC_NCLS(skb->tc_verd);
goto ncls;
}
#endif

if (pfmemalloc)
goto skip_taps;

list_for_each_entry_rcu(ptype, &ptype_all, list) {
if (pt_prev)
ret = deliver_skb(skb, pt_prev, orig_dev);
pt_prev = ptype;
}

...

if (pt_prev) {
if (unlikely(skb_orphan_frags(skb, GFP_ATOMIC)))
goto drop;
else
ret = pt_prev->func(skb, skb->dev, pt_prev, orig_dev);
} else {
drop:
atomic_long_inc(&skb->dev->rx_dropped);
kfree_skb(skb);
/* Jamal, now you will not able to escape explaining
* me how you were going to use this. :-)
*/
ret = NET_RX_DROP;
}

out:
return ret;
}
1
2
stap -e 'probe kernel.function("__netif_receive_skb_core").label("drop") { printf("__netif_receive_skb_core drop\n")  }’
stap -e 'probe kernel.function("__netif_receive_skb_core").label("out") { printf("__netif_receive_skb_core out\n") }'

linux函数使用goto处理函数返回点再正常不过了,此时如果你使用return探测丢包点,难达预期效果。只有将丢包点精准到goto语句的label标签,才可以发现丢包位置。

(之前对stap 的probe理解不深刻,也不会这样用,也不知曾为找具体丢包点挠过多少回头皮)

谁调用了__netif_receive_skb_core 收了我的数据包?

首先列出__netif_receive_skb_core所在的代码位置

1
2
root@ubuntu:~# stap -L  'kernel.function("__netif_receive_skb_core")'
kernel.function("__netif_receive_skb_core@/build/linux-W6HB68/linux-4.4.0/net/core/dev.c:3828") $skb:struct sk_buff* $pfmemalloc:bool
1
stap -e 'probe kernel.statement("*@net/core/dev.c:3829") {printf(" : %s\n", execname()) }'

qume虚拟机进程创建出了错,该怎么办?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
root@compute-001:~# cat qumetanche.stp
probe begin {
printf("start moniting qemu clone syscall...\n")
}

probe kernel.function("sys_clone") {
if (execname() == "qemu-system-x86") {
printf("sys_clone : %s\n", execname())
}
}

probe kernel.function("sys_clone").return {
if (execname() == "qemu-system-x86") {
printf("sys_clone_return : %s, %d\n", execname(), $return)
if ($return < 0)
printf("[error]sys_clone_return : %s, %d\n", execname(), $return)
}
}

进程在创建时候,会调用sys_clone,通过过滤调用该函数的执行者,可以定位到qemu-system-x86服务建立的进程,然后跟踪建立过程中返回值确认创建失败原因

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
root@compute-001:~# stap qumetanche.stp
start moniting qemu clone syscall...
sys_clone : qemu-system-x86
sys_clone_return : qemu-system-x86, 29946
sys_clone : qemu-system-x86
sys_clone_return : qemu-system-x86, 29947
sys_clone : qemu-system-x86
sys_clone_return : qemu-system-x86, 29948
sys_clone : qemu-system-x86
sys_clone_return : qemu-system-x86, 29949
sys_clone : qemu-system-x86
sys_clone_return : qemu-system-x86, 29950
sys_clone : qemu-system-x86
sys_clone_return : qemu-system-x86, 29951
sys_clone : qemu-system-x86
sys_clone_return : qemu-system-x86, 29952
sys_clone : qemu-system-x86
sys_clone_return : qemu-system-x86, 30088
sys_clone : qemu-system-x86

我想研究一个内核(信号处理过程)过程该怎么做?

步骤1:找到sys_kill函数所在的源文件

1
2
root@ubuntu:~# stap -l 'kernel.function("sys_kill")'
kernel.function("SyS_kill@/build/linux-W6HB68/linux-4.4.0/kernel/signal.c:2847")

得知信号处理函数所在的内核源代码路径为:/build/linux-W6HB68/linux-4.4.0/kernel/signal.c

因为不同平台会有不同的内核路径,首先要做的事情就是,先找到具体内核路径

步骤2:根据步骤1找到的内核路径,探测该路径下所有的函数的调用和返回并打出结果

-x -x PID sets target() to PID, 脚本里会用到此参数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
root@ubuntu:~# stap -x 15365 signal.stp
WARNING: function signals_init is in blacklisted section: keyword at signal.stp:5:1
source: probe kernel.function("*@/build/linux-W6HB68/linux-4.4.0/kernel/signal.c").call {
^
WARNING: function setup_print_fatal_signals is in blacklisted section: keyword at :5:1
source: probe kernel.function("*@/build/linux-W6HB68/linux-4.4.0/kernel/signal.c").call {
^
begin

0 bash(15365): -> get_signal,pid() 15365 target() 15365
5 bash(15365): -> get_signal,pid() 15365 target() 15365
8 bash(15365): -> dequeue_signal,pid() 15365 target() 15365
9 bash(15365): -> dequeue_signal,pid() 15365 target() 15365
11 bash(15365): -> __dequeue_signal,pid() 15365 target() 15365
13 bash(15365): -> __dequeue_signal,pid() 15365 target() 15365
14 bash(15365): -> __dequeue_signal,pid() 15365 target() 15365
16 bash(15365): -> __dequeue_signal,pid() 15365 target() 15365
18 bash(15365): -> __sigqueue_free,pid() 15365 target() 15365
19 bash(15365): -> __sigqueue_free,pid() 15365 target() 15365
22 bash(15365): -> recalc_sigpending,pid() 15365 target() 15365
23 bash(15365): -> recalc_sigpending,pid() 15365 target() 15365
25 bash(15365): -> recalc_sigpending_tsk,pid() 15365 target() 15365
27 bash(15365): -> recalc_sigpending_tsk,pid() 15365 target() 15365
31 bash(15365): -> signal_setup_done,pid() 15365 target() 15365
33 bash(15365): -> signal_setup_done,pid() 15365 target() 15365
34 bash(15365): -> __set_current_blocked,pid() 15365 target() 15365
36 bash(15365): -> __set_current_blocked,pid() 15365 target() 15365
38 bash(15365): -> __set_task_blocked,pid() 15365 target() 15365
39 bash(15365): -> __set_task_blocked,pid() 15365 target() 15365
41 bash(15365): -> recalc_sigpending,pid() 15365 target() 15365

有人用stap分析内存泄漏和重复释放,记录下来吧

步骤1:分析要要用的c语言源码,并编译

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
root@ubuntu:~# cat mem_test.c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

int main(int argc, char *argv[])
{
char *p1;
char *p2;
char *p3;
char *p4;

sleep(20);//让程序sleep 20s是因为我们程序先起来之后,等待SystemTap启动设置探测点
p1 = malloc(500);
p2 = malloc(200);
p3 = malloc(300);
p4 = malloc(300);//泄漏
free(p1);
free(p2);
free(p3);
free(p2);//重复释放
printf("p1: %p, p2: %p, p3: %p, p4: %p\n", p1, p2, p3, p4);
return 0;
}

编译:

1
gcc -g mem_test.c -o main

步骤2: 探测内存泄漏和重复释放的脚本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
root@ubuntu:~# cat mem.stp
probe begin {
printf("=============begin============\n")
}

//记录内存分配和释放的计数关联数组
global g_mem_ref_tbl
//记录内存分配和释放的调用堆栈关联数组
global g_mem_bt_tbl

probe process("/lib/x86_64-linux-gnu/libc.so.6").function("__libc_malloc").return, process("/lib/x86_64-linux-gnu/libc.so.6").function("__libc_calloc").return {
if (target() == pid()) {
if (g_mem_ref_tbl[$return] == 0) {
g_mem_ref_tbl[$return]++
g_mem_bt_tbl[$return] = sprint_ubacktrace()
}
}
}

probe process("/lib/x86_64-linux-gnu/libc.so.6").function("__libc_free").call {
if (target() == pid()) {
g_mem_ref_tbl[$mem]--

if (g_mem_ref_tbl[$mem] == 0) {
if ($mem != 0) {
//记录上次释放的调用堆栈
g_mem_bt_tbl[$mem] = sprint_ubacktrace()
}
} else if (g_mem_ref_tbl[$mem] < 0 && $mem != 0) {
//如果调用free已经失衡,那就出现了重复释放内存的问题,这里输出当前调用堆栈,以及这个地址上次释放的调用堆栈
printf("----------------------------------------------\n")
printf("[%p] has been freed : %d\n", $mem, g_mem_ref_tbl[$mem])
printf("who free this memory at error moment ? you can see the stack ")
print_ubacktrace()

printf("haha,The memory has been freed by : \n")
printf("%s\n", g_mem_bt_tbl[$mem])
printf("----------------------------------------------\n")
}
}
}

probe end {
//最后输出产生泄漏的内存是在哪里分配的
printf("=============end============\n")
foreach(mem in g_mem_ref_tbl) {
if (g_mem_ref_tbl[mem] > 0) {
printf("[%p] is not free ,but you malloc it in %s ,so This is memory Loss!!!!\n", mem, g_mem_bt_tbl[mem])
}
}
}

脚本分析:脚本主要记录下内存申请点和释放点,这样就可以很容易找到内存重复释放和泄漏点了

步骤3: 探测过程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
root@ubuntu:~# vim mem.stp
root@ubuntu:~# ./main &
[1] 18904
root@ubuntu:~# stap -x 18904 mem.stp
=============begin============
p1: 0xa28010, p2: 0xa28210, p3: 0xa282e0, p4: 0xa28420
*** Error in `./main': double free or corruption (!prev): 0x0000000000a28210 ***
======= Backtrace: =========
/lib/x86_64-linux-gnu/libc.so.6(+0x777e5)[0x7f1d146897e5]
/lib/x86_64-linux-gnu/libc.so.6(+0x8037a)[0x7f1d1469237a]
/lib/x86_64-linux-gnu/libc.so.6(cfree+0x4c)[0x7f1d1469653c]
./main[0x40069c]
/lib/x86_64-linux-gnu/libc.so.6(__libc_start_main+0xf0)[0x7f1d14632830]
./main[0x400529]
======= Memory map: ========
00400000-00401000 r-xp 00000000 fc:00 526710 /root/main
00600000-00601000 r--p 00000000 fc:00 526710 /root/main
00601000-00602000 rw-p 00001000 fc:00 526710 /root/main
00a28000-00a49000 rw-p 00000000 00:00 0 [heap]
7f1d10000000-7f1d10021000 rw-p 00000000 00:00 0
7f1d10021000-7f1d14000000 ---p 00000000 00:00 0
7f1d143fc000-7f1d14412000 r-xp 00000000 fc:00 786953 /lib/x86_64-linux-gnu/libgcc_s.so.1
7f1d14412000-7f1d14611000 ---p 00016000 fc:00 786953 /lib/x86_64-linux-gnu/libgcc_s.so.1
7f1d14611000-7f1d14612000 rw-p 00015000 fc:00 786953 /lib/x86_64-linux-gnu/libgcc_s.so.1
7f1d14612000-7f1d147d2000 r-xp 00000000 fc:00 791185 /lib/x86_64-linux-gnu/libc-2.23.so
7f1d147d2000-7f1d149d2000 ---p 001c0000 fc:00 791185 /lib/x86_64-linux-gnu/libc-2.23.so
7f1d149d2000-7f1d149d6000 r--p 001c0000 fc:00 791185 /lib/x86_64-linux-gnu/libc-2.23.so
7f1d149d6000-7f1d149d8000 rw-p 001c4000 fc:00 791185 /lib/x86_64-linux-gnu/libc-2.23.so
7f1d149d8000-7f1d149dc000 rw-p 00000000 00:00 0
7f1d149dc000-7f1d14a02000 r-xp 00000000 fc:00 791163 /lib/x86_64-linux-gnu/ld-2.23.so
7f1d14bf2000-7f1d14bf5000 rw-p 00000000 00:00 0
7f1d14bfe000-7f1d14c01000 rw-p 00000000 00:00 0
7f1d14c01000-7f1d14c02000 r--p 00025000 fc:00 791163 /lib/x86_64-linux-gnu/ld-2.23.so
7f1d14c02000-7f1d14c03000 rw-p 00026000 fc:00 791163 /lib/x86_64-linux-gnu/ld-2.23.so
7f1d14c03000-7f1d14c04000 rw-p 00000000 00:00 0
7ffc8d97f000-7ffc8d9a0000 rw-p 00000000 00:00 0 [stack]
7ffc8d9d3000-7ffc8d9d5000 r--p 00000000 00:00 0 [vvar]
7ffc8d9d5000-7ffc8d9d7000 r-xp 00000000 00:00 0 [vdso]
7fffffffe000-7ffffffff000 --xp 00000000 00:00 0 [uprobes]
ffffffffff600000-ffffffffff601000 r-xp 00000000 00:00 0 [vsyscall]
WARNING: Missing unwind data for a module, rerun with 'stap -d /root/main'
WARNING: Missing unwind data for a module, rerun with 'stap -d /lib/x86_64-linux-gnu/ld-2.23.so'
----------------------------------------------
[0xa28210] has been freed : -1
who free this memory at error moment ? you can see the stack 0x7f1d146964f0 : free+0x0/0x1d0 [/lib/x86_64-linux-gnu/libc-2.23.so]
0x40069c [/root/main+0x69c/0x1000]
haha,The memory has been freed by :
free+0x0 [libc-2.23.so]
0x400684 [main+0x684]
----------------------------------------------

^C=============end============
[0xa28420] is not free ,but you malloc it in 0x400643 [main+0x643] ,so This is memory Loss!!!!
[0xa28560] is not free ,but you malloc it in _IO_file_doallocate+0x55 [libc-2.23.so]
_IO_doallocbuf+0x34 [libc-2.23.so]
_IO_file_overflow@@GLIBC_2.2.5+0x1c8 [libc-2.23.so]
_IO_file_xsputn@@GLIBC_2.2.5+0xad [libc-2.23.so]
_IO_vfprintf+0xd1 [libc-2.23.so]
printf+0x99 [libc-2.23.so]
0x40066c [main+0x66c] ,so This is memory Loss!!!!
[0x7f1d100008c0] is not free ,but you malloc it in 0x7f1d149f8f5a [ld-2.23.so+0x1cf5a] ,so This is memory Loss!!!!
[0x7f1d100008f0] is not free ,but you malloc it in 0x7f1d149e7bf6 [ld-2.23.so+0xbbf6] ,so This is memory Loss!!!!
[0x7f1d10000da0] is not free ,but you malloc it in 0x7f1d149e7ef4 [ld-2.23.so+0xbef4] ,so This is memory Loss!!!!
[0x7f1d10000dd0] is not free ,but you malloc it in 0x7f1d149ea737 [ld-2.23.so+0xe737] ,so This is memory Loss!!!!
[0x7f1d10000e10] is not free ,but you malloc it in 0x7f1d149ee0be [ld-2.23.so+0x120be] ,so This is memory Loss!!!!
[1]+ Aborted (core dumped) ./main

步骤4:结果分析:

a. 申请内存地址如下

1
p1: 0xa28010, p2: 0xa28210, p3: 0xa282e0, p4: 0xa28420

b. 0xa28210 p2 重复释放,重复释放位置 0x40069c [/root/main+0x69c/0x1000] ,因为它已经在0x400684 [main+0x684] 释放过了

1
2
3
4
5
6
[0xa28210] has been freed : -1
who free this memory at error moment ? you can see the stack 0x7f1d146964f0 : free+0x0/0x1d0 [/lib/x86_64-linux-gnu/libc-2.23.so]
0x40069c [/root/main+0x69c/0x1000]
haha,The memory has been freed by :
free+0x0 [libc-2.23.so]
0x400684 [main+0x684]

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
2
3
4
5
6
7
8
9
10
11
12
13
root@ubuntu:~# cat test_all_func.stp
probe begin {
printf("SystemTap scrits start\n");}
probe kernel.function("tcp_v4_rcv"){
printf("skb->len = %d\n ",$skb->len);
printf("cpu %d \n",cpu())
printf("execname %s pid %d tid %d \n",execname(),pid(),tid());
printf("pp %s probefunc %s\n",pp(),probefunc());
printf("gettimeofday_s %d get_cycles %d \n",gettimeofday_s(),get_cycles());
printf("ppfunc %s \n target %d\n ",ppfunc(),target());
print_backtrace();
exit()
}

执行结果如下:具体含义自己体会和尝试

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
root@ubuntu:~# stap -d e1000 -x 1000  test_all_func.stp
SystemTap scrits start
skb->len = 32
cpu 3
execname sshd pid 14479 tid 14479
pp kernel.function("tcp_v4_rcv@/build/linux-W6HB68/linux-4.4.0/net/ipv4/tcp_ipv4.c:1555") probefunc tcp_v4_rcv
gettimeofday_s 1516860301 get_cycles 127888015890393
ppfunc tcp_v4_rcv
target 1000
0xffffffff81791250 : tcp_v4_rcv+0x0/0xa20 [kernel]
0xffffffff8176b414 : ip_local_deliver_finish+0x94/0x1e0 [kernel]
0xffffffff8176b71f : ip_local_deliver+0x6f/0xe0 [kernel]
0xffffffff8176b0f2 : ip_rcv_finish+0x92/0x320 [kernel]
0xffffffff8176ba21 : ip_rcv+0x291/0x3a0 [kernel]
0xffffffff8172c634 : __netif_receive_skb_core+0x704/0xa60 [kernel]
0xffffffff8172c9a8 : __netif_receive_skb+0x18/0x60 [kernel]
0xffffffff8172ca22 : netif_receive_skb_internal+0x32/0xa0 [kernel]
0xffffffff8172d6a3 : napi_gro_receive+0xc3/0x120 [kernel]
0xffffffffc00400d2 : e1000_clean_rx_irq+0x152/0x4c0 [e1000]
0xffffffffc0040722 : e1000_clean+0x262/0x8c0 [e1000]
0xffffffff8172ceee : net_rx_action+0x21e/0x360 [kernel]
0xffffffff81085db1 : __do_softirq+0x101/0x290 [kernel]
0xffffffff8183a30c : do_softirq_own_stack+0x1c/0x30 [kernel]

获取当前调用栈,probe的探测点代码位置,触发进程名称、进程id和线程id、执行cpu id,当然时间相关函数也可以打出来;

这些函数基本可以帮助你了解定位该probe点所处于位置和系统进程运行和调用情况;可谓强大之极。

我想了解用户态程序成员变量在某一行的值,我该怎么做?

使用statement定位到具体某一行,然后打印出你关注的变量即可。

eg: 用户态程序如下

root@ubuntu:~# cat test.c

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include <stdio.h>
typedef struct str {
int len;
char *data;
} str_t;

typedef struct policy {
str_t name;
int id;
} policy_t;

int main(int argc, char *argv[])
{
policy_t policy;
policy_t *p = &policy;
p->id = 111;
p->name.data = "test";
p->name.len = sizeof("test")-1;

printf(" p->id: %d, p->name.data:[%p] %s, p->name.len: %d\n", p->id, p->name.data, p->name.data, p->name.len);
return 0;
}

gcc -Wall -g -o test ./test.c

stap 脚本如下:

root@ubuntu:~# cat test.stp

1
2
3
4
probe process("./test").statement("main@./test.c:20")
{
printf("p->name->data pointer[%p] policy name: p->name->data %s : p->name->len %d p->id %d \n", $p->name->data,$p->name->data$,$p->name->len,$p->id);
}

如何探测:

步骤1:开启脚本监听

1
2
root@ubuntu:~# stap test.stp
p->name->data pointer[0x4006b8] policy name: p->name->data "test" : p->name->len 4 p->id 111

步骤2:运行用户态进程

1
2
root@ubuntu:~# stap test.stp
p->name->data pointer[0x4006b8] policy name: p->name->data "test" : p->name->len 4 p->id 111

我用户态的函数是void * 类型,脚本一直报类型不对,我想通过void *指针拿出具体成员,该怎么做?

eg:

如下: 我定义了一个void * 类型的变量q,想在stap脚本中通过q打印出成员变量的值,该怎么做?

root@ubuntu:~# cat test.c

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#include <stdio.h>
typedef struct str {
int len;
char *data;
} str_t;

typedef struct policy {
str_t name;
int id;
} policy_t;

int main(int argc, char *argv[])
{
policy_t policy;
policy_t *p = &policy;
void *q=(void*)p;
p->id = 111;
p->name.data = "test";
p->name.len = sizeof("test")-1;

printf("[p:%p] [q:%p] p->id: %d, p->name.data:[%p] %s, p->name.len: %d\n", p,q,p->id, p->name.data, p->name.data, p->name.len);
return 0;
}

脚本:

1
2
3
4
5
6
root@ubuntu:~# cat test.stp
probe process("./test").statement("main@./test.c:21")
{
d =&@cast($q,"policy_t")
printf("policy name: p->name->data %s \n", d->name->data$);
}

脚本解释:d变量为转换后的指针,是脚本的局部变量 ;@cast($q,”policy_t”) 方法可以转换指针类型

探测脚本运行:

1
2
root@ubuntu:~# stap test.stp
policy name: p->name->data "test"

编译运行:

1
2
3
gcc -Wall -g -o test ./test.c
root@ubuntu:~# ./test
[p:0x7ffc5202b240] [q:0x7ffc5202b240] p->id: 111, p->name.data:[0x4006d8] test, p->name.len: 4

怎么做到的? 通过@cast($q,”policy_t”)将void * 类型的指针转换为policy_t *类型的指针,即可

如果存在二级指针,如果通过stap脚本监控其值?

1
root@ubuntu:~# cat test1.c

c用户程序

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include <stdio.h>

struct test {
int count;
};

int main(int argc, char *argv[])
{
struct test t = {.count = 5566};
struct test *pt = &t;
struct test **ppt = &pt;

printf("t.count: %d, pt->count: %d, ppt->count: %d\n", t.count, pt->count, (*ppt)->count);

return 0;
}

stap监控脚本

1
2
3
4
5
root@ubuntu:~# cat test1.stp
probe process("./test1").statement("main@./test1.c:13")
{
printf("$t->count: %d, $pt->count: %d, $ppt->count: %d", $t->count, $pt->count, $ppt[0]->count);
}

脚本运行:

1
2
root@ubuntu:~# stap test1.stp
$t->count: 5566, $pt->count: 5566, $ppt->count: 5566

程序运行:

1
2
root@ubuntu:~# ./test1
t.count: 5566, pt->count: 5566, ppt->count: 5566

总结:ppt是二级指针,(*ppt)->count 在stap脚本中表现形式是 $ppt[0]->count

如何嵌入c语言?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
root@ubuntu:~# cat copy_process.stp
function getprocname:string(task:long)
%{
struct task_struct *task = (struct task_struct *)STAP_ARG_task;
snprintf(STAP_RETVALUE, MAXSTRINGLEN, "pid: %d, comm: %s", task->pid, task->comm);
%}

function getprocid:long(task:long)
%{
struct task_struct *task = (struct task_struct *)STAP_ARG_task;
STAP_RETURN(task->pid);
%}

probe kernel.function("copy_process").return
{
printf("copy_process return: %p, pid: %d, getprocname: %s, getprocid: %d\n", $return, $return->pid, getprocname($return), getprocid($return));
}

运行

窗口1:

1
2
root@ubuntu:~# stap -g copy_process.stp
copy_process return: 0xffff88030ff3aa00, pid: 11976, getprocname: pid: 11976, comm: bash, getprocid: 11976

窗口2

1
ls
  • stap脚本要在花括号前加上% 号
  • 获取参数STAP_ARG_前缀
  • 返回值用STAP_RETVALUE ,其它情况使用snprintf or strncat将返回值拼进来
  • 上述task是指针类型为long

如何修改进程中的变量?

root@ubuntu:~# cat test3.c

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include<stdio.h>
typedef struct policy{
int id;
}policy_t;

int main()
{
policy_t policy;
policy_t *p = &policy;
policy_t **pp ;
p->id =111;
printf("before stap set value,p->id:%d\n",p->id);
pp = &p;
printf("after stap set value,p->id : %d ,(*pp)->id : %d\n",p->id,(*pp)->id);
return 0;
}

修改进程中结构变了policy_t中的id

1
2
3
4
5
probe process("./test3").statement("main@./test3.c:13")
{
$p->id=222;
printf("$p$: %s\n",$p$)
}

执行stap脚本

1
2
root@ubuntu:~# stap -g test3.stap
$p$: {.id=222}

运行进程程序

1
2
3
root@ubuntu:~# ./test3
before stap set value,p->id:111
after stap set value,p->id : 222 ,(*pp)->id : 222