當(dāng)前位置:首頁(yè) > 嵌入式 > 嵌入式軟件
[導(dǎo)讀] SystemTap 是一種新穎的 Linux 內(nèi)核診斷工具,提供了一種從運(yùn)行中的 Linux 內(nèi)核快速和安全地獲取信息的能力。SystemTap 是內(nèi)核開(kāi)發(fā)人員和系統(tǒng)管理員的福音,因?yàn)檫@使得他

 SystemTap 是一種新穎的 Linux 內(nèi)核診斷工具,提供了一種從運(yùn)行中的 Linux 內(nèi)核快速和安全地獲取信息的能力。SystemTap 是內(nèi)核開(kāi)發(fā)人員和系統(tǒng)管理員的福音,因?yàn)檫@使得他們可以通過(guò)編寫(xiě)或者重用簡(jiǎn)單的腳本來(lái)收集內(nèi)核的實(shí)時(shí)數(shù)據(jù),而不需要再忍受修改源碼、編譯內(nèi)核、重啟系統(tǒng)的漫長(zhǎng)煎熬。本文介紹了 SystemTap 的安裝、使用和基本原理,并用一些有趣的例子揭示了 SystemTap 提供的強(qiáng)大能力。

在 SystemTap 出現(xiàn)之前,對(duì)于 Linux 程序員或者系統(tǒng)管理員而言,調(diào)試內(nèi)核往往是一場(chǎng)噩夢(mèng)。例如,你懷疑傳遞給系統(tǒng)調(diào)用 read 的參數(shù) fd 出了問(wèn)題,想把它打印出來(lái),你需要做的是:首先得到一份內(nèi)核源碼,找到 sys_read() 的函數(shù)體中插入 printk() 語(yǔ)句,接下來(lái)重新編譯內(nèi)核,然后用新的內(nèi)核重新啟動(dòng)系統(tǒng)。謝天謝地,你總算看到了你想要看到的東西,不過(guò)你馬上會(huì)發(fā)現(xiàn)遇到了一個(gè)新的麻煩:除非重新啟動(dòng)系統(tǒng)到原來(lái)的內(nèi)核,printk() 會(huì)無(wú)休止地打印下去。

SystemTap 的目的就是要把人們從這種泥潭中解救出來(lái)。SystemTap 提供了一個(gè)簡(jiǎn)單的命令行接口和強(qiáng)大的腳本語(yǔ)言,同時(shí)預(yù)定義了豐富的腳本庫(kù)?;趦?nèi)核中的 kprobe,SystemTap允許你自由地從運(yùn)行中的內(nèi)核無(wú)害地收集調(diào)試信息和性能數(shù)據(jù),來(lái)用于之后的分析和處理。你可以隨時(shí)開(kāi)始或者停止這種收集過(guò)程,而無(wú)需漫長(zhǎng)的修改代碼、編譯內(nèi)核和重啟系統(tǒng)的悲慘循環(huán)。SystemTap 使得上面的問(wèn)題變得簡(jiǎn)單了,簡(jiǎn)單得只需要一條命令就可以做到:

stap -e ‘probe syscall.read { printf("fd = %dn",fd) }

SystemTap的功能和Sun的DTrace和IBM的dprobe工具相似。但是和它們不同的是, SystemTap是遵循GPL的開(kāi)源軟件項(xiàng)目。它的出現(xiàn)使得Linux社區(qū)也擁有了功能強(qiáng)大而且易于使用的動(dòng)態(tài)內(nèi)核調(diào)試工具。目前,SystemTap 的主要開(kāi)發(fā)成員來(lái)自于RedHat、IBM、Intel和Hitachi,其中還包括來(lái)自IBM中國(guó)開(kāi)發(fā)中心的工程師。

安裝SystemTap

在安裝SystemTap之前,需要確保系統(tǒng)中已經(jīng)安裝了其它兩個(gè)軟件包:

kernel-debuginfo RPM:SystemTap需要通過(guò)內(nèi)核調(diào)試信息來(lái)定位內(nèi)核函數(shù)和變量的位置。對(duì)于通常的發(fā)行版,并沒(méi)有安裝kernel-debuginfo RPM,我們可以到發(fā)行版的下載站點(diǎn)下載。對(duì)于我的ThinkPad上的Fedora Core 6,這個(gè)地址是: http://download.fedora.redhat.com/pub/fedora/linux/core/6/i386/debug/

elfutils RPM:SystemTap需要elfutils軟件包提供的庫(kù)函數(shù)來(lái)分析調(diào)試信息。目前的SystemTap要求安裝elfutils-0.123以上版本。目前最新的版本是0.124-0.1。如果需要,我們可以從SystemTap的站點(diǎn)下載RPM或者源碼來(lái)升級(jí)。下載地址是: ftp://sources.redhat.com/pub/SystemTap/elfutils/i386/

接下來(lái)就可以安裝SystemTap了,這有通過(guò)RPM或者源碼安裝兩種方式:

1. 通過(guò)RPM安裝 Fedora Core 6缺省情況下已經(jīng)安裝了systemtap。如果沒(méi)有,也可以從如下的地址下載: http://download.fedora.redhat.com/pub/fedora/linux/

core/updates/testing/6/i386/SystemTap-0.5.10-1.fc6.i386.rpm

2.通過(guò)源碼安裝:

