3 分析
使用systemtap,可以分析数据,进行过滤,聚集,求和。不同的探针可以一起处理对共享数据。在处理函数中可以使用非常丰富的C-style 语法。
3.1 基本构成
if/else statemen
while(EXPR)STATEMENT
for(A;B;C)STATEMENT
systemtap中可以像C语言那样使用break/continue。探针处理函数中可以通过使用next(像在awk中一样)来提前获得返回。语句块也是使用{}括起来。在systemtap中,分号“ ; ”不是作为语句的结束,而是一条空语句,所以它不常见。
注释,使用shell格式的#,C/C++格式的//,/**/都可以。
表达式类似C或者是awk。并且支持一般常见的操作符,前缀,和字面量。string被作为一种原子类型,而不是char*。一个字符串用点号来结束。"hello" . " " . "world"
a string in three easy pieces
变量不需要声明,直接选个名字,用在表达式中就可以了。它们会自动初始化和声明。它的类型systemtap会通过操作符类型来推导。出现任何不一致的时候,systemtap会报错。类型转换需要显示的函数调用。
默认情况下,变量是在探针函数内部有效的。如果多个探针函数需要共享一个变量,就要声明全局变量,声明全局变量可以在systemtap脚本的任何地方。因为可能有并发的情况(e.g不同的探针函数在不同的),在使用全局变量的时候是自动上读写锁的。
声明全局变量:
global count_jiffies,count_ms
probe timer.jiffies(100){count_jiffies++}//jiffies()函数:This function returns the value of the kernel jiffies variable. This value is incremented periodically by timer interrupts, and may wrap around a 32-bit or 64-bit boundary. See HZ
.
3.2 目标变量。
一系列的目标变量allow access ti the probe point context。在一个符号性的调试器中,当你在断点处停下是,你可以输出程序上下文中的值。在systemtap的脚本中,对那些探测点(that match with specific executable piont),你也可以打印出需要的信息。
In addition, you can take their address (the &
operator), pretty-print structures (the $
and $$
suffix), pretty-print multiple variables in scope (the $$vars
and related variables), or cast pointers to their types (the @cast
operator), or test their existence / resolvability (the @defined
operator). Read about these in the manual pages.
@defined()用来测试括号内部的参数是否存在。
为了知道拿些变量是可用的,你应该对你探测的内核源码比较熟悉。并且要确保编译器没有做过优化,使得这些变量不能被探测到。
可以使用stap的参数-L来枚举可以使用的变量。
{Language reference:
以美元符号$开头的标识符被翻译成目标代码中的变量,而不是systemtap脚本中的变量。
Identifiers that begin with a dollar sign are interpreted as references to variables in the target software, rather than to SystemTap script variables.
}
3.3函数
systemtap允许你定义自己的函数,像全局变量一样,可以再systemtap脚本的任意位置定义全局函数。函数可以有参数列表,参数的类型和其他变量一样是systemtap自行推导的,但是也要求在上下文中保持一致。局部或者全局变量是可以用的,但是目标变量是不可以用的。That's because there is no specific debugging-level context associated with a function.
3.4 关联数组
有时候,探针需要共享一个没法用标量表示的数据。使用线程号,进程号,时间来索引的数据通常来说都是向量。systemtap提供了一个关联数组来完成这个目标。这些数组用一个哈希表实现,这个表的大小在启动时就确定了。因为它们太大了,不能在每个探针处理函数运行的时候动态创建,所以这些数组必须被声明为全局变量。
global b[400] 声明了一个数组,预留了可以存储400个元素的空间。
数组的基本操作是设置和查看元素。使用awk语法:数组名后面是[,然后是一个逗号隔开的索引表达式,最后以]结束。每个索引表达式可以是string或者是数值,但是设置后就不能再改了。
foo [4,"hello"] ++ | increment the named array slot |
processusage [uid(),execname()] ++ | update a statistic |
times [tid()] = get_cycles() | set a timestamp reference point |
delta = get_cycles() - times [tid()] | compute a timestamp delta |
if ([4,"hello"] in foo) { } | membership test |
delete times[tid()] | deletion of a single element |
delete times | deletion of all elements |
数组的迭代。使用关键字foreach。就像在awk中一样,创建一个循环,对一个数组中的关键元组(不是值)进行迭代。这个迭代可以用一个关键字来排序或者使用+,-来指定。
break/continue在循环中可以使用。因为循环很大,但是探针不会工作太久,所以应当尽早的退出循环。可以使用foreach中的‘limit’选项来实现。
为了简便,systemtap禁止在使用foreach的时候对数组进行任何修改。
foreach (x = [a,b] in foo) { fuss_with(x) } | simple loop in arbitrary sequence |
foreach ([a,b] in foo+ limit 5) { } | loop in increasing sequence of value, stop after 5 |
foreach ([a-,b] in foo) { } | loop in decreasing sequence of first key |
{Language reference:
数组元素可以是数值,字符串,或者一个聚合值。整个数组的使用期间不能改变。对数组元素的第一次决定了它的类型。、
数组的大小可以显示定义array[<size>].也可以使用默认值MAXMAPENTRIES global array
数组循环使用。定义数组加上%:global array1%[<size>],array2%
当插入元素个数超过数组容量的时候,以前插入的元素将会被覆盖。
}
3.5 Aggregates
除了string和数值型外,还有一种类型:统计聚合值,简称聚合值。这种类型被用在收集数值的统计量,(当快速积累一个新的数据类型比较重要的时候。)这种类型只对全局变量有意义。可以被分别存储也可以作为数组的元素来存储。
为了把一个值加入到聚合值中,systemtap提供了一个新的操作符<<<。类似c++中的输出操作符<<。左边的对象积累由右边对象提供的数据。
a <<< delta_timestamp
writes[execname()] <<< count
为了读取聚合值中的值,可以使用特殊的用于统计信息的函数。是不能使用常用的变量名来读取聚合值的。这种操作是具有排他性的,所以应当尽量少使用。
提供的有这些函数:@min
, @max
, @count
, @avg
, and @sum还有对单一的值进行赋值操作。此外可以用
@hist_log
或者 @hist_linear来提取数据流的直方图。用这两额函数处理的结果目前只允许进行打印。
@avg(a) | the average of all the values accumulated into a |
print(@hist_linear(a,0,100,10)) | print an ``ascii art'' linear histogram of the same data stream, bounds ![]() ![]() |
@count(writes["zsh"]) | the number of times ``zsh'' ran the probe handler |
print(@hist_log(writes["zsh"])) | print an ``ascii art'' logarithmic histogram of the same data stream |
3.6安全性
Q&A:
1.无限循环和递归?
一个探针处理函数在时间上是有界的。由systemtap产生的c代码中包含有显示的检测,从而把可执行的语句的总数限制到一个小的数量范围。对于函数调用的深度也有类似的限制。一旦这个限制被超过,这个探针函数就会中止或者发出一个错误信号。systemtap会话被配置在那个时间点中止。
2.内存溢出?
探针函数运行中无论如何都不会有动态内存被分配。数组,函数上下文,缓冲区都是在初始化的时候被分配的。这些资源在一个回话中可能会用完,这个时候就会产生一个错误的结果。
3.锁
如果多个探针想在一个共享的全局变量上上有冲突的锁,那么就会有函数超时或者被中止。这种事件被标记为“被跳过的”探针,在回话结束的时候总的个数会显示出来。
4.空指针
由systemtap产生的c代码
如果他们无效则会发送错误。如果结果超过了限制,一些算术和字符串的操作就会溢出。
5.翻译器和编译器的bugs?
翻译器和运行时的bugs肯定存在。另外,整个生成的c代码需要检查(可以试试-p3这个选项。)
systemtap编译器的bugs不会比内核多。也就是说,如果你相信可以编译一个内核,那么编译systemtap的模块是没有问题的。
6.结果都是正确的?
实际上,在systemtap和systemtap基于的kprobe系统在编写的时候就存在几个弱点。把探针不加区别的放到内核中所有感兴趣的地方是会引起系统崩溃的,在过去发生过。找到的我们已经修复了,并且建立了一个黑名单,但是它肯定是不完整的。
英文原文:
http://sourceware.org/systemtap/tutorial/Analysis.html
IBM-developerworks
http://www.ibm.com/developerworks/cn/linux/l-systemtap/#resources