ftrace介紹和使用

Ftrace 是一個內核中的追蹤器,用於幫助系統開發者或設計者查看內核運行情況,它可以被用來調試或者分析延遲/性能問題。最早 ftrace 是一個 function tracer,僅能夠記錄內核的函數調用流程。如今 ftrace 已經成爲一個framework,採用 plugin 的方式支持開發人員添加更多種類的 trace 功能。


一、Ftrace 的內核配置

ftrace 相關的配置選項列表
     CONFIG_FUNCTION_TRACER
     CONFIG_FUNCTION_GRAPH_TRACER
     CONFIG_CONTEXT_SWITCH_TRACER
     CONFIG_NOP_TRACER

     CONFIG_SCHED_TRACER


在內核的Menuconfig中查看更加直觀:
    Kernel hacking  --->
        Tracers ─>
            [*]   Kernel Function Tracer
            [*]     Kernel Function Graph Tracer (NEW)

            ...  (下面還有幾個追蹤器的選項,可以根據自己的需要選擇)


注: 如果是在 32 位 x86 機器上,編譯時不要選中 General setup 菜單項下的 Optimize for size 選項否則就無法看到 Kernel Function Graph Tracer 選項。這是因爲在 Konfig 文件中,針對 32 位 x86 機器,表項 FUNCTION_GRAPH_TRACER 有一個特殊的依賴條件:

             depends on !X86_32 || !CC_OPTIMIZE_FOR_SIZE


Ftrace 通過 debugfs 向用戶態提供了訪問接口,所以還需要將 debugfs 編譯進內核。激活對 debugfs 的支持,可以直接編輯內核配置文件 .config ,設置CONFIG_DEBUG_FS=y ;或者在 make menuconfig 時到 Kernel hacking 菜單下選中對 debugfs 文件系統的支持:
    Kernel hacking  --->

        -*- Debug Filesystem


二、Ftrace 的基本使用步驟



2.1. 掛載Debugfs:

Ftrace 通過 debugfs 向用戶態提供訪問接口。配置內核時激活 debugfs 後會創建目錄 /sys/kernel/debug ,debugfs 文件系統就是掛載

到該目錄。


2.1.1 運行時掛載:

官方掛載方法 :
mount -t debugfs nodev /sys/kernel/debug
我覺得自己建一個 /debug 文件夾,掛載在這個路徑下使用比較方便:
# mkdir /debug
# mount -t debugfs nodev /debug
# cd /debug/tracing                    // debugfs掛載路徑下的tracing 纔是 ftrace 的“大本營”!

或者我們可以使用官方掛載辦法後建立一個軟連接:

mount -t debugfs nodev /sys/kernel/debug
        # ln -s /sys/kernel/debug  /debug 


2.1.2 系統啓動自動掛載:

要在系統啓動自動掛載debugfs,需要將如下內容添加到 /etc/fstab 文件:
        debugfs  /sys/kernel/debug  debugfs  defaults  0  0


2.2. 選擇一種 tracer:

# cat current_tracer           // 查看當前追蹤器
nop   // no option
# cat available_tracers      // 查看當前內核中可用跟蹤器
blk function_graph function nop
# echo function_graph > current_tracer        // 我們選用 function_graph 追蹤器


2.3. 打開關閉追蹤

在老一點版本的內核上tracing目錄下有tracing_enabled,需要給tracing_enabled和tracing_on同時賦值 1 才能打開追蹤,而在比較新的內核上已經去掉 tracing_enabled ,我們只需要控制tracing_on 即可打開關閉追蹤。

#echo 1 > tracing_on            // 打開跟蹤

# echo 0 > tracing_on             // 關閉跟蹤

# echo 1 > tracing_on; run_test; echo 0 > tracing_on          //  打開跟蹤後做一件事再關閉跟蹤,所以此次跟蹤的結果基本上是這個運行的程序的跟蹤結果,但是肯定會包括很多雜訊,所以後面會介紹跟蹤某個 pid 的或者跟蹤某個函數。

注:對於爲什麼去掉 tracing_enabled 我問過 Ftrace 的維護人Steven Rostedt,他說使用 tracing_on 可以快速的打開 Ftrace 的追蹤,這讓 tracing_enabled 顯得很輕量級或者說顯得比較冗餘,下面可以會說到,我們寫內核程序時可以使用Ftrace 提供的內核函數 tracing_on() or tracing_off() 直接打開追蹤,這其實就是使用的 tracing_on ,所以在新內核中 tracing_enabled 這個看起來比較冗餘的選項已經被刪除。


2.4. 查看追蹤結果

ftrace 的輸出信息主要保存在 3 個文件中。
*   trace,該文件保存 ftrace 的輸出信息,其內容可以直接閱讀。
*   latency_trace,保存與 trace 相同的信息,不過組織方式略有不同。主要爲了用戶能方便地分析系統中有關延遲的信息。
*   trace_pipe 是一個管道文件,主要爲了方便應用程序讀取 trace 內容。算是擴展接口吧。


