1. 程式人生 > >systemtap 除錯kernel2

systemtap 除錯kernel2

一、簡介

SystemTap是一個診斷Linux系統性能或功能問題的開源軟體。它使得對執行時的Linux系統進行診斷調式變得更容易、更簡單。有了它,開發者或除錯人員不再需要重編譯、安裝新核心、重啟動等煩人的步驟。

為 了診斷系統問題或效能,開發者或除錯人員只需要寫一些指令碼,然後通過SystemTap提供的命令列介面就可以對正在執行的核心進行診斷除錯,以前需要的 修改或插入除錯程式碼、重新編譯核心、安裝核心和重啟動等這些瑣碎的工作完全消除。目前該工具並不支援對使用者態應用的診斷除錯,但是它們在以後會被新增進 去。當前該專案的主要開發人員為來自Red Hat, IBM, Intel和Hitachi的工程師。其中Redhat主要負責指令碼轉換/翻譯器和執行時庫,IBM負責kprobe和relayfs,Intel負責轉 換器安全檢查以及performance monitor tapset。



Systemtap使用了類似於awk和C語言的指令碼語言(類似於Dtrace的D語言),它只使用了三種資料類 型,整數(integers)、字串(strings)以及關聯陣列(associative Arrays),它有完整的控制結構,包括塊(blocks)、條件(conditionals)、迴圈(loops)和函式(functions)。語 句分割符;是可選的,變數不需要宣告型別,它們是根據上下文自動推測和檢查的,它使用了kprobe提供的介面來實現探測,對於每一個探測,需要定義探測 點以及相應的處理函式,探測點就是指kprobe中被探測的函式或指令地址(也被稱為核心事件)的),但在Systemtap中,使用者可以指定原檔案,原 程式碼的某一行,或者一個非同步事件,如週期性的定時器,探測點使用了層次化的命名方式,探測點處理函式能夠立刻輸出資料,與printk很類似,它也能檢視 核心資料。指令碼然後被一個翻譯器轉換成C程式碼並編譯成一個核心模組。探測點根據核心的DWARF除錯資訊對映到核心的虛地址(因此Systemtap要求 使用者必須準備好可用的核心除錯資訊),所有的指令碼內容在轉換時進行嚴格的檢查,並且在執行時也要檢查(如無限迴圈、記憶體使用、遞迴和無效指標等),因此有 好的安全性,不會影響正在執行的系統(這對生產系統是非常重要的)。 Systemtap包含了一個黑名單,其中列出的函式不能被Systemtap探測,因為它們會導致無限探測迴圈、鎖重入等問題。

下圖直觀地給出了Systemtap的工作原理:

Systemtap指令碼檔案是.stp字尾的檔案,使用的指令碼語言是前面講到的Systemtap自己定義的指令碼語 言,一個Systemtap指令碼描述了將要探測的探測點以及定義了相關聯的處理函式,每一個探測點對應於一個核心函式或事件或函式內部的某一位置。被關聯 的處理函式將在核心執行到對應的探測點時被執行。

tapsets是一個指令碼庫,包含了許多tapset,每一個tapset一般為某一內 核子系統或特定的功能塊預定義了一套探測點、輔助函式或全域性變數供使用者指令碼或其它的tapset引用,它定義的一些資料能夠被每一個探測點處理函式或指令碼 使用,這些資料通常通過使用處理函式語句塊(HSB Handler Statement Block)來出口,HSB語句塊中的變數就是被出口的資料。tapset一般由該核心子系統的開發者或對子系統非常瞭解的開發者編寫,既使用了指令碼語 言,也使用了C語言,並且它已經被測試和驗證,可以安全使用。tapsets屬於Systemtap發行包的一部分。

