定义探测点

插入probe的位置称为探测点(probe point)。探测点的定义方法有很多种,这里介绍其中一些经常使用的定义方法。


probe begin

probe end


定义脚本启动时、结束时运行的处理程序。它可以用于脚本内的全局变量的初始化,以及显示结束时收集的日志的情况等。


probe kernel.function("函数名")

probe kernel.function("函数名").return


分别在定义“函数名”所指定的内核函数在调用时、返回时在执行的probe时使用。“函数名”对应的部分也可以使用通配符(*)。


probe kernel.function("init")

probe kernel.function("init@kernel/sched.c")


在第一个示例中,是向所有内核的初始化函数插入probe。在第二个示例中,是向所有定义在kernel/sched.c中的初始化函数插入probe。像这样,也可以通过在函数名后指定文件名来限制对象范围。当不同文件中存在相同名称的函数时,可以使用这个描述限定一个对象函数。另外,在文件名部分也可以使用通配符。

在内核模块中定义probe时描述如下。


probe module("模块名").function("函数名")

probe module("模块名").funciton("函数名").return


只需要将之前写作kernel的地方改写成module("模块名"),其他代码完全相同。


probe syscall.系统调用名

probe syscall.系统调用名.return


这是probe对象作为系统调用时的描述方法。这些是使用别名(alias)功能,对前面所述的kernel.function()进行绑定(wrapping)。在系统调用的情况下,建议使用这个描述方法。


probe kernel.statement("函数名@文件名:行编号")

probe kernel.statement(地址)


这些用于在函数行中插入probe的情况。例如,可以在仅想调试满足函数内if语句的条件的情况时使用。当然,如果在对内核源代码进行了修改、行编号改变的情况下使用,就会出现偏差,因此要注意每次都要确认源代码。这是因为由stap命令从内核的调试信息推测出与行编号对应的地址,决定probe位置。至于地址的指定,由于在每次改变内核的条件进行创建时都会变化,因此需要经常对内核二进制映像文件进行反汇编,找出正确的位置。

同样,以内核模块为对象的描述如下。


probe module("模块名").statement("函数名@文件名:行编号")

probe module("模块名").statement(地址)


在本节的示例中使用这个描述,向nanosleep()在信号中断时运行的代码中插入probe。probe kernel.statement("hrtimer_nanosleep@kernel/hrtimer.c:1599")

这样就可以引用已插入probe的部分的内核源代码。使用这样的方法,还可以生成用来判断函数内if语句的判断结果是真还是假的脚本。


[kernel/hrtimer.c]

1570 long hrtimer_nanosleep(struct timespec*rqtp, struct timespec__user

*rmtp,

1571 const enum hrtimer_mode mode, const clockid_t

clockid)

1572{

……

/如果插入信号中,则该if语句为假/

1584 if(do_nanosleep(&t, mode))

1585 goto out;

……

1599 restart=&current_thread_info()->restart_block;

1600 restart->fn=hrtimer_nanosleep_restart;

1601 restart->nanosleep.index=t.timer.base->index;

1602 restart->nanosleep.rmtp=rmtp;

1603 restart->nanosleep.expires=hrtimer_get_expires_tv64(&t.timer);

1604

1605 ret=-ERESTART_RESTARTBLOCK;

1606 out:

1607 destroy_hrtimer_on_stack(&t.timer);

……


如果只是这样在检测点上记录处理方式,就会追踪系统上运行的所有nanosleep(),甚至不需要追踪的命令的运行情况也会被追踪。本次只想以nanosleeptest程序的运行为对象,因此需要进行下列过滤。


if(execname()!="nanosleeptest")next;


execname()函数会返回当前进程的执行命令名称,因此,如果不是nanosleeptest,就会执行next跳过剩下的处理。