systemtap系列之內核系統診斷使用方法

systemtap系列之系統診斷

SystemTap 是監控和跟蹤運行中的 Linux 內核的操作的動態方法。SystemTap 沒有使用工具構建一個特殊的內核,而是允許您在運行時動態地安裝該工具。它通過一個名爲Kprobes 的應用編程接口(API)來實現該目的

SystemTap 與一種名爲 DTrace 的老技術相似,該技術源於 Sun Solaris 操作系統。

測試內核看它是否支持 SystemTap:

stap -ve ‘probe begin { log(“hello world”) exit() }’

如果不支持就安裝一下,yum install systemtap就好了,缺什麼就安裝什麼,光盤自帶了。如果出現如下錯誤:semantic error: missing x86_64 kernel/module debuginfo [man warning::debuginfo]:答:yum install kernel-debuginfo 安裝內核debug信息。

SystemTap 用於檢查運行內核的兩種方法是 Kprobes 和 返回探針。但是理解任何內核的最關鍵要素是內核的映射,它提供符號信息(比如函數、變量以及它們的地址)。有了內核映射之後,就可以解決任何符號的地址,以及更改探針的行爲。

1.SystemTap 的基本流程

SystemTap 的基本流程,涉及到 3 個交互實用程序和 5 個階段。流程首先從 SystemTap 腳本開始。您使用 stap 實用程序將 stap 腳本轉換成提供探針行爲的內核模塊。stap 流程從將腳本轉換成解析樹開始 (pass 1)。然後使用細化(elaboration)步驟 (pass 2) 中關於當前運行的內核的符號信息解析符號。接下來,轉換流程將解析樹轉換成 C 源代碼 (pass 3) 並使用解析後的信息和 tapset 腳本(SystemTap 定義的庫,包含有用的功能)。stap 的最後步驟是構造使用本地內核模塊構建進程的內核模塊 (pass 4)。
有了可用的內核模塊之後,stap 完成了自己的任務,並將控制權交給其他兩個實用程序 SystemTap:staprun 和 stapio。這兩個實用程序協調工作,負責將模塊安裝到內核中並將輸出發送到 stdout (pass 5)。如果在 shell 中按組合鍵 Ctrl-C 或腳本退出,將執行清除進程,這將導致卸載模塊並退出所有相關的實用程序。
在這裏插入圖片描述
從內核態/用戶態來了解如下:
2.png

systemtap工作原理
在這裏插入圖片描述
SystemTap 腳本編寫

SystemTap 腳本由探針和在觸發探針時需要執行的代碼塊組成。
使用-L可以測試某條語句是否是正確的:
stap -L ‘process("/usr/local/mysql/libexec/mysqld").function(“apply_event”)’

關於探針

基本參考如下:

探針類型 說明 begin 在腳本開始時觸發 end 在腳本結束時觸發 kernel.function(“sys_sync”) 調用
sys_sync 時觸發 kernel.function(“sys_sync”).call 同上
kernel.function(“sys_sync”).return 返回 sys_sync 時觸發 kernel.syscall.*
進行任何系統調用時觸發 kernel.function("*@kernel/fork.c:934") 到達 fork.c 的第 934
行時觸發 module(“ext3”).function(“ext3_file_write”) 調用 ext3 write 函數時觸發
timer.jiffies(1000) 每隔 1000 個內核 jiffy 觸發一次 timer.ms(200).randomize(50)
每隔 200 毫秒觸發一次,帶有線性分佈的隨機附加時間(-50 到 +50)

變量和類型

SystemTap 允許定義多種類型的變量,類型可以從上下文推斷得出的,因此不需要使用類型聲明。在 SystemTap 中,您可以找到數字(64 位簽名的整數)、整數(64 位)、字符串和字面量(字符串或整數)。還可以使用關聯數組和統計數據。
表達式
SystemTap 提供 C 語言中常用的所有必要操作符,並且用法也是一樣的。還可以找到算術操作符、二進制操作符、賦值操作符和指針廢棄。還看到從 C 語言帶來的簡化,其中包括字符串連接、關聯數組元素和合並操作符。
語言元素
在探針內部,SystemTap 提供一組類似於 C 一樣易於使用的語句。需要注意的是,儘管該語言允許您開發複雜的腳本,但每個探針只能執行 1000 條語句(這個數量是可配置的)。許多元素和 C 中的一樣,儘管有一些附加的東西是特定於 SystemTap 的。
語句 說明
if (exp) {} else {} 標準的 if-then-else 語句
for (exp1 ; exp2 ; exp3 ) {} 一個 for 循環
while (exp) {} 標準的 while 循環
do {} while (exp) 一個 do-while 循環
break 退出迭代
continue 繼續迭代
next 從探針返回
return 從函數返回一個表達式
foreach (VAR in ARRAY) {} 迭代一個數組,將當前的鍵分配給 VAR
SystemTap 提供許多內部函數,這些函數提供關於當前上下文的額外信息。可以使用 caller() 識別當前的調用函數,使用 cpu() 識別當前的處理器號碼,以及使用 pid() 返回 PID。