Systemtap 實現了一個指令碼轉換器/翻譯器,當用戶執行一個Systemtap指令碼時,Systemtap將首先對它進行分析和一些安全檢查,如果它引用了 Systemtap預定義的指令碼庫提供的函式,Systemtap也將讀取指令碼庫得到相應的程式碼,對於一些核心變數或符號的引用,它必須根據核心除錯資訊 來解析到相應的地址。然後,它被轉換成C程式碼,在這個轉換中,Systemtap將根據需要增加必要的鎖和安全檢查程式碼。探測點之間共享的變數將被轉換成 恰當的靜態宣告並有鎖保護,每組本地變數被轉換到一個合成的呼叫幀結構中以避免消耗核心的棧空間。關聯到探測點的處理函式被封裝成一個介面函式,那呼叫恰 當的kprobe介面函式來註冊該探測點。

產生的C程式碼包含了一些對執行時tapset的引用,執行時tapset庫提供了許多 Systemtap介面函式,如通用的查詢表、受限記憶體管理、啟動、關閉、I/O操作以及其它一些函式。生成的C程式碼編譯連結之後生成一個可載入的核心模 塊。為了快速得到執行結果,Systemtap使用了relayfs,當載入生成的核心模組後,該模組的初始化函式初始化自身,然後呼叫kprobe介面 函式註冊指令碼中定義的探測點。當核心執行到註冊的探測點時,相應的處理函式被呼叫,使用者在處理函式中的輸出語句將呼叫relayfs介面函式輸出結果數 據,使用者在處理函式也可以呼叫一些核心的效能測量函式。當用戶主動停止或指令碼設定的條件滿足時,模組將呼叫退出函式解除安裝已經註冊的探測點並做一些清理處理 就解除安裝模組自身。

Systemtap在執行時啟動了一個程序,它專門負責通過relayfs讀去模組的輸出資料並即時地輸出給使用者。



專案方面:

使用的語言:

探測能力:

安全性:

圖形使用者介面:




執行Systemtap的前提條件是:

  • 核心支援並配置了kprobe(2.6.11和以上)
  • 核心模組編譯環境(即編譯核心模組所需的核心標頭檔案以及模組配置資訊,對於Fedora core或Redhat指kernel-devel或kernel-smp-devel RPM包)
  • 核心除錯資訊(對於Fedora core或Redhat指kernel-debuginfo RPM包)
  • C編譯環境(即libc庫標頭檔案和編譯工具鏈)
  • 有libdwfl的elfutils(只有支援libwdfl的elfutils,systemtap才能正常工作,如果您的系統的elfutils較舊,您必須下載elfutils原始碼包來編譯,systemtap能夠和elfutils一塊編譯)
  • root許可權(為了執行Systemtap,您必須具有root許可權)

如果您使用的是Fedora core 4或更新的Fedora core版本,安裝Systemtap非常容易:

# yum install kernel-devel
# yum --enablerepo=core-debuginfo --enablerepo=updates-debuginfo /
install kernel-debuginfo
# yum install systemtap


然後執行下面命令可以驗證是否成功。

# stap -ve 'probe begin { log("hello world") exit () }'
# stap -c df -e 'probe syscall.* { if (target()==pid()) log(name." ".argstr) }'




如果您想安裝最新的Systemtap,您可以自己用原始碼包來構建,其步驟是:

1.確保elfutils支援libdwlf

如果您的elfutils並沒有libdwlf,您需要下載它。

Systemtap能自動build它,因此對於這種情況,您只需下載最新的elfutils

  ftp://sources.redhat.com/pub/systemtap/elfutils/elfutils-NNNN.tar.gz
ftp://sources.redhat.com/pub/systemtap/elfutils/elfutils-portability.patch


,解壓elfutils-NNNN.tar.gz並打上補丁elfutils-portability.patch.具體命令如下:

# cd /home/yangyi
# wget ftp://sources.redhat.com/pub/systemtap/elfutils/elfutils-NNNN.tar.gz
# wget ftp://sources.redhat.com/pub/systemtap/elfutils/elfutils-portability.patch
# tar zxvf elfutils-NNNN.tar.gz
# cd elfutils-NNNN
# patch p0 ../ elfutils-portability.patch


2. 下載Systemtap原始碼包並解壓

# cd /home/yangyi
# wget ftp://sources.redhat.com/pub/systemtap/snapshots/systemtap-YYYYMMDD.tar.bz2
# tar -jxvf systemtap-YYYYMMDD.tar.bz2
# cd systemtap-YYYYMMNN/src


