《System语言详解》——4. 探测点

来源:互联网 发布:中兴软件南昌公司如何 编辑:程序博客网 时间:2024/04/25 07:46

 

英文原文:http://sourceware.org/systemtap/langref/Probe_points.html

译者:林永听

注:本系列文章为作者连载作品,请勿转载,否则视为侵权。

 

4 探测点

4.1 探测点的一般语法形式

探测点采用点分格式的语法,事件命名空间划分成多个部分,类似于域名系统。每部分可以由字符串或数字等字面值来参数化,与函数调用的语法格式十分相似。

下面是符合语法规则的探测点:

kernel.function("foo")

kernel.function("foo").return

module{"ext3"}.function("ext3_*")

kernel.function("no_such_function") ?

syscall.*

end

timer.ms(5000)

在某种程度上,探测点可分成同步异步两大类。当CPU执行到探针指定的指令时,产生一个同步事件,探针可利用引用点(指令地址)来获取更多的上下文数据。另一探测点家族与异步事件相关,如定时器。与同步探测点不同的是,它没有固定的引用点。每个探测点可以指定匹配多个位置,例如使用通配符或多个探针别名,多个位置均被探测。在探针声明中,使用逗句作为分隔符来指定多个探测位置。

4.1.1 前缀

探测点的前缀阐明了探测目标,如kernelmoduletimer,等等。

4.1.2 后缀

后缀进一步细化了探测点,例如.return探测函数的退出点。缺少后缀时意味着探测函数的进入点。

4.1.3 文件名和函数名的通配符

探测点各部分若包含星号字符(*),则扩展为与之匹配的探测点。请看下述例子:

kernel.syscall.*

kernel.function("sys_*)

4.1.4 可选探测点

如果探测点后面跟随一个问号(?)字符,表明这是可选探测点,即使它扩展失败,也不会导致错误。从顶层开始到底层,各层的别名和通配符扩展同样遵循此法则。

可选探测点的语法形式如下:

kernel.function("no_such_function") ?

4.2 内置探测点分类(DWARF探针)

探测点家族使用目标内核或模块的符号调试信息(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代表源程序标签。MPATTERNLPATTERN两者均可包含星号(*),方括号“[]”和问号(?)等通配符。

PATTERN是一个字符串字面值,它标识程序中的代码点。它由3部分构成。

  1. 第一部分是函数名字,该名字与nm工具的输出一致。此部分可使用星号和问号通配符来匹配多个函数名字。
  2. 第二部分是可选的,它以@字符开头,紧跟着此函数所在源文件的路径。此路径可以包含通配符模式,如mm/slab*。大多数情况下,路径名应为从Linux代码树顶层目录开始的相对路径,尽管某些内核要求使用绝对路径。如果相对路径不能工作,尝试使用绝对路径。
  3. 如果给定文件名,第三部仍是可选的。它以“:”或“+”开头,用来标识源文件的行号。”:”后面跟的是绝对行号,而”+”后面跟的是函数入口的相对行号。”:*”匹配函数的每一行,而”: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]"

例子:

# 引用内核所有名字具有initexit字符串的函数。

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.cbio_init+3这一行语句:

kernel.statement("bio_init@fs/bio.c+3")

4.3 DWARF-less probing

当目标内核或模块缺少调试信息时,你仍然可以使用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, bufcount的值。此时,探针处理函数必须先调用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探针。注意,FUNCTIONMODULE名字中不能出现通配符,通配符引致探针不能注册。同时statement探针只能运行在guru模式下。

4.4 用户空间探测技术

要支持用户空间探测,只需将kernel配置成包括utrace扩展即可。本文撰写之时,Red HatCentOS发行版的内核已支持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")

PIDPATH描述的进程被创建时,.begin变体探针会被调用。如果不指定PIDPATH(如process.begin),任何新进程的繁衍都会调用该探针。

PIDPATH描述的线程被创建时,.thread.begin变体探针会被调用。

PIDPATH描述的进程结束时,.end变体探针会被调用。

PIDPATH描述的线程结束时,.thread.end变体探针会被调用。

PIDPATH描述的线程进行系统调用时,.syscall变体探针会被调用。系统调用编号可在$syscall上下文变量中获得。系统调用的前6个参数可从$argN目标变量中获取,即$arg1, $arg2等。

PIDPATH描述的线程从系统调用中返回时,.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 {}

如果进程探针没有指定PIDPATH参数,那么所有用户空间线程将被探测。然而,如果systemTap以目标进程模式(target process mode)运行(invoked),进程探针仅限于探测目标进程家族树里的那些进程。

目标进程模式(使用-c CMD-x PID选项运行stap)蕴含所有的process.*探针只能局限于探测给定的进程和它的子进程,但不影响kernel.*和其它的探针类型。通常而言,CMD字符串是运行程序的名称,而不是”/bin/sh –c”shell程序的名字,因为utraceuprobe探针会(从内核)接收到相当“干净”的事件流。如果CMD中出现元字符,如重定向操作符,要求使用”/bin/sh –c CMD”形式的名称,届时utraceuprobe探针将从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().insnprocess().insn.block探针依次检查进程每个执行的指令或区块。但此探针仅在部分平台上实现,因此如果你所使用的系统没有实现该探针,那么在启动脚本时会收到错误信息。

PIDPATH描述的进程每执行一个单步指令,.insn探针都会被调用。

PIDPATH描述的进程每执行一个区块指令,.insn.block探针都会被调用。

若想统计进程执行的指令总数,可以使用类似下述的命令:

$ stap -e 'global steps; probeprocess("/bin/ls").insn {steps++}

           probeend {printf("Total instructions: %d/n", steps);}' /

       -c /bin/ls

但使用此特性会使进程执行速度放慢很多。

4.4.1 静态用户空间探测技术

你可以探测程序的静态符号测量仪(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提供了兼容dtracemarker和与之对应的python脚本。你可直接使用这些基于dtrace的内置的宏,只需将dtrace –h-G功能打开即可。

下述是一个用户空间支持符号探测的原型例子:

# stap -e 'probeprocess("ls").function("*").call {

           log(probefunc()." ".$$parms)

           }' /

       -c 'ls -l'

此脚本需要命令程序带有调试信息并且内核支持utrace才能运行。如果看见“pass 4a-time”这样的构建失败信息,请确保你的内核支持utrace

4.5 PROCFS探针

此类探测点允许探测procfs伪文件系统中/proc/systemtap/MODNAME目录下文件的创建,读和写,其中NODNAMEsytemTap模块的名字。转换器目前支持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) }

