英文原文:http://sourceware.org/systemtap/langref/Probe_points.html
译者:林永听
注:本系列文章为作者连载作品,请勿转载,否则视为侵权。
4 探测点
探测点采用点分格式的语法,事件命名空间划分成多个部分,类似于域名系统。每部分可以由字符串或数字等字面值来参数化,与函数调用的语法格式十分相似。
下面是符合语法规则的探测点:
kernel.function("foo")
kernel.function("foo").return
module{"ext3"}.function("ext3_*")
kernel.function("no_such_function") ?
syscall.*
end
timer.ms(5000)
在某种程度上,探测点可分成同步 和异步 两大类。当CPU 执行到探针指定的指令时,产生一个同步事件,探针可利用引用点(指令地址)来获取更多的上下文数据。另一探测点家族与异步事件相关,如定时器。与同步探测点不同的是,它没有固定的引用点。每个探测点可以指定匹配多个位置,例如使用通配符或多个探针别名,多个位置均被探测。在探针声明中,使用逗句作为分隔符来指定多个探测位置。
探测点的前缀阐明了探测目标,如kernel ,module 和timer ,等等。
后缀进一步细化了探测点,例如.return 探测函数的退出点。缺少后缀时意味着探测函数的进入点。
探测点各部分若包含星号字符(*) ,则扩展为与之匹配的探测点。请看下述例子:
kernel.syscall.*
kernel.function("sys_*)
如果探测点后面跟随一个问号(?) 字符,表明这是可选探测点,即使它扩展失败,也不会导致错误。从顶层开始到底层,各层的别名和通配符扩展同样遵循此法则。
可选探测点的语法形式如下:
kernel.function("no_such_function") ?
探测点家族使用目标内核或模块的符号调试信息(symbolic debugging information) ,这些信息可包含在未经stripped 的可执行文件,或在一个独立的debuginfo 软件包中。通过指源或目标代码点的集合,探针可在逻辑上定位到目标执行路径。当任一处理器执行与之匹配的语句,探针处理函数将在些上下文中运行。
内核点(points in a kernel) 可由模块,源文件,行号,函数名或者它们的组合来定位。
下述是目前支持的探测点清单:
kernel.function(PATTERN)
kernel.function(PATTERN).call
kernel.function(PATTERN).return
kernel.function(PATTERN).return.maxactive(VALUE)
kernel.function(PATTERN).inline
kernel.function(PATTERN).label(LPATTERN)
module(MPATTERN).function(PATTERN)
module(MPATTERN).function(PATTERN).call
module(MPATTERN).function(PATTERN).return.maxactive(VALUE)
module(MPATTERN).function(PATTERN).inline
kernel.statement(PATTERN)
kernel.statement(ADDRESS).absolute
module(MPATTERN).statement(PATTERN)
.function 变体使探针定位在命名函数的开始之处,因此探针可用上下文变量的方式来获取函数参数。
.return 变体让探针定位到命名函数返回的那一时刻,因此,探针可能过上下文变量$return 来获取函数的返回值。探针仍然可以获得函数的参数,但此时它们的值可能在函数执行过程中发生了变化。可以使用.maxactive 进一步修饰return 探针,它指定该函数有多少个实例可以同时被探测。大多数情况下,不需要指定.maxactive , 默认情况已足够使用了。然而,如果被忽略的探针过多,可以尝试将.maxactive 调高,再看看被忽略的探针是否减少。
.inline 修饰符使.function 变体过滤出那些仅为内联函数的实例,而.call 修饰符刚好选择相反的子集。内联函数没有唯一的返回点,因此.inline 探针不支持.return 后缀。
.statement 变体使探针探测到确切的代码行,函数内的局部变量对探针来说是可见的。
在上述探针描述中,MPATTERN 是一个字符串字面值,它标识加载的内核模块;LPATTERN 代表源程序标签。MPATTERN 和LPATTERN 两者均可包含星号(*) ,方括号“[] ”和问号(?) 等通配符。
PATTERN 是一个字符串字面值,它标识程序中的代码点。它由3 部分构成。
- 第一部分是函数名字,该名字与nm 工具的输出一致。此部分可使用星号和问号通配符来匹配多个函数名字。
- 第二部分是可选的,它以@ 字符开头,紧跟着此函数所在源文件的路径。此路径可以包含通配符模式,如mm/slab* 。大多数情况下,路径名应为从Linux 代码树顶层目录开始的相对路径,尽管某些内核要求使用绝对路径。如果相对路径不能工作,尝试使用绝对路径。
- 如果给定文件名,第三部仍是可选的。它以“: ”或“+ ”开头,用来标识源文件的行号。”:” 后面跟的是绝对行号,而”+” 后面跟的是函数入口的相对行号。”:*” 匹配函数的每一行,而”:x-y” 可以从x 行匹配到y 行。
另外,PATTERN 指定为数字常量时,它表示模块的相对地址或内核的绝对地址。
部分在编译单元内可见的源代码级别变量,诸如函数参数,局部或全局变量,在探针处理函数内同样是可见的。在脚本里使用美元符号($) 加上它们的名字就可以引用这些变量。此外,特殊的语法可防止无节制地遍历结构体,指针和数组。
$var 引用可见(in-scope )变量var 。如果它的类型是整数类型(译者注:即char, short, int, long 这些类型),脚本会把它转换为64 位的整数。如果指针的类型是字符串(char *) ,脚本会使用kernel_string() 和user_string() 函数将它拷贝到SystemTap 的字符串变量。
$var->field 遍历结构体的field 成员。可重复使用-> 操作符沿着子指针链访问各级成员。
$var[N] 访问数组的元素,下标由N 指定。下标只能是字面值整数。
$$vars 扩展为字符串并等价于sprintf("parm1=%x ... parmN=%x var1=%x ... varN=%x", $parm1, ..., $parmN, $var1, ..., $varN)
$$locals 扩展为字符串并等价于sprintf("var1=%x ... varN=%x", $var1, ..., $varN) 。
$$parms 扩展为字符串并等价于sprintf("parm1=%x ... parmN=%x", $parm1, ..., $parmN) 。
4.2.1 kernel.function, module().function
.function 变体将探针定位到命名函数开始之处,因此function 探针可用方问上下文变量的方式来访问函数的参数。
一般语法形式:
kernel.function("func[@file]"
module("modname").function("func[@file]"
例子:
# 引用内核所有名字具有init 或exit 字符串的函数。
kernel.function("*init*"), kernel.function("*exit*")
# 引用文件kernel/sched.c 内跨越第240 行的函数。
kernel.function("*@kernel/sched.c:240")
# 引用模块ext3 内的所有函数
module("ext3").function("*")
4.2.2 kernel.statement, module().statement
.statement 变体允许探针定位到确切的代码行,此代码行可见的变量均可被脚本访问。
一般语法形式如下:
kernel.statement("func@file:linenumber")
module("modname").statement("func@file:linenumber")
例子:
# 引用文件kernel/sched.c 内第2917 行这一语句:
kernel.statement("*@kernel/sched.c:2917")
# 引用文件fs/bio.c 内bio_init+3 这一行语句:
kernel.statement("bio_init@fs/bio.c+3")
当目标内核或模块缺少调试信息时,你仍然可以使用kprobe 家族探针来探测它们函数的进入点和退出点。但使用此种探针时你不能获取函数参数或局部变量的值。然而当你使用这方法时,systemTap 仍然为你提供了一种访问参数的方法:
当函数因被探测而停滞在它的进入点时,可以使用编号来引用它的参数。例如,假设被探测函数声明如下:
asmlinkage ssize_t sys_read(unsigned int fd, char __user * buf, size_t
count)
可以分别使用uint_arg(1) ,pointer_arg(2) 和ulong_arg(3) 来获得fd, buf 和count 的值。此时,探针处理函数必须先调用asmlinkage() ,因为在某些架构里,asklinkage 属性影响函数参数的传递方式。
译者注:例子中的sys_read 函数在定义时使用了asmlinkage 属性,在不同的CPU 架构上有不同的参数传递方式,例如使用寄存器和堆栈一起传递参数。在我们熟悉的x86CPU 上,asklinkage 修饰符的要义是通过堆栈来传递函数参数。因此在systemTap 脚本里,需要调用asklinkage() 函数来根据CPU 架构来初始化一系列数据,好让后面的type_arg(N) 调用知道在寄存器还是堆栈里获得参数的值。
当函数因被探测而停滞在它的退出点时,此种非DWARF 探针不支持$return 目标变量。但可以通过调用returnval() 来获得寄存器的值,函数的返回值通常是保持在这一寄存器里的,也可调用returnstr() 来获得返回值的字符串形式。
在处理函数代码里面,可调用register("regname") 来获得它被调用时特定CPU 寄存器的值。u_register("regname") 类似于register("regname") ,不同的是它将寄存器的值解释成无符号整数。
SystemTap 支持下述的kprobe 结构:
kprobe.function(FUNCTION)
kprobe.function(FUNCTION).return
kprobe.module(NAME).function(FUNCTION)
kprobe.module(NAME).function(FUNCTION).return
kprobe.statement(ADDRESS).absolute
.function 探针探测内核函数,而.module 探针探测特定模块的函数。如果知道内核或模块函数的绝对地址,可使用.statement 探针。注意,FUNCTION 和MODULE 名字中不能出现通配符,通配符引致探针不能注册。同时statement 探针只能运行在guru 模式下。
要支持用户空间探测,只需将kernel 配置成包括utrace 扩展即可。本文撰写之时,Red Hat 和CentOS 发行版的内核已支持utrace 了。关于utrace 更多的信息,请参阅http://people.redhat.com/roland/utrace/ 。
用户空间探测有几种形式。无调试符号的探测点,如process(PID).statement(ADDRESS).absolute 类似于kernel.statement(ADDRESS).absolute ,它们都使用原始的(raw) 、未经验证的虚拟地址,并且不能使用$variable 目标变量。目标PID 参数必须是正在运行的进程,ADRESS 必须是一个有效的指令地址。进程里的所有线程均被探测。此探针只能运行guru 模式下。
你可探测无调试符号的用户- 内核接口事件,这些事件由utrace 进行处理。可以通过下述的方式来探测:
process(PID).begin
process("PATH").begin
process.begin
process(PID).thread.begin
process("PATH").thread.begin
process.thread.begin
process(PID).end
process("PATH").end
process.end
process(PID).thread.end
process("PATH").thread.end
process.thread.end
process(PID).syscall
process("PATH").syscall
process.syscall
process(PID).syscall.return
process("PATH").syscall.return
process.syscall.return
process(PID).insn
process("PATH").insn.block
process(PID).insn.block
process("PATH").insn
process("PATH").mark("LABEL")
process("PATH").function("NAME")
process("PATH").statement("*@FILE.c:123")
process("PATH").function("*").return
process("PATH").function("myfun").label("foo")
当PID 或PATH 描述的进程被创建时,.begin 变体探针会被调用。如果不指定PID 或PATH (如process.begin ),任何新进程的繁衍都会调用该探针。
当PID 或PATH 描述的线程被创建时,.thread.begin 变体探针会被调用。
当PID 或PATH 描述的进程结束时,.end 变体探针会被调用。
当PID 或PATH 描述的线程结束时,.thread.end 变体探针会被调用。
当PID 或PATH 描述的线程进行系统调用时,.syscall 变体探针会被调用。系统调用编号可在$syscall 上下文变量中获得。系统调用的前6 个参数可从$argN 目标变量中获取,即$arg1, $arg2 等。
当PID 或PATH 描述的线程从系统调用中返回时,.syscall.return 变体探针被调用。系统调用编号同样可以$syscall 上下文变量中获得,而系统调用的返回值可在$return 上下文变量中获得。
.mark 变体探针由应用程序定义的静态探针来调用,更多信息请参阅4.4.1 节。
除此之外,用户空间程序和共享库支持带完整调试符号的源代码级别的探针。它们十分类似于上述基于DWARF 带调试符号的内核或模块探针,并且访问上下文$ 变量的方式也很相似。
process("PATH").function("NAME")
process("PATH").statement("*@FILE.c:123")
process("PATH").function("*").return
process("PATH").function("myfun").label("foo")
对于所有进程探针,PATH 名字引用可执行文件,执行文件的搜索方式和shell 的完全一致:要么明确指定该可以执行文件的路径,要么指定从当前工作目录开始的相对路径,好PATH 参数以./ 字符串开始。否则从$PATH 环境变量指的目录中搜索。下述是探针语法的例子:
probe process("ls").syscall {}
probe process("./a.out").syscall {}
等价于下述的探针:
probe process("/bin/ls").syscall {}
probe process("/my/directory/a.out").syscall {}
如果进程探针没有指定PID 或PATH 参数,那么所有用户空间线程将被探测。然而,如果systemTap 以目标进程模式(target process mode) 运行(invoked) ,进程探针仅限于探测目标进程家族树里的那些进程。
目标进程模式(使用-c CMD 或-x PID 选项运行stap )蕴含所有的process.* 探针只能局限于探测给定的进程和它的子进程,但不影响kernel.* 和其它的探针类型。通常而言,CMD 字符串是运行程序的名称,而不是”/bin/sh –c” 子shell 程序的名字,因为utrace 和uprobe 探针会(从内核)接收到相当“干净”的事件流。如果CMD 中出现元字符,如重定向操作符,要求使用”/bin/sh –c CMD” 形式的名称,届时utrace 和uprobe 探针将从shell 中接收事件。请看下述例子:
% stap -e 'probe process.syscall, process.end {
printf("%s %d %s/n", execname(), pid(), pp())}' /
-c ls
下述是这个命令的一种输出:
ls 2323 process.syscall
ls 2323 process.syscall
ls 2323 process.end
如果PATH 名字为共享库, 那么所有映射该共亨库的进程均被探测。若安装了带dwarf 调试信息的版本,尝试下述语法命令:
probe process("/lib64/libc-2.8.so").function("....") { ... }
此命令探测所有调用进共享库里面的线程,键入”stap –c CMD” 或”stap –x PID” 将之限制到仅探测某一命令和它的子孙进程。这里同样可使用$$var 和其它变量。可以使用-d DIRECTORY 选项告知stap 命令带调试信息文件的位置。
Process().insn 和process().insn.block 探针依次检查进程每个执行的指令或区块。但此探针仅在部分平台上实现,因此如果你所使用的系统没有实现该探针,那么在启动脚本时会收到错误信息。
PID 或PATH 描述的进程每执行一个单步指令,.insn 探针都会被调用。
PID 或PATH 描述的进程每执行一个区块指令,.insn.block 探针都会被调用。
若想统计进程执行的指令总数,可以使用类似下述的命令:
$ stap -e 'global steps; probe process("/bin/ls").insn {steps++}
probe end {printf("Total instructions: %d/n", steps);}' /
-c /bin/ls
但使用此特性会使进程执行速度放慢很多。
你可以探测程序的静态符号测量仪(instrumentation) ,只需将此测量信编译进编程或共享库,使用下述语法即可:
process("PATH").mark("LABEL")
.mark 变体由静态探针调用,而该静态探针是由应用程序使用STAP_PROBE1(handle,LABEL,arg1) 预先定义的。STAP_PROBE1 定义在sdt.h 文件中,参数如下:
参数 | 描述 |
|
handle | 应用程序句柄(handle) |
|
LABEL | 对应.mark 探针的参数 |
|
arg1 | 参数(译者注:传递给探针的参数) |
|
STAP_PROBE1 为探针提供1 个参数,STAP_PROBE2 可提供2 个,依次类推。探针可通过上下文变量$arg1, $arg2 等来获取参数。
此外,可以利用dtrace 脚本定制新的STAP_PROBE 宏。Sdt.h 文件使用DTRACE_PROBE 提供了兼容dtrace 的marker 和与之对应的python 脚本。你可直接使用这些基于dtrace 的内置的宏,只需将dtrace –h 或-G 功能打开即可。
下述是一个用户空间支持符号探测的原型例子:
# stap -e 'probe process("ls").function("*").call {
log (probefunc()." ".$$parms)
}' /
-c 'ls -l'
此脚本需要命令程序带有调试信息并且内核支持utrace 才能运行。如果看见“pass 4a-time ”这样的构建失败信息,请确保你的内核支持utrace 。
此类探测点允许探测procfs 伪文件系统中/proc/systemtap/MODNAME 目录下文件的创建,读和写,其中NODNAME 为sytemTap 模块的名字。转换器目前支持4 种探测点变种:
procfs("PATH").read
procfs("PATH").write
procfs.read
procfs.write
PATH 是被探测的文件,它是以/proc/systemtap/MODNAME 为起始目录的相对路径。如果没有指定PATH 参数(上述清单中的最后两个变种),PATH 的值默认为”command” 。
当用户程序读取/proc/systemtap/MODNAME/PATH 文件时,相应的procfs 读探针将被激活(triggered) 。从文件中已读取的数据串被分配到$value 变量,如下所示:
procfs("PATH").read { $value = "100/n" }
当用户程序写数据到/proc/systemtap/MONNAME/PATH 文件时,相应的procfs 写探针将被激法。即将要写到文件的数据被分配到$value 变量,如下所示:
procfs("PATH").write { printf("User wrote: %s", $value) }
Marker 探针家族关联被编译进内核或模块的静态marker 探针。这些marker 是内核里特殊的宏,与基于DWARF 的探针相比,它使用探测更快,更可靠。Marker 探针不需要利用DWARF 调试信息。
Marker 探测点名字以kernel 前缀开头,即标识用于查找marker 的模号表的源头,后缀是它自身即marker.(“MARK”) 。MARK 可以包含通配符,它匹配那些被编译进内核或模块的marker 宏的名字。可选地,你可以使用format( “FORMAT ”) 来指定marker 格式字符串,以区别两个有同样名字但格式字符串不相同的marker 。
Marker 探针处理函数可读取可选参数$arg1 到$argN ,这些参数由宏(STAP_MACRO) 的调用方指定,其中NN 为宏提供的参数个数。参数个数和参数串均使用类型安全的方式进行传递。
Marker 探针中的marker 格式字符可以通过$format 变量获取,同样地,marker 的名字字符串可通过$name 变量获取。
下述是marker 探针的结构:
kernel.mark("MARK")
kernel.mark("MARK").format("FORMAT")
关于marker 探针更详细的资料,请参阅http://sourceware.org/systemtap/wiki/UsingMarkers 。
Syscall.* 探针别名定义了数百个探针,语法形式如下:
syscall.NAME
syscall.NAME.return
通常,syscalls(2) 用户手册中列出的系统调用都定义了2 个探针:一个是进入点,另一个是退出点。有一例外就是那些不会返回的系统调用没有定义相应的.return 探针。
每个探针别名都定义了数个变量。可查阅tapset 的源代码来找到这些变量的定义。一般来说,系统调用的每个的参数,在脚本层面上都有一个变量与之对应。例如,syscall.open 提供了file name ,flags 和mode 对应的变量。此外,大数多syscall 探针别名都可以使用一套标准的变量,如下所示:
- argstr: 参数列表的字符串表示,可直接输出,除去左右两边的括号。
- name: 系统调用的名字。
- serstr: 对于.return 探针,为系统调用返回值的字符串表示,可直接输出。
但并不是所有的syscall.* 探针别名遵守这些约定的。如果你遇到bug ,请报告给我们。
Tracepoint 探测点家族探测被编译进内核或模块的静态探测tracepoint 。正如marker 探针那样,静态探测tracepoint 是开发者在内核代码里插入的宏调用,探测起来比基于DWARF 的探针更快,更可靠。Tracepoint 探针不需要DWARF 调试信息。Tracepoint 探针比marker 探针有更强的类型信息。
Tracepoint 探针以kernel 为前缀,随后是tracepoint 自身即trace(“name”) 。tracepoint 名字同时包含通配符,它匹配那些由内核开发者在tracepoint 头文中定义好的名字。
基于tracepoint 探针的处理函数可以读取由宏的调用方指定的可选参数。这些参数的名字由于tracepoint 的作者来声明。例如,tracepoint 探针kernel.trace(“sched_switch”) 提供的参数有$rq, $prev 和$next 。若参数是复杂类型,如结构体指针,可使用DWARF $ 目标变量一样的方式来访问它的成员。Tracepoint 探针不能修改参数,但在guru 模式就可以。
Tracepoint 探针的名字可通过$$name 来获取,也可通过$$vars 或$$params 来获得所有参数的name=value 对组成的字符串。
你可以使用由标准内核jiffies 定时器所定义的间隔(interval) 异步地激活探针处理函数。Jiffy 是内核定义的时间单元,通常是1ms 到60ms 之间。转换器支持两种jiffies 定时器探针变种:
timer.jiffies(N)
timer.jiffies(N).randomize(M)
探针处理函数每N jiffies 运行一次。如果指定randomize 部分,处理函数每次执行相隔的时间为N 加上[-M… +M] 范围的线性随机数。N 必须在一个合理的范围(从1 到大约1,000,000 ),M 必须小于N 。这两种探针的上下文中均不能使用目标变量。探针可以在多个处理器上并发运行。
间隔可以根据实际的时间单位来指定,与jiffies 定时器类似的定时器探测点有两类变种:
timer.ms(N)
timer.ms(N).randomize(M)
其中,N 和M 指定的是微秒数。systemTap 提供的全部时间单位分别是:秒(s 或sec) ,微秒(ms 或msec) ,毫秒(us 或usec) ,纳秒(ns 或nsec) 和赫兹(hz)
定时器的精度依赖于目标内核。2.6.17 之前的内核,定时器的精度为jiffies ,因此间隔上卷为最接近的jiffies 间隔。2.6.17 之后,内核使用hrtimers 来实现高精度时钟,尽管如此,最终的精度还是依赖了机器构架。无论是哪种定时器,如果指定randomize 部分,都是先将随机数加到间隔后再上卷。
systemTap 提供了Profiling 定时器探针,各个CPU 上的每次系统滴答,它都执行一次。此探针没有参数,如下所示:
timer.profile
可访问被中断进程的全部上下文信息,可利用它来实现基于时间的采样profiler 。
下述是关于定时器探针用法的例子:
# 引用定期中断,每1000 jiffies 激活一次:
timer.jiffies(1000)
# 每5 秒激活一次:
timer.sec(5)
# 引用定期中断,每一个1000 +/- 200 jiffies 激活一次:
timer.jiffies(1000).randomize(200)
.return 变种定位到指定函数的退出时机,可使用$return 上下文变量来获取函数的返回值。Return 探同样可以通过上下文来访问函数的入口参数,但它们的值可能在函数执行的过程中被改变了。内联函数没有返回点,因此.inline 探针没有.return 修饰符。
4.11 特殊的探测点
转换器定义了begin 和end 探测点,用于引用SystemTap 会话的启动和结束。此两探针均没有目标变量。
begin 探针关联到SystemTap 会话的启动事件。SystemTap 会话启动时,所有的begin 探针处理函数将被执行。所有的全局变量必须要此之前声明。
End 探针关联系SystemTap 会话的结束事件。当SystemTap 会话正常结束时,如脚本调用了exit() 函数,又或者接收到用户中断事件,所有的end 探针处理函数将被执行。如果是由于错误而导致会话结束,end 探针不会被执行。
error 探测点与end 探针点类似,不同之处只有当会话由于发生错误而结束时,error 探针才会被执行。若如此,end 探针将被忽略。在脚本结束时,可以使用error 探针做清理工作或最后的动作。
下述是一个简单的例子:
probe error { println ("Oops, errors occurred. Here's a report anyway.")
foreach (coin in mint) { println (coin) } }
4.11.4 多个begin ,end 和error 探针的执行顺序
可以使用可选的顺序数值来分别探制多个begin ,end 和error 探针运行次序。如果没有指定顺序数值,顺序数值默认为0 ,则按它们出现在脚本文件的顺序来运行。顺序数值可以是正数,亦可以是负数,这对于tapset 开发者来说十分有用,可以在begin 探针内做一些初始化工作。请看下述例子:
# In a tapset file:
probe begin(-1000) { ... }
# In a user script:
probe begin { ... }
用户脚本的begin 探针的顺序数值默认为0 ,故tapset 内的begin 探针比用户脚本的begin 探针先运行。
never 探针由转换器定义,顾名思义,它永远不会被执行。它后面的语句只是用来分析模号和类型的正确性。它的一个用法就是与可选探一起连用。详情请参阅4.1.4 节。
SystemTap 定义@cast() 操作符来支持类型转换功能。SystemTap 脚本使用long 数值来定义指针类型,然后使用与$target 变量一致的方法来访问类型的成员。但是当指针保存到脚本的整数变量时,转换器缺少基本的类型信息,以致不能从该指针(译者注:实际就是整数)访问它的成员。@cast() 操作符可告知转换器如何解释该指针。
在下述的句语中,将p 解释成名字为type_name 的结构体或联合体的指针,并解引用它的成员member 的值。
@cast(p, "type_name"[, "module"])->member
可选的module 参数告知转换器从何处可找到此类型的信息。可以用使分号(:) 分隔的列表来指定多个module 。如果没有指定module 参数,默认情况下,如果是dwarf 探针,它从被探测模块中查找;如果是function 探针或其它类型探针,它从内核中查找。
下述语句从内核的task_struct 结构查看它父进程的PID :
@cast(pointer, "task_struct", "kernel")->parent->tgid
正常情况下,如果没有调试信息,可以在module 参数中使用尖括号(<>) 包含头文件,转换器从头文件中获取类型信息。对于内核头文件,使用kerenl 作为module 的前缀,好让SystemTap 从内核代码树的目录读取该头文件。其它头文件,则使用gcc 默认的参数目录来读头文件。请看下述的语句例子:
@cast(tv, "timeval", "<sys/time.h>")->tv_sec
@cast(task, "task_struct", "kernel<linux/sched.h>")->tgid
Guru 模式下,转换器允许脚本对指针的成员进行赋值。
类型转换的另一用处是,可在运行时决定void* 成员的类型。请看下述例子:
probe foo {
if ($var->type == 1) {
value = @cast($var->data, "type1")->bar
} else {
value = @cast($var->data, "type2")->baz
}
print(value)
}