從SystemTap的FTP站點(diǎn)下載最新的源碼

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

運(yùn)行SystemTap

運(yùn)行SystemTap首先需要root權(quán)限。

運(yùn)行SystemTap有三種形式:

1. 從文件(通常以.stp作為文件名后綴)中讀入并運(yùn)行腳本:stap [選項(xiàng)] 文件名

2. 從標(biāo)準(zhǔn)輸入中讀入并運(yùn)行腳本: stap [選項(xiàng)] -

3. 運(yùn)行命令行中的腳本:stap [選項(xiàng)] -e 腳本

4.直接運(yùn)行腳本文件(需要可執(zhí)行屬性并且第一行加上#!/usr/bin/stap):./腳本文件名使用"Ctrl+C"中止SystemTap的運(yùn)行。

systemtap的選項(xiàng)還在不斷的擴(kuò)展和更新中,其中最常用的選項(xiàng)包括:

-v -- 打印中間信息

-p NUM -- 運(yùn)行完P(guān)ass Num后停止(缺省是運(yùn)行到Pass 5)

-k -- 運(yùn)行結(jié)束后保留臨時(shí)文件不刪除

-b -- 使用RelayFS文件系統(tǒng)來(lái)將數(shù)據(jù)從內(nèi)核空間傳輸?shù)接脩?hù)空間

-M -- 僅當(dāng)使用-b選項(xiàng)時(shí)有效,運(yùn)行結(jié)束時(shí)不合并每個(gè)CPU的單獨(dú)數(shù)據(jù)文件

-o FILE -- 輸出到文件,而不是輸出到標(biāo)準(zhǔn)輸出

-c CMD -- 啟動(dòng)探測(cè)后,運(yùn)行CMD命令,直到命令結(jié)束后退出

-g -- 采用guru模式,允許腳本中嵌入C語(yǔ)句

其它更多選項(xiàng)請(qǐng)參看stap的手冊(cè)。

SystemTap的語(yǔ)法

我們利用一個(gè)簡(jiǎn)單的systemtap腳本來(lái)介紹一下SystemTap的語(yǔ)法:

#!/usr/local/bin/stap
global count
function report(stat) {
printf("stat=%dn", stat)
}
probe kernel.function("sys_read") {
++count
}
probe end {
report()
}
[!--empirenews.page--]

探測(cè)點(diǎn)(probe):每個(gè)systemtap腳本中至少需要定義一個(gè)探測(cè)點(diǎn),也就是指定了在內(nèi)核的什么位置進(jìn)行探測(cè)。探測(cè)點(diǎn)名稱(chēng)后面緊跟的一組大括號(hào)內(nèi)定義了每次內(nèi)核運(yùn)行到該探測(cè)點(diǎn)時(shí)需要運(yùn)行的操作,這些操作完成后再返回探測(cè)點(diǎn),繼續(xù)下面的指令。這里給出了systemtap目前支持的所有探測(cè)點(diǎn)類(lèi)型。

全局變量(global):用來(lái)定義全局變量。單個(gè)探測(cè)點(diǎn)函數(shù)體中使用的局部變量不需要預(yù)先定義,但是如果一個(gè)變量需要在多個(gè)探測(cè)點(diǎn)函數(shù)體中使用,則需要定義為全局變量。

函數(shù)(function):用來(lái)定義探測(cè)點(diǎn)函數(shù)體中需要用到的函數(shù)。除了可以用腳本語(yǔ)言定義函數(shù)以外,還可以用C語(yǔ)言來(lái)定義函數(shù),只是這時(shí)函數(shù)名后面的大括號(hào)對(duì)需要換成%{ %}。例如,前面的report()函數(shù)可以寫(xiě)成:


function report(stat) %{
_stp_printf("stat=%dn", THIS->stat);
%}

SystemTap的例子

了解了SystemTap的基本用法,下面讓我們來(lái)看幾個(gè)有趣的例子。

統(tǒng)計(jì)當(dāng)前系統(tǒng)中調(diào)用最多的前10個(gè)系統(tǒng)調(diào)用

在進(jìn)行性能分析的時(shí)候,我們常常需要知道那些函數(shù)調(diào)用次數(shù)最多,才能有的放矢地展開(kāi)分析。下面這個(gè)簡(jiǎn)單的例子可以打印出在過(guò)去的5秒鐘里調(diào)用次數(shù)最多的那些系統(tǒng)調(diào)用。

#!/usr/bin/env stap
#
# display the top 10 syscalls called in last 5 seconds
#
global syscalls
function print_top () {
cnt=0
log ("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é)果一目了然:

看看是誰(shuí)在偷偷動(dòng)我的文件

有時(shí)候,我們?nèi)绻辛藧阂獾牟《拒浖?,?huì)發(fā)現(xiàn)某些文件莫名其妙的被修改,下面這個(gè)例子可以幫你監(jiān)視誰(shuí)在修改你的文件。

#!/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)
}

我們運(yùn)行這個(gè)腳本,在另外一個(gè)窗口做一些操作,來(lái)看看它的輸出結(jié)果:

打印ANSI字符串

SystemTap不僅僅是一個(gè)簡(jiǎn)單的調(diào)試工具,強(qiáng)大的腳本語(yǔ)言能力讓它同樣能做一些有趣的事情,

下面這個(gè)例子就可以對(duì)輸出的字符進(jìn)行美化:

#!/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("