Systemtap Statement types

9 minute read

背景

本文要讲的是systemtap中的语句类型, 例如在循环中使用的break, continue, 在handler中使用的next, 函数中使用的return, 等.  
Statement types  
Statements enable procedural control flow within functions and probe handlers.   
The total number of statements executed in response to any single probe event is limited to MAXACTION, which defaults to 1000.  
前面讲systemtap安全性的时候提到过, 一个事件的handler中能执行多少条语句是有限制的, 做这样的限制主要因为大多数事件是同步事件, handler执行时间太长会对性能造成严重影响.  
handler中能执行多少条语句是MAXACTION决定的, 默认是1000. 使用stap -D参数控制.  

详见 :

http://blog.163.com/digoal@126/blog/static/163877040201381021752228/

6.1 break and continue  
Use break or continue to exit or iterate the innermost nesting loop statement, such as within a while, for, or foreach statement.   
The syntax and semantics are the same as those used in C.  
退出或者继续循环的用法类似C.  
举例 :   
break结束当前循环.  
[root@db-172-16-3-39 ~]# stap -e 'global a;   
probe begin   
{for(i=0; i<15; i++)   
  {if (i%2 == 1) break; a[i]="digoal".sprint(i)};   
foreach(x in a)   
  println(a[x]);   
exit()}'  
输出 :   
digoal0  
continue继续当前循环, 但不执行continue后面的语句.  
[root@db-172-16-3-39 ~]# stap -e 'global a;   
probe begin   
{for(i=0; i<15; i++)   
  {if (i%2 == 1) continue; a[i]="digoal".sprint(i)};   
foreach(x in a)   
  println(a[x]);   
exit()}'  
输出 :   
digoal0  
digoal2  
digoal4  
digoal6  
digoal8  
digoal10  
digoal12  
digoal14  
  
6.2 try/catch  
Use try/catch to handle most kinds of run-time errors within the script instead of aborting the probe handler in progress.   
The semantics are similar to C++ in that try/catch blocks may be nested.   
The error string may be captured by optionally naming a variable which is to receive it.  
一般情况下, 如果handler执行到出错的语句, 将退出整个stap.   
stap中允许的错误次数和一个安全参数(MAXERRORS)有关. 默认为0, which means that the first error will exit the script.  

详见 :

http://blog.163.com/digoal@126/blog/static/163877040201381021752228/

使用try和catch可以捕获错误, 在catch中处理异常, 然后继续执行.  
try {   
   /* do something */  
   /* trigger error like kread(0), or divide by zero, or error("foo") */  
} catch (msg) { /* omit (msg) entirely if not interested */  
   /* println("caught error ", msg) */  
   /* handle error */  
}  
/* execution continues */  
例如 :   
[root@db-172-16-3-39 ~]# stap -e 'probe begin {  
try {a="hello"; error("emulate error.")}  
catch(a) {println("Error msg: ", a)}  
println("continue")  
exit()  
}'  
Error msg: emulate error.  
continue  
catch的错误消息并不是前面的变量a, 而是错误消息的标准输出. 如果是其他错误, 则是其他错误的信息.  
[root@db-172-16-3-39 ~]# stap -e 'probe begin {  
try {a=1; a=a/0}  
catch(msg) {println("Error msg: ", msg)}  
println("continue")  
exit()  
}'  
Error msg: division by 0  
continue  
另外要注意的是, 尽量不要使用重名变量, 例如上面这个例子的msg, 如果替换成a会报错.  
[root@db-172-16-3-39 ~]# stap -e 'probe begin {  
try {a=1; a=a/0}  
catch(a) {println("Error msg: ", a)}      
println("continue")  
exit()  
}'  
semantic error: type mismatch (string vs. long): identifier 'a' at <input>:3:7  
        source: catch(a) {println("Error msg: ", a)}  
                      ^  
  
semantic error: type was first inferred here (long): identifier 'a' at :2:6  
        source: try {a=1; a=a/0}  
                     ^  
  