# cvs -d :pserver:[email protected]:/cvs/systemtap login


注:密碼是anoncvs

# cvs -d :pserver:[email protected]:/cvs/systemtap co src
# cd src


3.安裝

# ./configure [--with-elfutils=/home/yangyi/elfutils-NNNN] [other autoconf options]
# make all check
# make install


注意,選項—with-elfutils帶的引數是elfutils的原始碼包的路徑,如果您已經安裝了最新的elfutils,這個選項是不必要的.

對於其它Linux發行,沒有便捷的安裝方式可利用,您必須自己構建核心並設定Systemtap要求的前提條件。有時,您可能想使用最新的核心,您也可以使用這種方式來做。

要想編譯一個支援Systemtap的核心,您必須配置這些核心選項:

Kernel hacking  --->
[*] Kernel debugging
[*] Compile the kernel with debug info

Instrumentation Support --->
[*] Kprobes (EXPERIMENTAL)

General setup --->
[*] Kernel->user space relay support (formerly relayfs)


您可以用以下符號grep生成的配置檔案.config來確認這些配置是否成功:

CONFIG_DEBUG_INFO
CONFIG_KPROBES
CONFIG_RELAY


如果成功,它們應當都為Y.

在使用該構建好的核心啟動系統後,您必須確保Systemtap能夠找到該核心對應的核心映像檔案(即vmlinux), 該映像必須為非壓縮的沒有去掉除錯和符號資訊的核心映像(就是在核心構建根目錄下的vmlinux檔案),Systemtap將會在以下三個位置

/boot/vmlinux-`uname -r`
/usr/lib/debug/lib/modules/`uname -r`/vmlinux
/lib/modules/`uname -r`/vmlinux


尋找該核心映像,因此您必須確保它在這三個位置的其中一個上。當然這三個可以是符號連結。

您也需要建立以下兩個符號連結指向您的核心的原始碼樹。

/usr/src/kernels/`uname -r`
/lib/modules/`uname -r`/source


您還需要建立以下符號連結指向您的核心的build樹。

/lib/modules/`uname -r`/build


例如, 假定您的核心原始碼樹是/home/yangyi/linux-2.6.20,您的核心build樹是/home/yangyi/kernel-build

(注意,2.6核心的build目錄可以和原始碼目錄不同,具體做法是

cd /home/yangyi/linux-2.6.20
make O=/home/yangyi/kernel-build menuconfig
make O=/home/yangyi/kernel-build
sudo make O=/home/yangyi/kernel-build modules_install install


這樣同一個原始碼樹可以做多個build,各build可以根據需要重新build而不影響其他的build,因此建議大家使用這種方式build核心。)

特別提醒,您的系統現在啟動的必須是您按剛才要求編譯好的核心,否則在執行下面的操作之前您必須用該核心啟動您的系統。

# ln -s /home/yangyi/linux-2.6.20/vmlinux /boot/vmlinux-`uname -r`
# mkdir -p /usr/src/kernels
# ln -s /home/yangyi/linux-2.6.20 /usr/src/kernels/`uname -r`
# mkdir -p /lib/modules/`uname -r`
# ln -s /home/yangyi/kernel-build /lib/modules/`uname -r`/build
# ln -s /home/yangyi/linux-2.6.20 /lib/modules/`uname -r`/source


對於使用debian Linux的讀者,您可以使用如下的方式安裝Systemtap:

# apt-get build-dep systemtap
# apt-get --compile source systemtap
# dpkg -i systemtap*deb


如果您想在debian Linux中為Systemtap使用新的核心,您可以按下面的步驟去做:

1. 下載並配置最新的核心原始碼包

# apt-get install linux-source-2.6.20 kernel-package fakeroot
# cd /usr/src
# tar jxvf linux-source-2.6.20.tar.bz2
# cd linux-source-2.6.20
# cp /boot/config-2.6-xxxxxxxx .
# make menuconfig


配置以下核心選項:

Kernel hacking  --->
[*] Kernel debugging
[*] Compile the kernel with debug info

Instrumentation Support --->
[*] Kprobes (EXPERIMENTAL)