4.6 Marker探针

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

4.7 Syscall探针

Syscall.*探针别名定义了数百个探针,语法形式如下:

syscall.NAME

syscall.NAME.return

通常,syscalls(2)用户手册中列出的系统调用都定义了2个探针:一个是进入点,另一个是退出点。有一例外就是那些不会返回的系统调用没有定义相应的.return探针。

每个探针别名都定义了数个变量。可查阅tapset的源代码来找到这些变量的定义。一般来说,系统调用的每个的参数,在脚本层面上都有一个变量与之对应。例如,syscall.open提供了file nameflagsmode对应的变量。此外,大数多syscall探针别名都可以使用一套标准的变量,如下所示:

  • argstr:参数列表的字符串表示,可直接输出,除去左右两边的括号。
  • name: 系统调用的名字。
  • serstr:对于.return探针,为系统调用返回值的字符串表示,可直接输出。

但并不是所有的syscall.*探针别名遵守这些约定的。如果你遇到bug,请报告给我们。

4.8 Tracepoints

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对组成的字符串。

4.9 定时器探针

你可以使用由标准内核jiffies定时器所定义的间隔(interval)异步地激活探针处理函数。Jiffy是内核定义的时间单元,通常是1ms60ms之间。转换器支持两种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)

其中,NM指定的是微秒数。systemTap提供的全部时间单位分别是:秒(ssec),微秒(msmsec),毫秒(ususec),纳秒(nsnsec)和赫兹(hz)

定时器的精度依赖于目标内核。2.6.17之前的内核,定时器的精度为jiffies,因此间隔上卷为最接近的jiffies间隔。2.6.17之后,内核使用hrtimers来实现高精度时钟,尽管如此,最终的精度还是依赖了机器构架。无论是哪种定时器,如果指定randomize部分,都是先将随机数加到间隔后再上卷。

systemTap提供了Profiling定时器探针,各个CPU上的每次系统滴答,它都执行一次。此探针没有参数,如下所示:

timer.profile

可访问被中断进程的全部上下文信息,可利用它来实现基于时间的采样profiler

下述是关于定时器探针用法的例子:

# 引用定期中断,每1000jiffies激活一次:

timer.jiffies(1000)

 

# 5秒激活一次:

timer.sec(5)

 

# 引用定期中断,每一个1000+/- 200 jiffies激活一次:

timer.jiffies(1000).randomize(200)

4.10 return探针

.return变种定位到指定函数的退出时机,可使用$return上下文变量来获取函数的返回值。Return探同样可以通过上下文来访问函数的入口参数,但它们的值可能在函数执行的过程中被改变了。内联函数没有返回点,因此.inline探针没有.return修饰符。

4.11 特殊的探测点

转换器定义了beginend探测点,用于引用SystemTap会话的启动和结束。此两探针均没有目标变量。

4.11.1 begin

begin探针关联到SystemTap会话的启动事件。SystemTap会话启动时,所有的begin探针处理函数将被执行。所有的全局变量必须要此之前声明。

4.11.2 end

End探针关联系SystemTap会话的结束事件。当SystemTap会话正常结束时,如脚本调用了exit()函数,又或者接收到用户中断事件,所有的end探针处理函数将被执行。如果是由于错误而导致会话结束,end探针不会被执行。

4.11.3 error

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 多个beginenderror探针的执行顺序

可以使用可选的顺序数值来分别探制多个beginenderror探针运行次序。如果没有指定顺序数值,顺序数值默认为0,则按它们出现在脚本文件的顺序来运行。顺序数值可以是正数,亦可以是负数,这对于tapset开发者来说十分有用,可以在begin探针内做一些初始化工作。请看下述例子:

# In a tapset file:

probe begin(-1000) { ... }

 

# In a user script:

probe begin { ... }

 

用户脚本的begin探针的顺序数值默认为0,故tapset内的begin探针比用户脚本的begin探针先运行。

4.11.5 never

never探针由转换器定义,顾名思义,它永远不会被执行。它后面的语句只是用来分析模号和类型的正确性。它的一个用法就是与可选探一起连用。详情请参阅4.1.4节。

4.12 指针类型转换

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)

}

 

 

原创粉丝点击