所以可以直接查看 trace 追蹤文件,也可以在追蹤之前使用trace_pipe 將追蹤結果直接導向其他的文件。
比如: # cat trace_pipe > /tmp/log &     // 使用trace_pipe 將跟蹤結果導入 /tmp/log 裏,我們可以直接 “ cat /tmp/log” 查看跟蹤信息。
當然也可以直接查看trace文件 #cat trace 或者使用cat trace  > /tmp/log 將跟蹤信息導入 /tmp/log


三、 Ftrace 的進階使用:

3.1. 追蹤指定的進程

使用 echo pid > set_ftrace_pid  來追蹤指定的進程!
我們寫程序時可以使用getpid 獲取進程PID,然後使用 write 將pid 寫入 /debug/tracing/set_ftrace_pid ,並使用write 寫1 到 tracing_on 打開追蹤(因爲在用戶空間使用不了tracing_on函數),此時即可追蹤當前這個進程。

3.2. 追蹤事件:

3.2.1 首先查看事件文件夾下面有哪些選項

# ls events/
block    ext4    header_event  jbd2 napi raw_syscalls…… enable
# ls events/sched/
enable    sched_kthread_stop_ret  sched_process_exit sched_process_wait ……


3.2.2 追蹤一個/若干事件

# echo 1 > events/sched/sched_wakeup/enable

                ...(省略追蹤過程)
# cat trace | head -10
# tracer: nop
#TASK-PID  CPU#  TIMESTAMP    FUNCTION
# ||| |
bash-2613 [001] 425.078164: sched_wakeup: task bash:2613 [120] success=0 [001]
bash-2613 [001] 425.078184: sched_wakeup: task bash:2613 [120] success=0 [001]
...


3.2.3 追蹤一類事件

# echo 1 > events/sched/enable

                ... 
# cat trace | head -10
# tracer: nop
#TASK-PID            CPU#  TIMESTAMP    FUNCTION
#   |                           |                |                     | 
events/0-9               [000]   638.042792:   sched_switch: task events/0:9 [120] (S) ==> kondemand/0:1305 [120]
ondemand/0-1305   [000]    638.042796:  sched_stat_wait: task: restorecond:1395 wait: 15023 [ns]
...


3.2.4 追蹤所有事件

# echo 1 > events/enable

                ...
# cat trace | head -10
# tracer: nop
#TASK-PID     CPU#     TIMESTAMP    FUNCTION
#   |                    |                    |                   | 
cpid-1470       [001]   794.947181:         kfree: call_site=ffffffff810c996d ptr=(null)
acpid-1470     [001]794.947182:      sys_read -> 0x1
acpid-1470     [001]   794.947183:      sys_exit: NR 0 = 1
...


3.3. stack_trace

# echo 1 > /proc/sys/kernel/stack_tracer_enabled
OR # kernel command line “stacktrace”
查看: # cat stack_trace


3.4. 設置追蹤過濾器

將要跟蹤的函數寫入文件 set_ftrace_filter ,將不希望跟蹤的函數寫入文件 set_ftrace_notrace。通常直接操作文件 set_ftrace_filter 就可以了. 


四、 Ftrace 提供的函數使用

內核頭文件 include/linux/kernel.h 中描述了 ftrace 提供的工具函數的原型,這些函數包括 trace_printk、tracing_on/tracing_off 等。


4.1. 使用 trace_printk 打印跟蹤信息

ftrace 提供了一個用於向 ftrace 跟蹤緩衝區輸出跟蹤信息的工具函數,叫做 trace_printk(),它的使用方式與 printk() 類似。可以通過 trace 文件讀取該函數的輸出。從頭文件 include/linux/kernel.h 中可以看到,在激活配置 CONFIG_TRACING 後,trace_printk() 定義爲宏:
     #define trace_printk(fmt, args...)  
        ...
所以在使用時:(例子是在一個內核模塊中添加打印信息)
  1. #include <linux/init.h>   
  2.  #include <linux/module.h>   
  3.  #include <linux/kernel.h>   
  4.   
  5.  MODULE_LICENSE("GPL");   
  6.   
  7.  static int ftrace_demo_init(void)   
  8.  {   
  9.      trace_printk("Can not see this in trace unless loaded for the second time\n");   
  10.      return 0;   
  11.  }   
  12.   
  13.  static void ftrace_demo_exit(void)   
  14.  {   
  15.      trace_printk("Module unloading\n");   
  16.  }   
  17.   
  18.  module_init(ftrace_demo_init);   
  19.  module_exit(ftrace_demo_exit);  


4.2. 使用 tracing_on/tracing_off 控制跟蹤信息的記錄