Pass 2: analysis failed.  Try again with another '--vp 01' option.  
  
6.3 delete  
delete removes an ARRAY element.  
The value will no longer be available, and subsequent iterations will not report the element.   
It is not an error to delete an element that does not exist.   
delete可以用于删除数组的元素或者整个数组. 删除一个不存在的元素不会报错, 但是删除一个不存在的数组会报错.  
[root@db-172-16-3-39 ~]# stap -e 'probe begin {delete a; exit()}'  
semantic error: unresolved array in delete statement: identifier 'a' at <input>:1:21  
        source: probe begin {delete a; exit()}  
                                    ^  
  
Pass 2: analysis failed.  Try again with another '--vp 01' option.  
删除一个未初始化的数组报错  
[root@db-172-16-3-39 ~]# stap -e 'global a; probe begin {delete a; exit()}'  
semantic error: unresolved type : identifier 'a' at <input>:1:31  
        source: global a; probe begin {delete a; exit()}  
                                              ^  
  
semantic error: unresolved type : identifier 'a' at :1:8  
        source: global a; probe begin {delete a; exit()}  
                       ^  
  
Pass 2: analysis failed.  Try again with another '--vp 01' option.  
删除一个已初始化的数组不会报错, 以下两个命令都不会报错.  
[root@db-172-16-3-39 ~]# stap -e 'global a; probe begin {a[1]="digoal"; delete a; exit()}'  
[root@db-172-16-3-39 ~]# stap -e 'global a; probe begin {a[1]="digoal"; delete a[1]; delete a; exit()}'  
如果数组有多个索引, 那么必须填写完全的索引值.  
The following statement removes from ARRAY the element specified by the index tuple.   
delete ARRAY[INDEX1, INDEX2, ...]  
The following syntax removes all elements from ARRAY:  
delete ARRAY  
举例 :   
如下a[1,2,3]这个元素要删除的话必须使用delete a[1,2,3], 而不能使用a[1]来删除.  
[root@db-172-16-3-39 ~]# stap -e 'global a; probe begin {a[1,2,3]="digoal"; delete a[1]; delete a; exit()}'  
semantic error: inconsistent arity (3 vs 1): identifier 'a' at <input>:1:50  
        source: global a; probe begin {a[1,2,3]="digoal"; delete a[1]; delete a; exit()}  
                                                                 ^  
  
semantic error: arity 3 first inferred here: identifier 'a' at :1:24  
        source: global a; probe begin {a[1,2,3]="digoal"; delete a[1]; delete a; exit()}  
                                       ^  
  
Pass 2: analysis failed.  Try again with another '--vp 01' option.  
一下可以正常执行 :   
[root@db-172-16-3-39 ~]# stap -e 'global a; probe begin {a[1,2,3]="digoal"; delete a[1,2,3]; delete a; exit()}'  
数组的索引可以想象成K-V中的key (同时还有主键的意思), 数组元素的值就是k-v中的value.  
删除数组的元素, 并不只是把这个元素初始化, 同时也会抹去这个索引. 例如一下a[1,2,3]也被抹去了 :   
[root@db-172-16-3-39 ~]# stap -e 'global a; probe begin {a[1,2,3]=100; a[1,2,2]=101; delete a[1,2,3]; foreach([x,y,z] in a) println(a[x,y,z]); exit()}'  
101  
如果只是清除它的值(int归0), 应该输出0 和 101;  
在下一篇将数组结构的时候, 会了解到在stap中数组不是动态分配空间的, 而是提前分配空间的(类似 hash 表), 所以这里说的清除并不是清除占用的内存空间, 仅仅是初始化了 . 即使delete a, 也不会回收a占用的空间.  
  
