SystemTap使用技巧【一】
阿新 • • 發佈:2019-01-09
SystemTap是一個強大的除錯工具,確切的說應該是一門除錯語言,因為它有自己的語法,也有解析、編譯、執行等過程(準確的說有五個階段),但它主要解決的問題是收集Linux核心或者使用者程序的資訊,主要目的是除錯。我一直以為gdb、kgdb是Linux最強大的偵錯程式,曾經愛不釋手,自從發現了SystemTap之後,又有了當初喜歡gdb的那種感覺了,真的是相見恨晚啊。gdb和SystemTap不是競爭關係,而是互補關係,gdb能做的事情SystemTap做不到,比如斷點/watch變數等等這些SystemTap都做不到,而SystemTap能做的事情gdb做不到或者非常麻煩才做到,比如很方便檢視核心除錯棧/嵌入C語言等等gdb就傻眼了。SystemTap真的非常強大,會了這個又覺得自己帥幾個百分點了。來看一下幾個技巧吧:
1、定位函式位置。
比較典型的是定位核心系統呼叫函式在哪個檔案上,以往我都是source insight或者grep找,比如要找open系統呼叫在核心的實現,就這樣:Linux比較新的核心定義系統呼叫都是SYSCALL_DEFINE方式,不像以前可以通過關鍵字sys_open來找,非常麻煩而且很慢,如果用SystemTap的話一個命令就搞定了:[email protected] ~/linux-source-3.13.0# grep -nr 'SYSCALL_DEFINE3(open' ./ ./fs/compat.c:1075:COMPAT_SYSCALL_DEFINE3(open, const char __user *, filename, int, flags, umode_t, mode) ./fs/compat.c:1461:COMPAT_SYSCALL_DEFINE3(open_by_handle_at, int, mountdirfd, ./fs/open.c:1011:SYSCALL_DEFINE3(open, const char __user *, filename, int, flags, umode_t, mode) ./fs/fhandle.c:254:SYSCALL_DEFINE3(open_by_handle_at, int, mountdirfd,
[email protected] ~/linux-source-3.13.0# stap -l 'kernel.function("sys_open")'
kernel.function("[email protected]/build/buildd/linux-lts-trusty-3.13.0/fs/open.c:1011")
我們很快知道open系統呼叫的實現程式碼就在fs/open.c的第1011行,搜尋比grep快,而且很準確。
同理,我們也可以用來定位使用者程序的函式位置:
比如nginx的檔案ngx_shmtx.c裡面為了相容各個作業系統而實現了兩個版本的ngx_shmtx_trylock鎖操作,用#if (NGX_HAVE_ATOMIC_OPS)、#else、#endif來做條件編譯,那怎麼知道我們編譯出來的是哪個版本呢,用SystemTap的話就很簡單了,否則要去grep一下這個巨集NGX_HAVE_ATOMIC_OPS有沒有定義才知道了,看看SystemTap的例子吧:[email protected] ~/develop# stap -l 'process("/opt/nginx/sbin/nginx").function("ngx_shmtx_trylock")'
process("/opt/nginx/sbin/nginx").function("[email protected]/core/ngx_shmtx.c:63")
ngx_shmtx.c第63行就是編譯出來的版本。
另外也可以定位出nginx中所有以ngx_epoll_開頭的函式在哪裡:
[email protected] ~/work/nginx# stap -l 'process("/opt/nginx/sbin/nginx").function("ngx_epoll_*")'
process("/opt/nginx-dso/sbin/nginx").function("[email protected]/event/modules/ngx_epoll_module.c:502")
process("/opt/nginx-dso/sbin/nginx").function("[email protected]/event/modules/ngx_epoll_module.c:386")
process("/opt/nginx-dso/sbin/nginx").function("[email protected]/event/modules/ngx_epoll_module.c:807")
process("/opt/nginx-dso/sbin/nginx").function("[email protected]/event/modules/ngx_epoll_module.c:526")
process("/opt/nginx-dso/sbin/nginx").function("[email protected]/event/modules/ngx_epoll_module.c:444")
process("/opt/nginx-dso/sbin/nginx").function("[email protected]/event/modules/ngx_epoll_module.c:348")
process("/opt/nginx-dso/sbin/nginx").function("[email protected]/event/modules/ngx_epoll_module.c:295")
process("/opt/nginx-dso/sbin/nginx").function("[email protected]/event/modules/ngx_epoll_module.c:824")
process("/opt/nginx-dso/sbin/nginx").function("[email protected]/event/modules/ngx_epoll_module.c:564")
2、檢視實際的函式。
如果對nginx或者核心程式碼有點了解的,可能知道很多地方資料結構裡面用到了函式指標,這是C語言裡面向物件思想的程式設計方式,非常有用,但對看程式碼的人來說就不太方便了。最近在csdn的linux bbs裡看到這個帖子:http://bbs.csdn.net/topics/390972532,為了滿足自己對底層實現的好奇心就深入地研究了一把。問題是這樣:我想知道核心裡一個程序的程序組組長pid存在task_struct的什麼地方,通過ps可以看出程序的pgid一般是自己的pid或者父程序pid:[email protected] ~# ps -ef -o pid,ppid,pgid,comm
Warning: bad ps syntax, perhaps a bogus '-'? See http://procps.sf.net/faq.html
PID PPID PGID COMMAND
28400 28399 28400 bash
5637 28400 5637 \_ cc_epoll
5638 5637 5637 \_ cc_epoll
5639 5637 5637 \_ cc_epoll
5640 5637 5637 \_ cc_epoll
5641 5637 5637 \_ cc_epoll
5642 5637 5637 \_ cc_epoll
這是自己寫的cc_epoll程序,從上面的結果可以看出,父程序fork出5個子程序,其中父程序是程序組組長,子程序的PGID是父程序,那父程序和子程序的PGID在什麼時候設定的呢,首先通過strace跟蹤一下:
[email protected] ~# strace ps -ef -o pid,ppid,pgid,comm
……
stat("/proc/5640", {st_mode=S_IFDIR|0555, st_size=0, ...}) = 0
open("/proc/5640/stat", O_RDONLY) = 6
read(6, "5640 (cc_epoll) S 5637 5637 2840"..., 1024) = 324
read(6, "", 700) = 0
close(6) = 0
……
原來是去讀取/proc/5640/stat這個檔案的內容啊。那我們就看一下它是怎麼read的,在google在搜了一圈都沒找到proc檔案系統的實現思路,那就只能從read系統呼叫入手了。我們可以自己cat一下:
[email protected] ~# cat /proc/5640/stat
5640 (cc_epoll) S 5637 5637 28400 34828 5637 4219200 63 0 0 0 0 0 0 0 20 0 1 0 746032770 8626176 30 18446744073709551615 4194304 4200196 140734779287888 140734779166376 140232232810899 0 0 0 0 18446744071581020176 0 0 17 0 0 0 0 0 0 6299160 6299888 32350208 140734779291850 140734779291861 140734779291861 140734779293677 0
[email protected] ~/systemtap# strace cat /proc/5640/stat
……
open("/proc/5640/stat", O_RDONLY) = 3
fstat(3, {st_mode=S_IFREG|0444, st_size=0, ...}) = 0
fadvise64(3, 0, 0, POSIX_FADV_SEQUENTIAL) = 0
read(3, "5640 (cc_epoll) S 5637 5637 2840"..., 32768) = 324
write(1, "5640 (cc_epoll) S 5637 5637 2840"..., 3245640 (cc_epoll) S 5637 5637 28400 34828 5637 4219200 63 0 0 0 0 0 0 0 20 0 1 0 746032770 8626176 30 18446744073709551615 4194304 4200196 140734779287888 140734779166376 140232232810899 0 0 0 0 18446744071581020176 0 0 17 0 0 0 0 0 0 6299160 6299888 32350208 140734779291850 140734779291861 140734779291861 140734779293677 0
) = 324
read(3, "", 32768) = 0
close(3) = 0
……
用SystemTap定位read系統呼叫後得到的程式碼如下:
從程式碼可以看出sys_read主要呼叫vfs_read,vfs_read主要呼叫file->f_op->read,而這個read是一個指標,要是能gdb step進去一下就知道是哪個函數了,除錯nginx時我就經常這麼幹,可惜這是核心啊,gdb除錯核心連環境都要搭半天,太麻煩了划不來。還是用SystemTap吧,SystemTap可以在vfs_read設定個probe,也就是設定一個探測點,SystemTap程式碼如下:
probe kernel.statement("[email protected]/build/buildd/linux-lts-trusty-3.13.0/fs/read_write.c:392")
{
if ($count == 32768) { //這裡設定條件是為了過濾只留下需要的資訊,32768是上面用strace跟蹤得到read的第三個引數
printf("open: %s, read: %s\n", symname($file->f_op->open), symname($file->f_op->read));
}
}
從SystemTap提供的symname函式介面可以得到指定地址的符號,也就是這小節的技巧:檢視實際的函式
執行stap後再執行cat /proc/5640/stat就可以得到如下結果:
[email protected] ~/systemtap# stap vfs_read.stp
open: 0x0, read: inotify_read
open: 0x0, read: inotify_read
open: proc_single_open, read: seq_read
open: proc_single_open, read: seq_read
接下來就是分析proc_single_open和seq_read這兩個函數了,通過這種方式分析下去,最後也得到程序的PGID是存在什麼地方了,具體存在什麼地方就略了,你也可以分析一下,從分析的過程中還能學到不少東西呢。
3、檢視一個函式裡面能在哪一行設定probe以及能獲取哪些變數。
Linux核心的copy_process函式比較長,但被編譯器優化了不少,在一些行上設定不了probe或者獲取不了區域性變數。設定行數不對或者獲取不到變數符號就容易得到正面這些錯誤:[email protected] ~/systemtap# stap copy_process.stap
semantic error: no line records for /build/buildd/linux-lts-trusty-3.13.0/kernel/fork.c:1235 [man error::dwarf] (try :1234 or :1236)
semantic error: while resolving probe point: identifier 'kernel' at copy_process.stap:1:7
source: probe kernel.statement("[email protected]/build/buildd/linux-lts-trusty-3.13.0/kernel/fork.c:1235")
^
semantic error: no match
Pass 2: analysis failed. [man error::pass2]
[email protected] ~/systemtap# stap copy_process.stap
semantic error: failed to retrieve location attribute for 'p' [man error::dwarf]: identifier '$p' at copy_process.stap:3:29
dieoffset: 0x9d4ac2 from unknown debug file for kernel
function: copy_process at /build/buildd/linux-lts-trusty-3.13.0/kernel/fork.c:1236
source: printf("$p->pid: %d\n", $p->pid);
^
Pass 2: analysis failed. [man error::pass2]
解決行數設定不對的辦法是一是按照錯誤提示設定,二是用stap的-L引數看能在哪些行上設probe:
[email protected] ~/systemtap# stap -L 'kernel.statement("[email protected]/build/buildd/linux-lts-trusty-3.13.0/kernel/fork.c:*")'
kernel.statement("[email protected]/build/buildd/linux-lts-trusty-3.13.0/kernel/fork.c:1134")
kernel.statement("[email protected]/build/buildd/linux-lts-trusty-3.13.0/kernel/fork.c:1140")
kernel.statement("[email protected]/build/buildd/linux-lts-trusty-3.13.0/kernel/fork.c:1144") $clone_flags:long unsigned int $stack_start:long unsigned int $stack_size:long unsigned int $child_tidptr:int* $pid:struct pid* $trace:int
kernel.statement("[email protected]/build/buildd/linux-lts-trusty-3.13.0/kernel/fork.c:1145") $clone_flags:long unsigned int $stack_start:long unsigned int $stack_size:long unsigned int $child_tidptr:int* $pid:struct pid* $trace:int
kernel.statement("[email protected]/build/buildd/linux-lts-trusty-3.13.0/kernel/fork.c:1147") $clone_flags:long unsigned int $stack_start:long unsigned int $stack_size:long unsigned int $child_tidptr:int* $pid:struct pid* $trace:int $p:struct task_struct*
kernel.statement("[email protected]/build/buildd/linux-lts-trusty-3.13.0/kernel/fork.c:1154") $clone_flags:long unsigned int $stack_start:long unsigned int $stack_size:long unsigned int $child_tidptr:int* $pid:struct pid* $trace:int $p:struct task_struct*
kernel.statement("[email protected]/build/buildd/linux-lts-trusty-3.13.0/kernel/fork.c:1162") $clone_flags:long unsigned int $stack_start:long unsigned int $stack_size:long unsigned int $child_tidptr:int* $pid:struct pid* $trace:int $p:struct task_struct*
kernel.statement("[email protected]/build/buildd/linux-lts-trusty-3.13.0/kernel/fork.c:1171") $clone_flags:long unsigned int $stack_start:long unsigned int $stack_size:long unsigned int $child_tidptr:int* $pid:struct pid* $trace:int $p:struct task_struct*
kernel.statement("[email protected]/build/buildd/linux-lts-trusty-3.13.0/kernel/fork.c:1172") $clone_flags:long unsigned int $stack_start:long unsigned int $stack_size:long unsigned int $child_tidptr:int* $pid:struct pid* $trace:int $p:struct task_struct*
copy_process函式雖長但能獲取的變數也就這麼多,試圖獲取其他變數也會出錯。我們要獲取copy_process複製出來的task_struct的話只能在第1147、1154、1162、1171、1172這幾行上獲取$p,而這些行上不一定能執行到,從程式碼上看在這些行上$p是一個野指標,要在第1192行呼叫dup_task_struct(current)後才給$p賦值。所以在copy_process函式裡設定statement探測點來獲取task_struct是獲取不到的。那怎麼辦呢,還好,copy_process函式會把複製出的task_struct返回給呼叫它的函式,那就可以在copy_process上設定function
return探測點:
probe kernel.function("copy_process").return
{
printf("pid: %d\n", $return->pid);
}
要是函式沒有返回你要找的變數,那就看它有沒有把這個函式傳給其他函數了,如果傳那就在其他函式裡面獲取,沒有的話就只能反彙編這個函式,再看這個函式把這個變數地址放在哪個暫存器上,再通過這個暫存器得到變數的地址來獲取這個變數的內容。
4、獲取函式引數。
我們來看一下sys_open的程式碼:再用stap -L來看一下sys_open能設定哪些probe和能獲取哪些變數:
[email protected] ~/systemtap# stap -L 'kernel.function("sys_open")'
kernel.function("[email protected]/build/buildd/linux-lts-trusty-3.13.0/fs/open.c:1011") $ret:long int
[email protected] ~/systemtap# stap -L 'kernel.statement("[email protected]/build/buildd/linux-lts-trusty-3.13.0/fs/open.c:*")'
kernel.statement("[email protected]/build/buildd/linux-lts-trusty-3.13.0/fs/open.c:1011") $ret:long int
kernel.statement("[email protected]/build/buildd/linux-lts-trusty-3.13.0/fs/open.c:1014") $filename:long int $flags:long int $ret:long int
kernel.statement("[email protected]/build/buildd/linux-lts-trusty-3.13.0/fs/open.c:1016") $filename:long int $ret:long int
我們要想獲取使用者傳進來的引數filename怎麼辦呢,一是在sys_open函式上設定function
call探測點,二是在第1016行設定statement探測點。第二種方法是能獲取filename,但獲取不到最後一個引數mode。對於函式引數還有一種辦法就是用SystemTap提供的*_arg函式介面,*是根據型別指定的,比較pointer_arg是獲取指標型別引數,int_arg是獲取整型引數,類似的還有long_arg、longlong_arg、uint_arg、ulong_arg、ulonglong_arg、s32_arg、s64_arg、u32_arg、u64_arg,例子程式碼如下:
[email protected] ~/systemtap# cat sys_open.stp
probe kernel.function("sys_open").call
{
printf("filename: %p, flags: %d, mode: %x\n", pointer_arg(1), int_arg(2), int_arg(3));
}
這樣我們就可以得到函式的指定引數了。
5、列印__user字串。
在核心裡巨集__user是給地址加上一個屬性,指定這個地址是使用者態地址,在核心裡不能直接用,需要轉換才能用。我們在SystemTap程式碼裡面不能直接用user_string或者kernel_string,否則執行時出現下面這個錯誤:ERROR: user string copy fault -14 at 00007f16323ffd90 [man error::fault] near identifier 'user_string_n' at /usr/local/share/systemtap/tapset/uconversions.stp:120:10
ERROR: kernel string copy fault at 0x00007fde54e79d90 [man error::fault] near identifier 'kernel_string' at /usr/local/share/systemtap/tapset/linux/conversions.stp:18:10
想要列印__user限定的字串,比如列印sys_open的第一個引數filename,要像下面這樣:
[email protected] ~/systemtap# cat sys_open.stp
probe kernel.function("sys_open").call
{
filename = user_string_quoted(pointer_arg(1));
printf("filename: %s, execname: %s\n", filename, execname());
}
6、列印函式呼叫堆疊。
一般列印函式呼叫堆疊,使用者程序的話直接gdb上去在檔案行數或者函式設定一個斷點,等待斷點停下來後,用gdb命令backtrace(縮寫bt)就能得到呼叫堆疊了,這樣很有用,不管是學習新程式碼或者debug時都非常有用,比如在學nginx的時候,裡面很多函式指標,用source insight、understand、vim+ctag都很難把程式碼流程讀通,還是讓程式碼執行起來後gdb上去設定斷點bt一下就知道了。使用者程序這種方法很管用,但在核心就不好辦了,核心也一堆函式指標,很不好分析函式的呼叫流程,還好SystemTap給我們提供了print_backtrace,比如我們要看核心是怎麼收包的,它的呼叫流程是怎樣的,我們在netif_receive_skb函式上設定個探測點再把呼叫函式堆疊打印出來就知道了:[email protected] ~/systemtap# cat netif_receive_skb.stp
probe kernel.function("netif_receive_skb")
{
printf("--------------------------------------------------------\n");
print_backtrace();
printf("--------------------------------------------------------\n");
}
[email protected] ~/systemtap# stap netif_receive_skb.stp
--------------------------------------------------------
0xffffffff8164dc00 : netif_receive_skb+0x0/0x90 [kernel]
0xffffffff8164e280 : napi_gro_receive+0xb0/0x130 [kernel]
0xffffffff81554537 : handle_incoming_queue+0xe7/0x100 [kernel]
0xffffffff815555d9 : xennet_poll+0x279/0x430 [kernel]
0xffffffff8164ee09 : net_rx_action+0x139/0x250 [kernel]
0xffffffff810702cd : __do_softirq+0xdd/0x300 [kernel]
0xffffffff8107088e : irq_exit+0x11e/0x140 [kernel]
0xffffffff8144e785 : xen_evtchn_do_upcall+0x35/0x50 [kernel]
0xffffffff8176c9ed : xen_hvm_callback_vector+0x6d/0x80 [kernel]
--------------------------------------------------------
7、定義指定型別的變數。
我在看SystemTap_Beginners_Guide文件的時候,看到nettop.stp這個例子,就試著執行一下:[email protected] ~/source/systemtap# stap ./testsuite/systemtap.examples/network/nettop.stp
semantic error: not accessible at this address (pc: 0xffffffff8164dc00) [man error::dwarf]: identifier '$skb' at /usr/local/share/systemtap/tapset/linux/networking.stp:64:27
dieoffset: 0x5cb9e3e from unknown debug file for kernel
function: netif_receive_skb at /build/buildd/linux-lts-trusty-3.13.0/net/core/dev.c:3694
<cannot suggest any alternative locations, elfutils too old>
source: dev_name = kernel_string($skb->dev->name)
^
Pass 2: analysis failed. [man error::pass2]
出錯了,說是$skb找不到,那就看看/usr/local/share/systemtap/tapset/linux/networking.stp這個檔案是怎麼寫的:
原來是想獲取netif_receive_skb函式的引數skb,這個就是上面第4個技巧說的獲取函式引數獲取不到的情況,那就改成下面這樣:
提前定義一個skb的變數,然後把後面用到$skb的地方改成skb,再執行試試:
[email protected] ~/source/systemtap# stap ./testsuite/systemtap.examples/network/nettop.stp
semantic error: unknown type in dereference: operator '->' at /usr/local/share/systemtap/tapset/linux/networking.stp:65:30
source: dev_name = kernel_string(skb->dev->name)
^
Pass 2: analysis failed. [man error::pass2]
還是出錯,這回是解引用一個不知道型別指標的錯誤,那用@cast轉換一下:
[email protected] ~/source/systemtap# stap ./testsuite/systemtap.examples/network/nettop.stp
semantic error: 'struct sk_buff' is being accessed instead of a member: operator '@cast' at /usr/local/share/systemtap/tapset/linux/networking.stp:64:11
source: skb = @cast(pointer_arg(1), "struct sk_buff")
^
Pass 2: analysis failed. [man error::pass2]
還是出錯,這回是@cast的錯誤了,難道@cast不能這麼用?原來@cast應該這麼用:
[email protected] ~/source/systemtap# stap ./testsuite/systemtap.examples/network/nettop.stp
PID UID DEV XMIT_PK RECV_PK XMIT_KB RECV_KB COMMAND
0 0 eth1 7 357 0 14 swapper/0
0 0 eth0 0 12 0 0 swapper/0
3 0 eth1 0 2 0 0 ksoftirqd/0
9838 0 eth1 0 2 0 0 redis-server
8 0 eth1 0 1 0 0 rcuos/0
這回是成功了,但每次都@cast一下,很費事啊,難道就不能提前宣告定義一下嗎,尋覓了(grep
-nr @cast ./)它的所有例子,看看@cast還有沒有其他用法,最終還是找到一個可行的辦法,再改一下:
這回也成功了,所以得到結論就是,@cast是可以提前定義指定型別變數的,原理就是先取變數第0個數組元素(其實就是自身)再對自身取地址就是指標變量了。
8、多級指標用法。
function deref:long(ptr:long)
%{
STAP_RETURN(*(void **)STAP_ARG_ptr);
%}
但最近發現一個更簡單的方法,從第7個技巧得到的靈感,看看下面二級指標用法的例子吧:
[email protected] ~/develop# cat cc_multi_pointer.c
#include <stdio.h>
struct test {
int count;
};
int main(int argc, char *argv[])
{
struct test t = {.count = 5566};
struct test *pt = &t;
struct test **ppt = &pt;
printf("t.count: %d, pt->count: %d, ppt->count: %d\n", t.count, pt->count, (*ppt)->count);
return 0;
}
[email protected] ~/develop# gcc -Wall -g -o cc_multi_pointer ./cc_multi_pointer.c
[email protected] ~/develop# cat cc_multi_pointer.stp
probe process("./cc_multi_pointer").statement("[email protected]/cc_multi_pointer.c:13")
{
printf("$t->count: %d, $pt->count: %d, $ppt->count: %d", $t->count, $pt->count, $ppt[0]->count);
}
[email protected] ~/develop# ./cc_multi_pointer
t.count: 5566, pt->count: 5566, ppt->count: 5566
[email protected] ~/develop# stap ./cc_multi_pointer.stp -c './cc_multi_pointer'
t.count: 5566, pt->count: 5566, ppt->count: 5566
$t->count: 5566, $pt->count: 5566, $ppt->count: 5566
[email protected] ~/develop# vim cc_multi_pointer.stp
[email protected] ~/develop#
現在三級指標你應該知道怎麼搞了吧。
9、嵌入C語言程式碼。
這個技巧很有用,可以獲取各種資訊,而且可以呼叫核心函式,比如我們想在程序fork出子程序時打印出程序id和程序名,也是先看看例子吧:[email protected] ~/systemtap# cat copy_process.stp
function getprocname:string(task:long)
%{
struct task_struct *task = (struct task_struct *)STAP_ARG_task;
snprintf(STAP_RETVALUE, MAXSTRINGLEN, "pid: %d, comm: %s", task->pid, task->comm);
%}
function getprocid:long(task:long)
%{
struct task_struct *task = (struct task_struct *)STAP_ARG_task;
STAP_RETURN(task->pid);
%}
probe kernel.function("copy_process").return
{
printf("copy_process return: %p, pid: %d, getprocname: %s, getprocid: %d\n", $return, $return->pid, getprocname($return), getprocid($return));
}
[email protected] ~/systemtap# stap -g copy_process.stp
copy_process return: 0xffff880039f61800, pid: 12212, getprocname: pid: 12212, comm: bash, getprocid: 12212
copy_process return: 0xffff880039f61800, pid: 12212, getprocname: pid: 12212, comm: bash, getprocid: 12212
copy_process return: 0xffff880039f63000, pid: 12213, getprocname: pid: 12213, comm: cc_epoll, getprocid: 12213
copy_process return: 0xffff880039f63000, pid: 12213, getprocname: pid: 12213, comm: cc_epoll, getprocid: 12213
copy_process return: 0xffff8800081a9800, pid: 12214, getprocname: pid: 12214, comm: cc_epoll, getprocid: 12214
copy_process return: 0xffff8800081a9800, pid: 12214, getprocname: pid: 12214, comm: cc_epoll, getprocid: 12214
copy_process return: 0xffff8800004d8000, pid: 12215, getprocname: pid: 12215, comm: cc_epoll, getprocid: 12215
copy_process return: 0xffff8800004d8000, pid: 12215, getprocname: pid: 12215, comm: cc_epoll, getprocid: 12215
copy_process return: 0xffff880000564800, pid: 12216, getprocname: pid: 12216, comm: cc_epoll, getprocid: 12216
copy_process return: 0xffff880000564800, pid: 12216, getprocname: pid: 12216, comm: cc_epoll, getprocid: 12216
copy_process return: 0xffff880000566000, pid: 12217, getprocname: pid: 12217, comm: cc_epoll, getprocid: 12217
copy_process return: 0xffff880000566000, pid: 12217, getprocname: pid: 12217, comm: cc_epoll, getprocid: 12217
有三個需要注意的地方:
1)、SystemTap腳本里面嵌入C語言程式碼要在每個大括號前加%字首,是%{…… %} 而不是%{ …… }%;
2)、獲取指令碼函式引數要用STAP_ARG_字首;
3)、一般long等返回值用STAP_RETURN,而string型別返回值要用snprintf、strncat等方式把字串複製到STAP_RETVALUE裡面。
SystemTap的技巧還有很多,以後有空再寫。
附:
[email protected] ~/systemtap# uname -a
Linux jusse 3.13.0-36-generic #63~precise1-Ubuntu SMP Thu Sep 4 22:28:20 UTC 2014 x86_64 x86_64 x86_64 GNU/Linux
[email protected] ~/systemtap# stap -V
Systemtap translator/driver (version 2.7/0.152, commit release-2.6-65-g89538c0b970d)
Copyright (C) 2005-2014 Red Hat, Inc. and others
This is free software; see the source for copying conditions.
enabled features: TR1_UNORDERED_MAP NLS
參考: