1. 程式人生 > >systemtap系列之核心系統診斷使用方法

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(“[email protected]/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