使用systemtap进行内核跟踪

当我们利用systemtap跟踪内核时,首先需要了解systemtap提供了什么跟踪点,这些跟踪点在systemtap中被称为probe事件。systemtap的语法类似于awk和bpftrace语法,是一种事件驱动的语言。当内核执行到时某一种事件被触发就会处理相应的动作。格式如下:

probe probe-point { statement }

systemtap中支持的事件分为很多类型,本文将触发介绍一些最常用的probe类型。查看probe点的命令为:

stap -l *.*           #列出对应格式的事件
stap -L  *.*         #列出对应格式的事件,并且包括支持的详细参数列表
stap -L syscall.*
stap -L 'kernel.trace("*")'
stap -L 'kernel.function("*")'

begin

该事件标志着脚本执行的开始。举例:

#!/usr/bin/env stap
  
probe begin
{
        printf("hello world\n");
}

这个脚本会在运行时打印hello world

end

该事件标志着脚本执行的结束,举例:

#!/usr/bin/env stap
  
probe begin
{
        printf("helle,world\n");
}

probe end
{
        printf("Goodbye,world\n");
}

这个脚本会在开始时打印"helle,world",结束时打印"Goodbye,world"。

syscall.{system_call}

系统调用事件,system_call是对应的系统调用名字,如果要看系统中支持所有系统调用事件,可以如下命令:

stap -L syscall.*

比如在脚本中可以这样使用:

probe syscall.openat
{
        if(pid() != stp_pid())
          printf("PID: %d\tNAME: %s\tARGSTR: %s\n",pid(), name, argstr);
}

如果要跟踪一个系统调用返回,那么只需要在后面加上return后缀,例如:syscall.openat.return

kernel.trace(“tracepoint”)

这个类型用于跟踪内核中tracepoint事件,通过如下命令查看systemtap支持的tracepoint:

stap -L 'kernel.trace("*")'

举例跟踪sched:sched_switch事件,查看-L列出的信息:

kernel.trace("sched:sched_switch") $preempt:bool $prev:struct task_struct* $next:struct task_struct*

通过列表出的信息可以看到后面能够引用的变量有$preempt、 $prev和 $next::

probe kernel.trace("sched:sched_switch")
{
	printf("prev pid: %d next pid:%d\n", $prev->pid, $next->pid);
}

vfs.{file_operation}

通过如下命令查看systemtap支持的vfs事件:

stap -l vfs.*

在我的机器上,输出如下所示:

vfs.__add_to_page_cache
vfs.__set_page_dirty_buffers
vfs.add_to_page_cache
vfs.buffer_migrate_page
vfs.do_mpage_readpage
vfs.do_sync_read
vfs.do_sync_write
vfs.open
vfs.read
vfs.readv
vfs.remove_from_page_cache
vfs.write
vfs.writev

kernel.function(“function”)

这个类型用于跟踪内核函数,查看支持的所有函数:

stap -L 'kernel.function("*")'

如果要想跟踪一个函数的kretprobe点,只需要在后面加上 return后缀,例如:kernel.function(“function”).return

module(“module”).function(“function”)

和上面的类似,不过这个功能追踪的函数只限定于特定的module中。比如:

probe module("ext3").function("*") { }
probe module("ext3").function("*").return { }

timer.*

timer定时器类型,常用的具体事件:

timer.hz(hertz)
timer.jiffies(jiffies)
timer.s(seconds)
timer.ms(milliseconds)
timer.us(microseconds)
timer.ns(nanoseconds)
timer.ms(200).randomize(50)   # 每隔 200 毫秒触发一次,带有线性分布的随机附加时间(-50 到 +50)

快速查询

这里罗列一些probe点,便于后续快速查询:

begin // 在脚本开始时触发
end   // 在脚本结束时触发
kernel.syscall.* // 执行系统调用时触发
kernel.trace(PATTERN) //执行到内核tracepoin时触发
kernel.function(PATTERN)        // 在函数执行时触发
kernel.function(PATTERN).call  //在函数执行时触发
kernel.function(PATTERN).return  //在函数返回时触发
kernel.function(PATTERN).inline  //inline函数处执行
kernel.function(PATTERN).label(LPATTERN) //执行到label时触发
module(MPATTERN).function(PATTERN)    //执行到特定module特定函数时触发
module(MPATTERN).function(PATTERN).call //执行到特定module特定函数时触发
module(MPATTERN).function(PATTERN).return //执行到特定module特定函数返回时触发
kernel.statement(*@kernel/time.c:296) // 探测到确切的代码行
module("virtio_net").statement("receive_buf@drivers/net/virtio_net.c:1346")  // 探测到确切的代码行

你可能感兴趣的:(systemtap)