1. 程式人生 > >SystemTap使用技巧【二】

SystemTap使用技巧【二】

1、獲取資料結構成員

    比如資料結構如下:
[email protected] ~/develop# cat -n cc_stap_test.c
     1  #include <stdio.h>
     2  
     3  typedef struct str {
     4      int    len;
     5      char *data;
     6  } str_t;
     7  
     8  typedef struct policy {
     9      str_t    name;
    10      int     id;
    11  } policy_t;
    12  
    13  int main(int argc, char *argv[])
    14  {
    15      policy_t policy;
    16      policy_t *p = &policy;
    17  
    18      p->id = 111;
    19      p->name.data = "test";
    20      p->name.len = sizeof("test")-1;
    21  
    22      printf("p->id: %d, p->name.data: %s, p->name.len: %d\n", p->id, p->name.data, p->name.len);
    23  
    24      return 0;
    25  }

[email protected]
~/develop# gcc -Wall -g -o cc_stap_test ./cc_stap_test.c
    如果要在第22行設定個statement探測點獲取區域性變數p中name的data,那怎麼寫stp呢,這個問題在剛學SystemTap時困擾了我兩天,不過後來仔細看官方文件時才知道,最直覺的寫法就像下面這個:
probe process("./cc_stap_test").statement("[email protected]/cc_stap_test.c:22")
{
    printf("policy name: %s\n", $p->name.data);
}
$p是指標,通過->箭頭可以直接訪問name,這個沒有問題,但name不是指標而是一個結構啊,難道不是像C語言一樣用點號來訪問它裡面的成員嗎。這是C語言的思路,可惜是不對的,SystemTap已經把點號用來做字串連線符了,所以SystemTap把結構變數或者結構指標變數統一對待,都是通過->來訪問結構裡面或者結構指標所指結構裡面的成員,上面例子正確寫法如下:
probe process("./cc_stap_test").statement("[email protected]/cc_stap_test.c:22")
{
    printf("policy name: %s\n", $p->name->data);
}

2、列印整個資料結構

     在除錯的時候,有時我們需要輸出整個資料結構,看看結構裡面變數的值到底是什麼。比如上面的例子中我們想列印變數p中所有成員的值,應該是這樣:
[email protected] ~/develop# cat cc_stap_test.stp
probe process("./cc_stap_test").statement("[email protected]/cc_stap_test.c:22")
{
    printf("$p$: %s, $p$$: %s\n", $p$, $p$$);
}

[email protected] ~/develop# stap cc_stap_test.stp -c './cc_stap_test'        
p->id: 111, p->name.data: test, p->name.len: 4
$p$: {.name={...}, .id=111}, $p$$: {.name={.len=4, .data="test"}, .id=111}

[email protected] ~/develop# 
     就是在變數的後面加$或者$$,其中加$是表示獲取結構中基本資料型別的字串值,像上面$p$就只輸出id的值,name就省略了;變數後面加$$功能與加一個$類似,只是它要展開結構裡面的結構,上面的輸出已經很明顯了。

3、修改函式變數

     還是上面的例子,cc_stap_test.c第18行給id賦了值111,在第20行,我們也可以用SystamTap修改id的值:
[email protected] ~/develop# cat cc_stap_set_var.stp 
probe process("./cc_stap_test").statement("[email protected]/cc_stap_test.c:20")
{
    $p->id = 222;
    printf("$p$: %s, $p$$: %s\n", $p$, $p$$);
}

[email protected] ~/develop# stap -g cc_stap_set_var.stp -c './cc_stap_test'
p->id: 222, p->name.data: test, p->name.len: 4
$p$: {.name={...}, .id=222}, $p$$: {.name={.len=0, .data="test"}, .id=222}
     直接賦值即可,只是需要注意的是stap要加-g引數在guru模式下才能修改變數的值。

4、可選探測點和嘗試探測點用法

     如果你看SystemTap的tapset,裡面有些語法可能看不懂,比如設定probe時後面的問號或者感嘆號是什麼意思,看看/usr/local/share/systemtap/tapset/linux/memory.stp的一個例子:
     這個probe裡面有兩個!和兩個?,簡單地說?定義可選探測點,!定義嘗試探測點。可選探測點就是即使不能在這裡設定探測點就不報錯了而直接忽略這個探測點。嘗試探測點是前面探測點設定失敗之後再嘗試設定後面的探測點,像上面這個例子,SystemTap會嘗試設定kernel.function("vm_mmap")這個探測點,如果設定成功,那麼後面的kernel.function("do_mmap_pgoff") !, kernel.function("do_mmap") ?, kernel.function("do_mmap2") ? 就直接忽略了,如果設定失敗,那繼續嘗試設定kernel.function("do_mmap_pgoff")這個探測點,同樣成功的話後面的兩個探測點就不用設定了,失敗了才設定kernel.function("do_mmap")。

5、跟蹤程序執行流程

     當我在學習新程式碼時,首先想要了解的是程式碼的處理流程,比如在學haproxy和nginx的時候,首先想看看它們是從main函式開始後怎麼從核心收發資料,這個是我學習這兩款開原始碼的切入點,在還沒學會SystemTap之前,硬是看了好幾遍程式碼啊,而且很多地方有條件編譯或者函式指標就比較難分析了,後來用gdb設斷點分析總算大體流程能分析明白了,但還是不太理想。最近才發現SystemTap分析起來太方便了,而且能實時記錄函式的耗費時間,整個函式呼叫流程就實時呈現到眼前了,太太爽了。看看下面這個是分析nginx worker程序的執行流程:
[email protected] ~/systemtap# cat trace_nginx.stp           
probe process("/opt/nginx-dso/sbin/nginx").function("*").call
{
    printf("%s -> %s\n", thread_indent(4), ppfunc());
}

probe process("/opt/nginx-dso/sbin/nginx").function("*").return
{
    printf("%s <- %s\n", thread_indent(-4), ppfunc());
}

[email protected] ~/systemtap# stap -x 29774 trace_nginx.stp 
WARNING: function _start return probe is blacklisted: keyword at trace_nginx.stp:6:1
 source: probe process("/opt/nginx-dso/sbin/nginx").function("*").return
         ^
     0 nginx(29774):    -> ngx_time_update
    10 nginx(29774):    <- ngx_time_update
     0 nginx(29774):    -> ngx_event_process_posted
     3 nginx(29774):    <- ngx_event_process_posted
     0 nginx(29774):    -> ngx_event_expire_timers
     3 nginx(29774):    <- ngx_event_expire_timers
     0 nginx(29774):    -> ngx_event_process_posted
     2 nginx(29774):    <- ngx_event_process_posted
     0 nginx(29774):    -> ngx_process_events_and_timers
     5 nginx(29774):        -> ngx_event_find_timer
     8 nginx(29774):        <- ngx_event_find_timer
    11 nginx(29774):        -> ngx_trylock_accept_mutex
    16 nginx(29774):            -> ngx_shmtx_trylock
    19 nginx(29774):            <- ngx_shmtx_trylock
    21 nginx(29774):        <- ngx_trylock_accept_mutex
    25 nginx(29774):        -> ngx_epoll_process_events
500591 nginx(29774):            -> ngx_time_update
500604 nginx(29774):                -> ngx_gmtime
500608 nginx(29774):                <- ngx_gmtime
500617 nginx(29774):                -> ngx_vslprintf
500623 nginx(29774):                    -> ngx_sprintf_num
……
500655 nginx(29774):                    <- ngx_sprintf_num
500657 nginx(29774):                <- ngx_vslprintf
500659 nginx(29774):            <- ngx_sprintf
500664 nginx(29774):            -> ngx_localtime
500689 nginx(29774):            <- ngx_localtime
500695 nginx(29774):            -> ngx_vslprintf
500699 nginx(29774):                -> ngx_sprintf_num
……
500732 nginx(29774):                <- ngx_sprintf_num
500734 nginx(29774):            <- ngx_vslprintf
500736 nginx(29774):        <- ngx_sprintf
500741 nginx(29774):        -> ngx_vslprintf
500745 nginx(29774):            -> ngx_sprintf_num
……
500784 nginx(29774):            <- ngx_sprintf_num
500786 nginx(29774):        <- ngx_vslprintf
500788 nginx(29774):    <- ngx_sprintf
     0 nginx(29774):    -> ngx_vslprintf
     4 nginx(29774):        -> ngx_sprintf_num
