1. 程式人生 > >Linux系統中使用SystemTap除錯核心

Linux系統中使用SystemTap除錯核心

SystemTap 是一種新穎的 Linux 核心診斷工具,提供了一種從執行中的 Linux 核心快速和安全地獲取資訊的能力。SystemTap 是核心研發人員和系統管理員的福音,因為這使得他們能夠通過編寫或重用簡單的指令碼來收集核心的實時資料,而無需再忍受修改原始碼、編譯核心、重啟系統的漫長煎熬。本文介紹了 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,其中還包括來自IBM中國研發中央的工程師。

安裝SystemTap

在安裝SystemTap之前,需要確保系統中已安裝了其他兩個軟體包:

kernel-debuginfo RPM:SystemTap需要通過核心除錯資訊來定位核心函式和變數的位置。對於通常的發行版,並沒有安裝kernel-debuginfo RPM,我們能夠到發行版的下載站點下載。對於我的ThinkPad上的Fedora Core 6,這個地址是: http://download.fedora.redhat.com/pub/fedora/linux/core/6/i386/debug/

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。

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]/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)

            }

我們執行這個指令碼,在另外一個視窗做一些操作,來看看他的輸出結果:

列印ANSI字串

SystemTap不但僅是個簡單的除錯工具,強大的指令碼語言能力讓他同樣能做一些有趣的事情,下面這個例子就能夠對輸出的字元進行美化:

#!/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("/033[%d;%d%s %s /033[0;0m",

            r, c, !t ? "m" : ";1m", !t ? "Normal" : "Bold  ");

            printf("/12");

            }

            exit();

            }

來看看他的輸出:

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:這個階段,將編譯好的核心模組插入核心,開始進行資料收集和傳輸。

小結

SystemTap是個全新的工具,但已表現出了強大的功能和廣泛的適用性。SystemTap使得動態收集Linux核心資訊和效能資料變得輕而易舉,這就使人能夠從繁瑣的資料採集中解放出來,而專注於資料的處理和分析,這無疑是核心研發人員和系統管理人員的福音。隨著越來越多使用者的體驗,越來越多的bug會被報告和修正,越來越多的新功能會被新增,SystemTap也會變得越來越穩定和完善。