在跟蹤過程中,有時候在檢測到某些事件發生時,想要停止跟蹤信息的記錄,這樣,跟蹤緩衝區中較新的數據是與該事件有關的。在用戶態,可以通過向文件 tracing_on 寫入 0 來停止記錄跟蹤信息,寫入 1 會繼續記錄跟蹤信息。而在內核代碼中,可以通過函數 tracing_on() 和 tracing_off() 來做到這一點,它們的行爲類似於對 /sys/kernel/debug/tracing 下的文件 tracing_on 分別執行寫 1 和 寫 0 的操作。
使用這兩個函數,會對跟蹤信息的記錄控制地更準確一些,這是因爲在用戶態寫文件 tracing_on 到實際暫停跟蹤,中間由於上下文切換、系統調度控制等可能已經經過較長的時間,這樣會積累大量的跟蹤信息,而感興趣的那部分可能會被覆蓋掉了。


實際代碼中,可以通過特定條件(比如檢測到某種異常狀況,等等)來控制跟蹤信息的記錄,函數的使用方式類似如下的形式:
 if (condition) 
tracing_on() or tracing_off()

跟蹤模塊運行狀況時,使用 ftrace 命令操作序列在用戶態進行必要的設置,而在代碼中則可以通過 traceing_on() 控制在進入特定代碼區域時開啓跟蹤信息,並在遇到某些條件時通過 tracing_off() 暫停;讀者可以在查看完感興趣的信息後,將 1 寫入 tracing_on 文件以繼續記錄跟蹤信息。實踐中,可以通過宏來控制是否將對這些函數的調用編譯進內核模塊,這樣可以在調試時將其開啓,在最終發佈時將其關閉。
用戶態的應用程序可以通過直接讀寫文件 tracing_on 來控制記錄跟蹤信息的暫停狀態,以便了解應用程序運行期間內核中發生的活動。


五、 簡單的 Ftrace 腳本案例:

我在下面使用一個腳本執行 Ftrace 的操作,自動追蹤 HelloWorld 程序執行:

  1. #!/bin/bash  
  2. debugfs_path=/debug                       
  3. ftrace_call_path=$debugfs_path/tracing  
  4. app_path=/home/dslab/test  
  5.   
  6. # 測試我們的 /debug 路徑是否已經建立  
  7. if ! test -d $debugfs_path; then  
  8.         echo "I'll create the directory /debug"  
  9.         mkdir $debugfs_path  
  10. else echo "Hum ,the /debug directory is standing by"  
  11. fi   
  12.   
  13. # 測試debugfs是否已經掛載  
  14. mount | grep "debugfs" > /dev/null  
  15. if [ $? != 0 ]; then  
  16.         echo "we have not mount debugfs"  
  17.         mount -t debugfs nodev $debugfs_path  
  18. else echo "yeah,debugfs has been mounted"  
  19. fi  
  20.   
  21. # 測試ftrace 是否可用  
  22. if ! test -d $ftrace_call_path; then  
  23.         echo "sorry, may be your kernel is not support for the ftrace"  
  24.         exit -1;  
  25. else echo "Hum ,Ftrace is standing by"  
  26. fi        
  27. # 將目前的trace 文件清空  
  28. echo "" >  $ftrace_call_path/trace  
  29. # 選擇 function_graph 跟蹤器  
  30. echo "function_graph" > $ftrace_call_path/current_tracer  
  31. # 我的Debian7 使用的3.2 內核上還有tracing_enabled ,先給它賦值 1  
  32. echo 1 > $ftrace_call_path/tracing_enabled  
  33. # 使用 trace_pipe 將追蹤結果重定向到/tmp/result(後臺運行,使得下面的步驟得以進行)  
  34. cat $ftrace_call_path/trace_pipe > /tmp/result &  
  35. # 打開追蹤  
  36. echo 1 > $ftrace_call_path/tracing_on  
  37. # 我在這裏執行了一個已經編譯好helloworld 程序  
  38. exec $app_path/hello  
  39. # 關閉追蹤  
  40. echo 0 > $ftrace_call_path/tracing_on  

運行效果如下:

  1. dslab@wheezy:~$ sudo ./ftrace.sh  
  2. [sudo] password for dslab:   
  3. Hum ,the /debug directory is standing by  
  4. yeah,debugfs has been mounted  
  5. Hum ,Ftrace is standing by  
  6. Hello World  
  7. dslab@wheezy:~$   


===================================

參考資料:

【1】Linux內核中的ftrace 文檔,路徑爲:linux-source-3.2/Documentation/trace/ftrace.txt (還有個ftrace-design.txt也很值得學習)

【2】ftrace 簡介 :   http://www.ibm.com/developerworks/cn/linux/l-cn-ftrace/

【3】使用 ftrace 調試 Linux 內核:
            part1   :   http://www.ibm.com/developerworks/cn/linux/l-cn-ftrace1/
            part2   :   http://www.ibm.com/developerworks/cn/linux/l-cn-ftrace2/
            part3   :   http://www.ibm.com/developerworks/cn/linux/l-cn-ftrace3/


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