2009年6月14日 星期日

內核診斷工具 SystemTap

內核診斷工具 SystemTap
在 SystemTap 出現之前,對于 Linux 程序員或者系統管理員而言,調試內核往往是一場噩夢。
例如,你懷疑傳遞給系統調用 read 的參數 fd 出了問題,想把它打印出來,你需要做的是︰
首先得到一份內核源碼,找到 sys_read() 的函數體中插入 printk() 語句,接下來重新編譯內核,然后用新的內核重新啟動系統。
謝天謝地,你總算看到了你想要看到的東西,不過你馬上會發現遇到了一個新的麻煩︰
除非重新啟動系 統到原來的內核,printk() 會無休止地打印下去。
SystemTap 的目的就是要把人們從這種泥潭中解救出來。
SystemTap 提供了一個簡單的命令行界面和強大的腳本語言,同時預定義了豐富的腳本庫。
基于內核中的 kprobe,SystemTap允許你自由地從營運中的內核無害地收集調試訊息和性能數據,來用于之后的分析和處理。
你可以隨時開始或者停止這種收集過程,而無需漫長的修改代碼、編譯內核和重啟系統的悲慘循環。
SystemTap 使得上面的問題變得簡單了,簡單得只需要一條命令就可以做到︰
stap -e 'probe syscall.read { printf("fd = %d\n",fd) }'
SystemTap的功能和Sun的DTrace和IBM的dprobe工具相似。
但是和它們不同的是, SystemTap 是遵循GPL的開源軟體項目。
它的出現使得Linux社群也擁有了功能強大而且易于使用的動態調試內核工具。
目前,SystemTap 的主要開發成員來自于RedHat、IBM、Intel 和 Hitachi。
安裝SystemTap
  在安裝SystemTap之前,需要確保系統中已經安裝了其它兩個套裝軟件︰
kernel-debuginfo RPM︰SystemTap需要透過內核調試訊息來定位內核函數和變量的位置。
對於通常的發行版,并沒有安裝 kernel-debuginfo RPM,我們可以到發行版的下載站下載。
對于 Fedora Core 6,這個位址是︰ http://download.fedora.redhat.com/pub/fedora/linux/core/6/i386/debug/
編譯 kernel
例如 linux-2.6.18.5
make menuconfig 需選 CONFIG_KPROBES, CONFIG_DEBUG_INFO, CONFIG_RELAY, CONFIG_DEBUG_FS
make; make modules_install; make install
cd /usr/lib/debug/lib/modules/
ln -s /lib/modules/2.6.18.5/