……
    48 nginx(29774):        <- ngx_sprintf_num
    50 nginx(29774):    <- ngx_vslprintf
     0 nginx(29774): <- ngx_sprintf
     0 nginx(29774):    -> ngx_vslprintf
     5 nginx(29774):        -> ngx_sprintf_num
……
    25 nginx(29774):        <- ngx_sprintf_num
    27 nginx(29774):    <- ngx_vslprintf
     0 nginx(29774): <- ngx_sprintf
     0 nginx(29774): <- ngx_time_update
     0 nginx(29774): <- ngx_epoll_process_events
     (省略號是刪去一些重複的輸出,下同)。前面的數字是這個函式呼叫開始或者結束時間,單位是微秒。      這個效果是thread_indent函式實現的,引數是新增空格的數量。不過這個函式有點問題,打印出來補的空格不太對,先來看看它的程式碼:
     預設程式碼路徑在/usr/local/share/systemtap/tapset/indent.stp,從上面截圖可以看出thread_indent直接呼叫_generic_indent,_generic_indent這個函式又呼叫_generic_indent_depth來獲取對齊的空格數depth,_generic_indent_depth這個函式把thread_indent傳進來的引數delta更新到_indent_counters全域性陣列中,但是thread_indent(4)和thread_indent(-4)不對稱也就是呼叫次數不一樣的時候就容易出問題了,在分析執行中的程序時就容易出現這種情況,這是因為在function return探測點中呼叫thread_indent(-4),除錯執行中的程序時可能就直接從一個函式中間開始分析,這時就只捕獲到return探測點而捕獲不到call探測點,這就導致_indent_counters[idx]一直小於0,_generic_indent_depth返回的x就一直是0,從而導致輸出對齊一直是0相當於沒對齊,效果像下面這樣:
[email protected] ~/systemtap# stap -x 29774 trace_nginx.stp
0 nginx(29774):    -> ngx_vslprintf
     4 nginx(29774):        -> ngx_sprintf_num
……
    48 nginx(29774):        <- ngx_sprintf_num
    50 nginx(29774):    <- ngx_vslprintf
     0 nginx(29774): <- ngx_sprintf
     5 nginx(29774): -> ngx_vslprintf
     0 nginx(29774):    -> ngx_sprintf_num
……
     2 nginx(29774):    <- ngx_sprintf_num
     0 nginx(29774): <- ngx_vslprintf
     2 nginx(29774): <- ngx_sprintf
     3 nginx(29774): <- ngx_time_update
     5 nginx(29774): <- ngx_epoll_process_events
    11 nginx(29774): -> ngx_event_process_posted
    14 nginx(29774): <- ngx_event_process_posted
    19 nginx(29774): -> ngx_event_expire_timers
    22 nginx(29774): <- ngx_event_expire_timers
    25 nginx(29774): -> ngx_event_process_posted
    28 nginx(29774): <- ngx_event_process_posted
    30 nginx(29774): <- ngx_process_events_and_timers
    35 nginx(29774): -> ngx_process_events_and_timers
前面部分輸出效果是對的,後面就沒效果了。 _generic_indent_depth應該修改成這樣(其實就是限制_indent_counters[idx]最小值為0):

6、跟蹤特定程序

     跟蹤一個程序直接用stap的-x引數指定特定程序的PID就可以了(有時候也要在stp程式碼裡面用target()進行過濾),但是像haproxy或者nginx這種多個子程序而我們想跟蹤所有子程序系統呼叫的話就得在程式碼裡面實現了,其實也比較簡單,用execname()獲取程序名,再進行匹配過濾就可以了,比如下面是跟蹤haproxy系統呼叫的例子:
[email protected] ~/systemtap# ps aux | grep haproxy          
root       314  0.0  0.0   9388   912 pts/11   S+   09:08   0:00 grep --color haproxy
99       22785  0.0  0.4  11428  4440 ?        Ss   Jan22   0:22 ./haproxy -f ./haproxy.cfg
99       22786  0.0  0.3  10140  3132 ?        Ss   Jan22   0:21 ./haproxy -f ./haproxy.cfg
99       22787  0.0  0.2   9020  2084 ?        Ss   Jan22   0:21 ./haproxy -f ./haproxy.cfg
99       22788  0.0  0.2   9168  2076 ?        Ss   Jan22   0:21 ./haproxy -f ./haproxy.cfg

[email protected] ~/systemtap# cat trace_haproxy_syscall.stp 
probe nd_syscall.*
{
    procname = execname();
    if (procname =~ "haproxy.*") {
        printf("%s[%d]: %s\n", procname, pid(), name);
    }
}

[email protected] ~/systemtap# stap trace_haproxy_syscall.stp 
haproxy[22785]: gettimeofday
haproxy[22785]: gettimeofday
haproxy[22785]: epoll_wait
haproxy[22787]: gettimeofday
haproxy[22787]: gettimeofday
haproxy[22787]: epoll_wait
haproxy[22787]: gettimeofday

7、正則表示式匹配

     上面的例子中就用到了正則表示式匹配,SystemTap提供兩個正則匹配的運算子,分別是=~和!~,=~就是按正則匹配第二個運算元,!~就是取反嘍。支援大多數POSIX及其擴充套件的語法。 上一篇文章寫了一些SystemTap的技巧,在這裡:SystemTap使用技巧【一】 參考:

相關推薦

SystemTap使用技巧

1、獲取資料結構成員     比如資料結構如下: [email protected] ~/develop# cat -n cc_stap_test.c 1 #include <stdio.h> 2 3 typed

SystemTap使用技巧

1、檢視程式碼執行路徑      在分析程式碼的時候,能清晰地觀察程式碼執行路徑對Debug效率很有幫助,比如,最近在分析核心tcp回覆ack的程式碼執行路徑的時候就用到這個技巧,看看下面是tcp回覆ack的程式碼:      我想看看程式碼執行到if分支裡面還是else

SystemTap使用技巧

SystemTap是一個強大的除錯工具,確切的說應該是一門除錯語言,因為它有自己的語法,也有解析、編譯、執行等過程(準確的說有五個階段),但它主要解決的問題是收集Linux核心或者使用者程序的資訊,主要目的是除錯。我一直以為gdb、kgdb是Linux最強大的偵錯程式,曾經

SystemTap使用技巧

1、檢視核心檔案中函式的執行流程         前段時間研究了一下Linux核心訊號處理流程,記錄一下用到的技巧吧。         其實如果不用工具,硬是看程式碼去分析這個訊號處理流程的話,還真的可能搞不定,因為不知道看到的程式碼是否得到執行,有可能都沒有編譯進去,所以

SCCM部署規劃

sccm 規劃 sccm 本篇主要對SCCM的前期規劃進行討論。 1.Active Directory整合 在ConfigMgr部署中,可以選擇CofigMgr和現有的Active Directory進行整合,既然整合與否並非強制性的,那與Active Directo

[轉]Web APi之認證(Authentication)兩種實現方式(十三)

用戶數 ted das 客戶 元素 基礎 目標 開始 net 本文轉自:http://www.cnblogs.com/CreateMyself/p/4857799.html 前言 上一節我們詳細講解了認證及其基本信息,這一節我們通過兩種不同方式來實現認證,並且分析如

搜索練習

