使用 SystemTap 調(diào)試內(nèi)核
SystemTap 是一種新穎的 Linux 內(nèi)核診斷工具,提供了一種從運行中的 Linux 內(nèi)核快速和安全地獲取信息的能力。SystemTap 是內(nèi)核開發(fā)人員和系統(tǒng)管理員的福音,因為這使得他們可以通過編寫或者重用簡單的腳本來收集內(nèi)核的實時數(shù)據(jù),而不需要再忍受修改源碼、編譯內(nèi)核、重啟系統(tǒng)的漫長煎熬。本文介紹了 SystemTap 的安裝、使用和基本原理,并用一些有趣的例子揭示了 SystemTap 提供的強大能力。
在 SystemTap 出現(xiàn)之前,對于 Linux 程序員或者系統(tǒng)管理員而言,調(diào)試內(nèi)核往往是一場噩夢。例如,你懷疑傳遞給系統(tǒng)調(diào)用 read 的參數(shù) fd 出了問題,想把它打印出來,你需要做的是:首先得到一份內(nèi)核源碼,找到 sys_read() 的函數(shù)體中插入 printk() 語句,接下來重新編譯內(nèi)核,然后用新的內(nèi)核重新啟動系統(tǒng)。謝天謝地,你總算看到了你想要看到的東西,不過你馬上會發(fā)現(xiàn)遇到了一個新的麻煩:除非重新啟動系統(tǒng)到原來的內(nèi)核,printk() 會無休止地打印下去。
SystemTap 的目的就是要把人們從這種泥潭中解救出來。SystemTap 提供了一個簡單的命令行接口和強大的腳本語言,同時預(yù)定義了豐富的腳本庫?;趦?nèi)核中的 kprobe,SystemTap允許你自由地從運行中的內(nèi)核無害地收集調(diào)試信息和性能數(shù)據(jù),來用于之后的分析和處理。你可以隨時開始或者停止這種收集過程,而無需漫長的修改代碼、編譯內(nèi)核和重啟系統(tǒng)的悲慘循環(huán)。SystemTap 使得上面的問題變得簡單了,簡單得只需要一條命令就可以做到:
stap -e ‘probe syscall.read { printf("fd = %dn",fd) }
SystemTap的功能和Sun的DTrace和IBM的dprobe工具相似。但是和它們不同的是, SystemTap是遵循GPL的開源軟件項目。它的出現(xiàn)使得Linux社區(qū)也擁有了功能強大而且易于使用的動態(tài)內(nèi)核調(diào)試工具。目前,SystemTap 的主要開發(fā)成員來自于RedHat、IBM、Intel和Hitachi,其中還包括來自IBM中國開發(fā)中心的工程師。
安裝SystemTap
在安裝SystemTap之前,需要確保系統(tǒng)中已經(jīng)安裝了其它兩個軟件包:
kernel-debuginfo RPM:SystemTap需要通過內(nèi)核調(diào)試信息來定位內(nèi)核函數(shù)和變量的位置。對于通常的發(fā)行版,并沒有安裝kernel-debuginfo RPM,我們可以到發(fā)行版的下載站點下載。對于我的ThinkPad上的Fedora Core 6,這個地址是: http://download.fedora.redhat.com/pub/fedora/linux/core/6/i386/debug/
elfutils RPM:SystemTap需要elfutils軟件包提供的庫函數(shù)來分析調(diào)試信息。目前的SystemTap要求安裝elfutils-0.123以上版本。目前最新的版本是0.124-0.1。如果需要,我們可以從SystemTap的站點下載RPM或者源碼來升級。下載地址是: ftp://sources.redhat.com/pub/SystemTap/elfutils/i386/
接下來就可以安裝SystemTap了,這有通過RPM或者源碼安裝兩種方式:
1. 通過RPM安裝 Fedora Core 6缺省情況下已經(jīng)安裝了systemtap。如果沒有,也可以從如下的地址下載: http://download.fedora.redhat.com/pub/fedora/linux/
core/updates/testing/6/i386/SystemTap-0.5.10-1.fc6.i386.rpm
2.通過源碼安裝:
從SystemTap的FTP站點下載最新的源碼
ftp://sources.redhat.com/pub/SystemTap/snapshots/SystemTap-20061104.tar.bz2
然后安裝如下:
/root > tar -jxf SystemTap-20061104.tar.bz2/root > cd src/root/src> ./configure/root/src> make/root/src> make install
運行SystemTap
運行SystemTap首先需要root權(quán)限。
運行SystemTap有三種形式:
1. 從文件(通常以.stp作為文件名后綴)中讀入并運行腳本:stap [選項] 文件名
2. 從標準輸入中讀入并運行腳本: stap [選項] -
3. 運行命令行中的腳本:stap [選項] -e 腳本
4.直接運行腳本文件(需要可執(zhí)行屬性并且第一行加上#!/usr/bin/stap):./腳本文件名使用"Ctrl+C"中止SystemTap的運行。
systemtap的選項還在不斷的擴展和更新中,其中最常用的選項包括:
-v -- 打印中間信息
-p NUM -- 運行完P(guān)ass Num后停止(缺省是運行到Pass 5)
-k -- 運行結(jié)束后保留臨時文件不刪除
-b -- 使用RelayFS文件系統(tǒng)來將數(shù)據(jù)從內(nèi)核空間傳輸?shù)接脩艨臻g
-M -- 僅當使用-b選項時有效,運行結(jié)束時不合并每個CPU的單獨數(shù)據(jù)文件
-o FILE -- 輸出到文件,而不是輸出到標準輸出
-c CMD -- 啟動探測后,運行CMD命令,直到命令結(jié)束后退出
-g -- 采用guru模式,允許腳本中嵌入C語句
其它更多選項請參看stap的手冊。
SystemTap的語法
我們利用一個簡單的systemtap腳本來介紹一下SystemTap的語法:
#!/usr/local/bin/stapglobal countfunction report(stat) {printf("stat=%dn", stat)}probe kernel.function("sys_read") {++count}probe end {report()}
探測點(probe):每個systemtap腳本中至少需要定義一個探測點,也就是指定了在內(nèi)核的什么位置進行探測。探測點名稱后面緊跟的一組大括號內(nèi)定義了每次內(nèi)核運行到該探測點時需要運行的操作,這些操作完成后再返回探測點,繼續(xù)下面的指令。這里給出了systemtap目前支持的所有探測點類型。
全局變量(global):用來定義全局變量。單個探測點函數(shù)體中使用的局部變量不需要預(yù)先定義,但是如果一個變量需要在多個探測點函數(shù)體中使用,則需要定義為全局變量。
函數(shù)(function):用來定義探測點函數(shù)體中需要用到的函數(shù)。除了可以用腳本語言定義函數(shù)以外,還可以用C語言來定義函數(shù),只是這時函數(shù)名后面的大括號對需要換成%{ %}。例如,前面的report()函數(shù)可以寫成:function report(stat) %{_stp_printf("stat=%dn", THIS->stat);%}
SystemTap的例子
了解了SystemTap的基本用法,下面讓我們來看幾個有趣的例子。
統(tǒng)計當前系統(tǒng)中調(diào)用最多的前10個系統(tǒng)調(diào)用
在進行性能分析的時候,我們常常需要知道那些函數(shù)調(diào)用次數(shù)最多,才能有的放矢地展開分析。下面這個簡單的例子可以打印出在過去的5秒鐘里調(diào)用次數(shù)最多的那些系統(tǒng)調(diào)用。[!--empirenews.page--]
#!/usr/bin/env stap## display the top 10 syscalls called in last 5 seconds#global syscallsfunction print_top () {cnt=0log ("SYSCALLttttCOUNT")foreach ([name] in syscalls-) {printf("%-20stt%5dn",name, syscalls[name])if (cnt++ == 10)break}printf("--------------------------------------n")delete syscalls}probe syscall.* {syscalls[probefunc()]++}probe timer.ms(5000) {print_top ()}
它的輸出結(jié)果一目了然:
看看是誰在偷偷動我的文件
有時候,我們?nèi)绻辛藧阂獾牟《拒浖?,會發(fā)現(xiàn)某些文件莫名其妙的被修改,下面這個例子可以幫你監(jiān)視誰在修改你的文件。
#!/usr/bin/env stap## monitor who is messing my file of secrets#probe generic.fop.open {if(filename == "secrets")printf("%s is opening my file: %sn", execname(), filename)}
我們運行這個腳本,在另外一個窗口做一些操作,來看看它的輸出結(jié)果:
打印ANSI字符串
SystemTap不僅僅是一個簡單的調(diào)試工具,強大的腳本語言能力讓它同樣能做一些有趣的事情,
下面這個例子就可以對輸出的字符進行美化:
#!/usr/bin/env stap## print colorful ANSI strings#probe begin {printf("a \ b |");for (c = 40; c < 48; c++)printf(" %d ", c);printf("12");for (l = 0; l < 71; l++)printf("-");printf("12");for (r = 30; r < 38; r++)for (t = 0; t < 2; t++) {printf("%d |", r);for (c = 40; c < 48; c++)printf("