delete还可以用于标量变量, 统计变量存储的值.  
标量的值清除后, 整型为0, 字符串为null (""), 统计类型则回到初始空的状态.  
The following statement removes the value of SCALAR.   
Integers and strings are cleared to zero and null ("") respectively, while statistics are reset to their initial empty state.  
delete SCALAR  
举例 :   
[root@db-172-16-3-39 ~]# stap -e 'probe begin {a=10; b="digoal"; delete a; delete b; println(a); println(b); exit()}'  
0  
统计类型, 清除后为空, 所以delete s1后输出@max等会报错.  
[root@db-172-16-3-39 ~]# stap --vp 00001 -e 'global s1; probe begin {for(i=0; i<100; i++) s1 <<< i; printf("%d,%d,%d,%d,%d\n", @count(s1), @max(s1), @min(s1), @avg(s1), @sum(s1)); delete s1; printf("%d,%d,%d,%d,%d\n", @count(s1), @max(s1), @min(s1), @avg(s1), @sum(s1));exit()}'  
Pass 5: starting run.  
ERROR: empty aggregate near identifier '@max' at <input>:1:186  
100,99,0,49,4950  
WARNING: Number of errors: 1, skipped probes: 0  
WARNING: /usr/bin/staprun exited with status: 1  
Pass 5: run completed in 0usr/20sys/304real ms.  
Pass 5: run failed.  Try again with another '--vp 00001' option.  
  
6.4 EXP (expression)  
An expression executes a string- or integer-valued expression and discards the value.  
表达式, 如  
1+1  
a+b  
  
6.5 for 循环  
General syntax:  
for (EXP1; EXP2; EXP3) STMT  
The for statement is similar to the for statement in C.   
The for expression executes EXP1 as initialization.   
While EXP2 is non-zero, it executes STMT, then the iteration expression EXP3.  
  
6.6 foreach   
General syntax:  
foreach (VAR in ARRAY) STMT  
The foreach statement loops over each element of a named global array, assigning the current key to VAR.   
The array must not be modified within the statement.   
每次循环将指派数组的索引给VAR变量. 例如 :   
[root@db-172-16-3-39 ~]# stap -e 'global arr1; probe begin { arr1["i1"]=1; arr1["i2"]=2; foreach(x in arr1) printf("index: %s, value:%d \n", x, arr1[x]); exit()}'  
index: i1, value:1   
index: i2, value:2  
# 为了减少handler执行时间, 在foreach循环中, 数组元素不允许被修改.  
例如:   
[root@db-172-16-3-39 ~]# stap -e 'global a,i; probe kernel.function("icmp_echo") {a[i++]=1;} probe end {foreach (x in a) {a[x]=2; println(a[x])}}'  
semantic error: variable 'a' modified during 'foreach' iteration: identifier 'a' at <input>:1:89  
        source: global a,i; probe kernel.function("icmp_echo") {a[i++]=1;} probe end {foreach (x in a) {a[x]=2; println(a[x])}}  
                                                                                                        ^  
  
Pass 2: analysis failed.  Try again with another '--vp 01' option.  
  
If you add a single plus (+) or minus (-) operator after the VAR or the ARRAY identifier,   
the iteration order will be sorted by the ascending or descending index or value.  
Use a sorting suffix on at most one VAR or ARRAY identifier.  
  