std 技術分享 arc out gin load cnblogs view mes 1617: 阿克曼函數(遞歸) 時間限制: 1 Sec 內存限制: 128 MB提交: 135 解決: 91[提交][狀態][討論版] 題目描述 阿克曼(

遺傳算法(GA)的MATLAB實現

tool view ima baidu ges matlab實現 編程 from 函數調用 essay from:https://wenku.baidu.com/view/ce45bbf44693daef5ef73df3.html 一、MATLAB編程實現GA

大數據HDFS部署及文件讀寫(包含eclipse hadoop配置)

throw 大數據 我的電腦 ssh 生效 manager 方法 slave .sh 一  原理闡述 1‘  DFS     分布式文件系統(即DFS,Distributed File System),指文件系統管理的物理存儲資源不一定直接連接在本地節點上,而是通過計算機網

008-shiro與spring web項目整合認證、授權、session管理

添加 ner != efi ebs ref private date err 一、認證 1、添加憑證匹配器 添加憑證匹配器實現md5加密校驗。 修改applicationContext-shiro.xml: <!-- realm -->

最常用的15大Eclipse開發快捷鍵技巧

建議 ont ports 腳本語言 launch rate 成功 顯示屏 uil 引言 做java開發的,經常會用Eclipse或者MyEclise集成開發環境,一些實用的Eclipse快捷鍵和使用技巧,可以在平常開發中節約出很多時間提高工作效率,下面我就結合

剛學Python的幾道簡單練習題

print text for 輸入密碼 color 三次 center p s int python交友娛樂會所:613176398 1、使用while循環輸入 1 2 3 4 5 6 8 9 10 2、求1-100的所有數的和 3、輸出 1-100 內的所

MongoDB入門

ger 劃分 lob .com 字符 help further 如果 keys 下面是mongodb的一些基本概念: 文檔是MongoDB中數據的基本單元,類似關系數據庫中的行。 集合,是存儲文檔的容器,類似關系數據庫中的表。 MongoDB的單個實例容納多個數據庫,每個

riot.js教程組件撰寫準則、預處理器、標簽樣式和裝配方法

def coffee 將在 tom enter 名稱 spa 配方法 undefined 基本要求 一個riot標簽,就是展現和邏輯的組合(也就是html和JS); 以下是編寫riot標簽最基本的規則: 先撰寫HTML,再撰寫JS,JS代碼可以寫在<script

移動端三事:移動端觸摸事件點透及多種解決方案。

優化 提前 sta 屬性 lis 剛才 觸摸事件 功能 觸發 大家都知道的少說,多分享一些幹貨。 一、首先說移動端的三大主要事件: 1.手指按下: ontouchstart2.手指移動:ontouchmove3.手指擡起 ontouchend *使用移動端事件時,為盡

PHP+Redis 實例頁面緩存 新玩法

做了 urn 最好 博客 更新 有一個 返回 致命傷 什麽 今天算是認識到博客園裏的審查團隊多內幕了,哈哈,貼個圖玩下。 氣死寶寶了。 進入主題! 今天就不寫什麽功能性的了,換下口味說下關於頁面級的緩存,應該怎麽做。 相信有很多小夥伴查了百度,甚至google,

004-詮釋 Java 工程師

configure 帶來 pat 9.png 開放 images 組件 測試 根據 三、框架篇 框架基礎 反射:反射是Java開發的一類動態相關機制。因為本身Java語言並不是一款動態語言,如果我們想要得到程序動態的效果,因此便引入了反射機制這一概念。

Linuxlinux 壓縮文件(txt)、查看壓縮文件內容、解壓縮文件、

str tool div png gun medium spa clas info 通過Xshell 壓縮文件、解壓縮文件 gzip  tools.txt        壓縮【tools.txt】文件 zcat  tools.txt.gz       查看壓縮文件

Mongodb高級查詢

osql log _id 入參 init 講解 mon 關系 class 上一篇文章,寫了 mongodb常規操作,繼續寫入,本章主要講高級查詢,文本,聚集,大數據查詢。 Mongodb的查詢語法是很多的,是NOSQL隊伍中比較豐富的一個。當然有很多查詢跟關系型查詢無法相比

Web APi之認證(Authentication)兩種實現方式(十三)

基於web 推薦 zed {0} scheme sage https 函數 ges 原文:Web APi之認證(Authentication)兩種實現方式【二】(十三)前言 上一節我們詳細講解了認證及其基本信息,這一節我們通過兩種不同方式來實現認證,並且分析如何合理的利用