通过简单的例子,学习systemtap


看例子先! 



怎样遍历数组:


1.  #


2.  # Print the system call count by process name in descending order.


3.  #


4.


5.  global syscalls


6.


7.  probe begin {


8.     print ("Collecting data... Type Ctrl-C to exit and display results\n")


9.  }


10.


11. probe syscall.* {


12.    syscalls[execname()]++


13. }


14.


15. probe end {


16.    printf ("%-10s %-s\n", "#SysCalls", "Process Name")


17.    foreach (proc in syscalls-)


18.       printf("%-10d %-s\n", syscalls[proc], proc)


19. }


 


这中间,比较难理解的是第17行,那么它的解释如下:


The variable proc is an index variable that iterates over the range of values possible for the array index of syscalls. Also note the en dash (-) after syscalls that denotes that the iteration runs in reverse order. This character ensures that the number of system calls made print in descending order. To print in ascending order, change the script tosyscalls+.


--------------------------------------------------------------------


 


例子二:怎样探测内核变量或者局部变量


 


通过stap的L参数可以查看支持的内核变量.在stap的man手册中,有下面一句:


 


stap the -L option should be the used to get possible variables


 


例如:


 


$stap -L 'kernel.function("sys_read")'


kernel.function("sys_read@/build/buildd/linux-2.6.38/fs/read_write.c:402") $fd:unsigned int $buf:char* $count:size_t $fput_needed:int


 


从这个例子中,可以看到,对于内核中的变量,通过$加上变量的名字就可以访问了,但是并不是内核里面所有的变量都可以访问,使用前,请先用stap -L 检测一下!


 


不过在使用的过程中,可能会碰到一些问题,比如对于char * name的变量,用printf("%s\n",$name)输出的时候,会报错,错误的类型是"type mismatch".为什么呢?因为name本身是一个指针,需要转换一下才可以.转换函数可以查看man stapfuncs,不过,有些机器上安装的man手册不太完整,完整的可以看这个网站:http://linux.die.net/man/5/stapfuncs 


在下面列举一些:


 


kernel_string:string (addr:long)


Copy a 0-terminated string from kernel space at given address.


kernel_long:long (addr:long)


Copy a long from kernel space at given address.


kernel_int:long (addr:long)


Copy an int from kernel space at given address.


kernel_short:long (addr:long)


Copy a short from kernel space at given address.


kernel_char:long (addr:long)


Copy a char from kernel space at given address.


user_string:string (addr:long)


Copy a string from user space at given address. If the access would fault, return "<unknown>" and signal no errors.


user_string2:string (addr:long, err_msg:string)


Copy a string from user space at given address. If the access would fault, return instead the err_msg value.


user_string_warn:string (addr:long)


Copy a string from user space at given address. If the access would fault, signal a warning and return "<unknown>".


 


使用其中kernel_string() 就可以把char *name的值取出来:kernel_string($name).


 


除了可以检测到内核里面的变量之外,还可以检测到系统中具有全局意义的变量,比如进程的名字,进程的ID,以及UID等,这些变量被stap包装为一个函数,列表如下:


 


tid() The id of the current thread.


pid() The process (task group) id of the current thread.


uid() The id of the current user.


execname() The name of the current process.


cpu() The current cpu number.


gettimeofday_s() Number of seconds since epoch.可以有us ms等


get_cycles() Snapshot of hardware cycle counter.


pp() A string describing the probe point being currently handled.


probefunc() If known, the name of the function in which this probe was placed.


 


例如下面这个例子,这个例子在运行stap后有哪些进程调用do_fork,以及调用时候的标志是什么:


 


1.  #!/usr/bin/stap


2.


3.  global proc_counter


4.


5.  probe begin {


6.  print ("Started monitoring creation of new processes....Press ^C to terminate\n")


7.  printf ("%-25s %-10s %-s\n", "Process Name", "#Process ID", "#Clone Flags")


8.  }


9.


10. probe kernel.function("do_fork") {


11.    proc_counter++


12.    printf("%-25s %-10d 0x%-x\n", execname(), pid(), $clone_flags)


13. }


14.


15. probe end {


16.    printf ("\n%d processes forked during the observed period\n", proc_counter)


17.}


在上面这个例子中,调用了execname()来访问进程的名字,pid()来访问进程号,$clone_flags来说明其中使用的标志.


 


除此之外,对于syscall还有一些特殊的探测变量:argstr :参数列表,只对syscall有。


name: syscall的名字


retstr.对syscall.return才有


使用这些变量的时候,不用加前面的$符号.


 


另外,systemtap还内置了一些函数:比如我想只跟踪某个程序调用sys_read的情况.怎么判断程序的名字是否与execname()相等呢? 使用systemtap内置的isinstr函数就可以了.例如:


 


probe syscall.open{




  if(isinstr(execname(),"ls")


     printf("%s \n",execname())} 




 


如果是ls程序的话,那么就把该程序的名字打印出来


 


---------------------


 


好了,最后在贴一个调试程序的例子:


例子3,


  1 probe syscall.* {


  2     en = execname();


  3     ui = uid();


  4     eui = euid();


  5     if (en == "<redacted>")


  6     {


  7         printf("%s(%d): %s(%s)", en, pid(), name, argstr);


  8     


  9         if (ui != eui) {


 10            printf(" as %d/%d ", ui, eui);


 11         } else {


 12         printf(" as %d ", ui);


 13         }


 14      }


 15 }           


 16 


 17 probe syscall.*.return {


 18         en = execname();


 19         if (en == "<redacted>") {


 20             printf("= %s\n", retstr);


 21         }


 


This produces output with system call arguments and return values helpfully decoded for you; it looks like:


 


  <redacted>(14087): open("/etc/passwd", O_RDONLY) as 2315/0 = 3  [...]  <redacted>(14087): close(1) as 2315/0 = -9 (EBADF)


 


<全文完>


注:ubuntu下安装systemtap请参考以前的blog.其他发行版请直接看tutorial. 欢迎批评指正!


 

评论

此博客中的热门博文

Linux/ARM Page Table Entry 属性设置分析

提交了30次才AC ---【附】POJ 2488解题报告

笔记