cd 2.6.18.5
ln -s /usr/src/kernels/linux-2.6.18.5/vmlinux
elfutils RPM︰SystemTap需要elfutils套裝軟件提供的庫函數來分析調試訊息。
目前的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缺省情況下已經安裝了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站點下載最新的源碼
  然后安裝如下︰
  /root > tar -jxf SystemTap-20061104.tar.bz2
  /root > cd src
  /root/src> ./configure
  /root/src> make
  /root/src> make install

  運行SystemTap
  營運SystemTap首先需要root權限。
  營運SystemTap有三種形式︰
  1. 從文件(通常以.stp作為文件名后綴)中讀入並營運腳本︰stap [選項] 文件名
  2. 從標準輸入中讀入並營運腳本︰ stap [選項] -
  3. 營運命令行中的腳本︰stap [選項] -e 腳本
  4. 直接營運腳本文件(需要可執行屬性並且第一行加上#!/usr/bin/stap)︰./腳本文件名使用"Ctrl+C"中止SystemTap的營運。
systemtap的選項還在不斷的擴展和更新中,其中最常用的選項包括︰
  -v -- 打印中間訊息
  -p NUM -- 營運完Pass Num后停止(缺省是營運到Pass 5)
  -k -- 營運結束后保留臨時文件不刪除
  -b -- 使用RelayFS文件系統來將數據從內核空間傳輸到用戶空間
  -M -- 僅當使用-b選項時有效,營運結束時不合併每個CPU的單獨數據文件
  -o FILE -- 輸出到文件,而不是輸出到標準輸出
  -c CMD -- 啟動探測后,營運CMD命令,直到命令結束后退出
  -g -- 採用guru模式,允許腳本中嵌入C語句
  其它更多選項請參看stap的手冊。
SystemTap的語法
我們利用一個簡單的systemtap腳本來介紹一下SystemTap的語法︰
#!/usr/local/bin/stap
global count
function report(stat) {
printf("stat=%d\n", stat)
}
probe kernel.function("sys_read") {
++count
}
probe end {
report()
}
探測點(probe)︰每 個systemtap腳本中至少需要定義一個探測點,也就是指定了在內核的什么位置進行探測。
探測點名稱后面緊跟的一組大括號內定義了每次內核營運到該探測點時需要營運的操作,這些操作完成后再返回探測點,繼續下面的指令。
這裡給出了systemtap目前支持的所有探測點類型。
全局變量(global)︰用來定義全局變量。
單個探測點函數體中使用的局部變量不需要預先定義,但是如果一個變量需要在多個探測點函數體中使用,則需要定義為全局變量。
函數(function)︰用來定義探測點函數體中需要用到的函數。
除了可以用腳本語言定義函數以外,還可以用C語言來定義函數,只是這時函數名后面的大括號對需要換成%{ %}。
例如,前面的report()函數可以寫成︰
  function report(stat) %{
  _stp_printf("stat=%d\n", THIS->stat);
 %}

SystemTap的例子
  了解了SystemTap的基本用法,下面讓我們來看幾個有趣的例子。
  統計當前系統中調用最多的前10個系統調用
  在進行性能分析的時候,我們常常需要知道那些函數調用次數最多,才能有的放矢地展開分析。
下面這個簡單的例子可以打印出在過去的5秒鐘裡調用次數最多的那些系統調用。
#!/usr/bin/env stap
#
# display the top 10 syscalls called in last 5 seconds
#
global syscalls
function print_top () {
cnt=0
log ("SYSCALL\t\t\t\tCOUNT")
foreach ([name] in syscalls-) {
printf("%-20s\t\t%5d\n",name, syscalls[name])
if (cnt++ == 10)
break
}
printf("--------------------------------------\n")
delete syscalls
}
probe syscall.* {
syscalls[probefunc()]++
}
probe timer.ms(5000) {
print_top ()
}
看誰在動我的文件
有時候,我們如果中了惡意的病毒軟體,會發現某些文件莫名其妙的被修改,下面這個例子可以幫你監視誰在修改你的文件。
 #!/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: %s\n", execname(), filename)
 }
SystemTap的基本原理
現下,大家已經熟悉了SystemTap的基本用法。
在結束之前,讓我們再來了解一下SystemTap的基本原理和工作流程以加深理解。
  可以看出,SystemTap營運的過程依次分為五個階段,通常稱為Pass 1 - Pass 5。
就像前面介紹用法的時候提到的,在命令行中加上-p NUM選項可以使得SystemTap在營運完Pass NUM之后停止,而不是營運到Pass 5。
這允許你分析SystemTap在每一個階段的輸出,對于調試腳本尤其有用。
下面來介紹每一個階段的主要功能︰
Pass 1 - parse︰這個階段主要是檢查輸入腳本是否存在語法錯誤,例如大括號是否匹配,變量定義是否規範等
Pass 2 - elaborate︰這個階段主要是對輸入腳本中定義的探測點或者用到的函數展開,不但需要綜合SystemTap的預定義腳本庫,還需要分析內核或者內核模塊的調試訊息
Pass 3 - translate: 在這個階段,將展開后的腳本轉換成C文件。前三個階段的功能類似于編譯器,將.stp文件編譯成為完整的.c文件,因此又被合起來稱為轉換器(translator)
Pass 4 - build︰在這個階段,將C源文件編譯成內核模塊,在這過程中還會用到SystemTap的營運時庫函數。
Pass 5 - run︰這個階段,將編譯好的內核模塊插入內核,開始進行數據收集和傳輸。

沒有留言:

張貼留言