統計系統調用 sys_sync

調用內核系統調用 sys_sync 時觸發。當該探針觸發時,計算調用的次數,併發送這個計數以及表示調用進程 ID(PID)的信息。首先,聲明一個任何探針都可以使用的全局值(全局名稱空間對所有探針都是通用的),然後將它初始化爲 0。其次,定義探針,它是一個探測內核函數 sys_sync 的條目。與探針相關聯的腳本將遞增 count 變量,然後發出一條消息,該消息定義調用的次數和當前調用的 PID。

global count=0
probe kernel.function("sys_sync") {
count++
printf( "sys_sync called %d times, currently by pid %d\n", count, pid() );
}

執行stap syscall.stp

stap命令與staprun命令

命令的區別在於:
stap命令的操作對象是stp文件或script命令等,而staprun命令的操作對象是編譯生成的內核模塊。

監控所有系統調用

腳本如下:

global syscalllist
probe begin {
printf("System Call Monitoring Started (10 seconds)...\n")
}

probe syscall.* {
syscalllist[pid(), execname()]++
}

probe timer.ms(10000) {
foreach ( [pid, procname] in syscalllist ) {
printf("%s[%d] = %d\n", procname, pid, syscalllist[pid, procname] )
}
exit()
}

統計10秒內系統調用

腳本如,代碼中定義了一個10秒的定時器:

global syscalllist
probe begin {
printf("System Call Monitoring Started (10 seconds).../n")
}
probe syscall.*
{
syscalllist[pid(), execname()]++
}
probe timer.ms(10000) {
foreach ( [pid, procname] in syscalllist ) {
printf("%s[%d] = %d/n", procname, pid, syscalllist[pid, procname] )
}
exit()
}

收集網絡包長度

參考如下腳本:

global recv, xmit

probe begin {
printf("Starting network capture (Ctl-C to end)\n")
}

probe netdev.receive {
recv[dev_name, pid(), execname()] <<< length
}

probe netdev.transmit {
xmit[dev_name, pid(), execname()] <<< length
}

probe end {
printf("\nEnd Capture\n\n")

printf("Iface Process........ PID.. RcvPktCnt XmtPktCnt\n")

foreach ([dev, pid, name] in recv) {
recvcount = @count(recv[dev, pid, name])
xmitcount = @count(xmit[dev, pid, name])
printf( "%5s %-15s %-5d %9d %9d\n", dev, name, pid, recvcount, xmitcount )
}

delete recv
delete xmit
}

按 Ctrl-C 時退出腳本,然後發送捕獲的數據。

柱狀顯示數據
以柱狀圖的形式顯示數據,將數據捕獲到一個名爲 histogram 的聚合中。然後,使用 netdev 接收和發送探針以捕捉包長度數據。當探針結束時,使用 @hist_log 提取器以柱狀圖的形式呈現數據。@hist_log 提取器是一個以 2 爲底數的對數柱狀圖.
代碼如下:

global histogram

probe begin {
printf("Capturing...\n")
}

probe netdev.receive {
histogram <<< length
}

probe netdev.transmit {
histogram <<< length
}

probe end {
printf( "\n" )
print( @hist_log(histogram) )
}

systemtap其他作用
定位函數位置
stap -l ‘process("/lib/x86_64-redhat-linux6E/lib64/libc.so").function(“printf”)’

查找內核代碼中的函數
如果內核函數sys_open被新定義,可以通過如下命令進行查找。
#stap -L ‘kernel.function(“sys_open”)’
kernel.function(“SyS_open@fs/open.c:1036”) $filename:long int $flags:long int $mode:long int

參考
linuxperformance-tools
http://www.brendangregg.com/linuxperf.html
Linux 自檢和 SystemTap
https://www.ibm.com/developerworks/cn/linux/l-systemtap/
Linux 下的一個全新的性能測量和調式診斷工具 Systemtap, 第 3 部分: Systemtap
https://www.ibm.com/developerworks/cn/linux/l-cn-systemtap3/
systamtap編程參考
https://sourceware.org/systemtap/langref/
systemtap有用的IO監控腳本
https://segmentfault.com/a/1190000000680628
調試內核模塊
http://blog.chinaunix.net/uid-14528823-id-4726046.html

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章