The following statement behaves the same as the first example, except it is used when an array is indexed with a tuple of keys.   
foreach ([VAR1, VAR2, ...] in ARRAY) STMT  
在VAR或者ARRAY后加上符号加或减可以对索引值或者元素值进行排序.   
注意不能同时对索引和元素进行排序. 如: foreach (x+ in arr1+) 是错误的. foreach([x-,y+] in arr1) 也是错误的.  
例子 :   
[root@db-172-16-3-39 ~]# stap -e 'global arr1; probe begin { arr1["i1", "x2"]=1; arr1["i2", "x1"]=2; foreach([x-,y] in arr1) printf("index: %s,%s, value:%d \n", x, y, arr1[x,y]); exit()}'  
index: i2,x1, value:2   
index: i1,x2, value:1   
[root@db-172-16-3-39 ~]# stap -e 'global arr1; probe begin { arr1["i1", "x2"]=1; arr1["i2", "x1"]=2; foreach([x,y-] in arr1) printf("index: %s,%s, value:%d \n", x, y, arr1[x,y]); exit()}'  
index: i1,x2, value:1   
index: i2,x1, value:2   
[root@db-172-16-3-39 ~]# stap -e 'global arr1; probe begin { arr1["i1", "x2"]=1; arr1["i2", "x1"]=2; foreach([x,y] in arr1-) printf("index: %s,%s, value:%d \n", x, y, arr1[x,y]); exit()}'  
index: i2,x1, value:2   
index: i1,x2, value:1   
错误例子 :   
[root@db-172-16-3-39 ~]# stap -e 'global arr1; probe begin { arr1["i1", "x2"]=1; arr1["i2", "x1"]=2; foreach([x,y-] in arr1-) printf("index: %s,%s, value:%d \n", x, y, arr1[x,y]); exit()}'  
parse error: multiple sort directives  
        saw: operator '-' at <input>:1:90  
     source: global arr1; probe begin { arr1["i1", "x2"]=1; arr1["i2", "x1"]=2; foreach([x,y-] in arr1-) printf("index: %s,%s, value:%d \n", x, y, arr1[x,y]); exit()}  
                                                                                                      ^  
1 parse error.  
Pass 1: parse failed.  Try again with another '--vp 1' option.  
  
[root@db-172-16-3-39 ~]# stap -e 'global arr1; probe begin { arr1["i1", "x2"]=1; arr1["i2", "x1"]=2; foreach([x-,y+] in arr1) printf("index: %s,%s, value:%d \n", x, y, arr1[x,y]); exit()}'  
parse error: multiple sort directives  
        saw: operator '+' at <input>:1:81  
     source: global arr1; probe begin { arr1["i1", "x2"]=1; arr1["i2", "x1"]=2; foreach([x-,y+] in arr1) printf("index: %s,%s, value:%d \n", x, y, arr1[x,y]); exit()}  
                                                                                             ^  
1 parse error.  
Pass 1: parse failed.  Try again with another '--vp 1' option.  
合并赋值的用法, 可以同时取出索引和元素值.  
You can combine the first and second syntax to capture both the full tuple and the keys at the same time as follows.  
foreach (VAR = [VAR1, VAR2, ...] in ARRAY) STMT  
例子 :   
[root@db-172-16-3-39 ~]# stap -e 'global arr1; probe begin { arr1["i1", "x2"]=1; arr1["i2", "x1"]=2; foreach(var=[x,y] in arr1-) printf("index: %s,%s, value:%d \n", x, y, var); exit()}'  
index: i2,x1, value:2   
index: i1,x2, value:1   
  
The following statement is the same as the first example, except that the limit keyword limits the number of loop iterations to EXP times.   
EXP is evaluated once at the beginning of the loop.  
foreach (VAR in ARRAY limit EXP) STMT  
使用limit, 限制元素个数的输出, EXP只执行一次, 在循环开始前执行.  
[root@db-172-16-3-39 ~]# stap -e 'global arr1; probe begin {v1=0; arr1["i1", "x2"]=1; arr1["i2", "x1"]=2; foreach(var=[x,y] in arr1- limit v1+1) printf("index: %s,%s, value:%d \n", x, y, var); exit()}'  
index: i2,x1, value:2   
例如输出元素的最大值.  
[root@db-172-16-3-39 ~]# stap -e 'global arr1; probe begin { arr1["i1", "x2"]=1; arr1["i2", "x1"]=2; foreach(var=[x,y] in arr1- limit 1) printf("index: %s,%s, value:%d \n", x, y, var); exit()}'  
index: i2,x1, value:2   
  
6.7 if  
General syntax:  
if (EXP) STMT1 [ else STMT2 ]  
The if statement compares an integer-valued EXP to zero.   
It executes the first STMT if non-zero, or the second STMT if zero.  
The if command has the same syntax and semantics as used in C.  
EXP非零真, 0假.  
  
6.8 next  
The next statement returns immediately from the enclosing probe handler.  
next用于退出当前正在处理的handler, 例如 :   
[root@db-172-16-3-39 ~]# stap -e 'probe begin{print("hello\n"); next; print(" world\n")}'  
hello  
因为begin只会触发一次, 所以next后就无任何反应了.  
对于可能多次触发的探针, 例子 :   
[root@db-172-16-3-39 ~]# stap -e 'probe kernel.function("icmp_echo") {printf("hello\n"); next; printf(" world\n")}'  
在另一个窗口开一个ping  
[root@db-172-16-3-39 soft_bak]# ping 127.0.0.1  
PING 127.0.0.1 (127.0.0.1) 56(84) bytes of data.  
64 bytes from 127.0.0.1: icmp_seq=1 ttl=64 time=0.037 ms  
64 bytes from 127.0.0.1: icmp_seq=2 ttl=64 time=0.023 ms  
64 bytes from 127.0.0.1: icmp_seq=3 ttl=64 time=0.028 ms  
64 bytes from 127.0.0.1: icmp_seq=4 ttl=64 time=0.021 ms  
stap输出 :   
hello  
hello  
hello  
hello  
  
6.9 ; (null statement)  
General syntax:  
statement1  
;  
statement2  
The semicolon represents the null statement, or do nothing.   
It is useful as an optional separator between statements to improve syntax error detection and to handle certain grammar ambiguities.  
分号表示空语句, 一般用于将两个语句隔开, 减少误解析的可能性.  
  
6.10 return  
General syntax:  
return EXP  
The return statement returns the EXP value from the enclosing function.   
If the value of the function is not returned, then a return statement is not needed, and the function will have a special unknown type with no return value.  
函数的返回值使用return EXP语法 .  
例如 :   
[root@db-172-16-3-39 ~]# stap -e 'function add(x,y) {return x+y} probe begin {printf("%d\n", add($1,$2)); exit()}' 10 9  
19  
如果函数中没有return, 那么将返回unknown类型例如.  
[root@db-172-16-3-39 ~]# stap -e 'function add(x,y) {} probe begin {printf("%d\n", add($1,$2)); exit()}' 10 9  
WARNING: side-effect-free function 'add': identifier 'add' at <input>:1:10  
 source: function add(x,y) {} probe begin {printf("%d\n", add($1,$2)); exit()}  
                  ^  
0  
  
6.11 { } (statement block)  
This is the statement block with zero or more statements enclosed within brackets.   
The following is the general syntax:  
{ STMT1 STMT2 ... }  
The statement block executes each statement in sequence in the block.   
Separators or terminators are generally not necessary between statements.   
The statement block uses the same syntax and semantics as in C.  
语句块使用语法 :   
{ STMT1 STMT2 ... }  
例如在条件判断中要使用多个语句的话, 需要用到语句块.  
[root@db-172-16-3-39 ~]# stap -e 'probe begin {if ($1>100) {printf("hello "); printf("%d\n",$1);}; exit();}' 101  
hello 101  
  
6.12 while  
General syntax:  
while (EXP) STMT  
The while statement uses the same syntax and semantics as in C.   
In the statement above, while the integer-valued EXP evaluates to non-zero, the parser will execute STMT.  
while循环 :   
[root@db-172-16-3-39 ~]# stap -e 'probe begin {while(i<10) {println(i); i++}; exit()}'  
0  
1  
2  
3  
4  
5  
6  
7  
8  
9  

参考

1. https://sourceware.org/systemtap/langref/Statement_types.html

2. https://sourceware.org/systemtap/langref/SystemTap_overview.html#sub:SystemTap-safety

3. http://blog.163.com/digoal@126/blog/static/163877040201381021752228/

Flag Counter

digoal’s 大量PostgreSQL文章入口