General setup --->
[*] Kernel->user space relay support (formerly relayfs)


2.新增下行到檔案/etc/kernel-pkg.conf

install_vmlinux = YES


3.構建核心

# fakeroot make-kpkg --initrd --append-to-version=-systemtap-1.0 /
kernel_image kernel_headers


注意兩個選 項:--initrd讓kernel-package構建initrd,--append-to-version將修改命令uname -a的輸出中出現的核心名稱,您可以指定為您喜歡的名稱。引數kernel_image表示構建核心映像,引數kernel_headers表示構建核心 標頭檔案。

4.安裝定製的核心

dpkg -i ../kernel-image-2.6.20-systemtap-1.0_10.00.Custom_i386.deb
dpkg -i ../kernel-headers-2.6.20-systemtap-1.0_10.00.Custom_i386.deb


注意,您構建的deb包的名稱依賴於您選擇的核心以及構建時的引數。

5.拷貝您的核心build目錄到/lib/modules/<your new kernel version>/build



下面給出一個簡單的例子詳細解釋systemtap的工作原理。

這個stp指令碼將每隔5秒鐘輸出系統呼叫的最多的20個系統呼叫。

#!/usr/bin/env stap
#
# This script continuously lists the top 20 systemcalls on the system
#

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++ == 20)
break
}
printf("--------------------------------------/n")
delete syscalls
}

probe kernel.function("sys_*") {
syscalls[probefunc()]++
}

# print top syscalls every 5 seconds
probe timer.ms(5000) {
print_top ()
}

該指令碼第一行指定了指令碼的解釋 器stap,它首先將分析該指令碼並把它轉換成C語言的程式碼,然後進行編譯,和systemtap安裝時帶的庫進行連結,產生一個核心模組並把它載入到系統 中,最後啟動一個使用者態程序不停得從systemtap提供的relayfs介面讀取資料並顯示到螢幕上。

#表示註釋,類似於shell語法。

語句global syscalls宣告syscalls是一個全域性變數。

注意,stp並不需要分號這樣的語句分割符。

function print_top()定義一個函式print_top,這與shell的語法類似。

Systemtap 實現了強大的輸出支援, log和printf用的比較多,其中printf與C語言中的printf語法一樣。迴圈語句foreach ([name] in syscalls-)表示把syscalls陣列按降序排列然後遍歷每一個它的元素, name將儲存得到的元素的下標,在該例項中就是系統呼叫名稱, 因而syscalls實際是一個關聯陣列。 讀者不難看出print_top函式就是輸出syscalls陣列中值最大的二十個元素。

probe kernel.function(“sys_*)為每一個以sys_開頭的核心函式定義一個kprobe探測點以及相應的探測點處理函式。對核心熟悉的讀者一看就知道以sys_開頭的核心函式就是系統呼叫。它定義的探測點函式是為相應的系統呼叫記數器加1。

probe timer.ms(5000)聲明瞭一個5000毫秒的定時器探測點(kprobe現在已經支援定時器探測點),相應的探測點處理函式將呼叫print_top輸出系統呼叫的最多的20個系統呼叫的名稱和呼叫次數。

更 多關於stp指令碼語言的語言參考能在最新的systemtap的原始碼包中找到,有興趣的讀者可以看看。需要特別提醒該指令碼在編譯連結後生成了一個核心模 塊,因此它執行在核心態,所有使用者在執行該指令碼後看到的輸出都是stap啟動的使用者態程序通過systemtap提供的relayfs介面從核心讀取出來 才顯示到螢幕上的。



小結

本文詳細地講解了Systemtap的工作原理並通過與Dtrace比較讓讀者瞭解Systemtap的來由和與現 存工具的異同。為了讓讀者能夠根據自己使用的Linux發行版方便安裝Systemtap,本文詳細講解了幾種安裝方式。最後通過一個實際的例子讓讀者真 正體會一下Systemtap與kprobe的關係和執行機理。本文是系列文章“Linux下的一個全新的效能測量和調式診斷工具 -- Systemtap”之三,有興趣的讀者可以閱讀該系列文章之一和二。