定义探测点
插入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跳过剩下的处理。