一、Ftrace 的內核配置
ftrace 相關的配置選項列表CONFIG_FUNCTION_TRACER
CONFIG_FUNCTION_GRAPH_TRACER
CONFIG_CONTEXT_SWITCH_TRACER
CONFIG_NOP_TRACER
CONFIG_SCHED_TRACER
Kernel hacking --->
Tracers ─>
[*] Kernel Function Tracer
[*] Kernel Function Graph Tracer (NEW)
... (下面還有幾個追蹤器的選項,可以根據自己的需要選擇)
depends on !X86_32 || !CC_OPTIMIZE_FOR_SIZE
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 // 關閉跟蹤
注:對於爲什麼去掉 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_enabledOR # 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...) \
...
所以在使用時:(例子是在一個內核模塊中添加打印信息)
- #include <linux/init.h>
- #include <linux/module.h>
- #include <linux/kernel.h>
- MODULE_LICENSE("GPL");
- static int ftrace_demo_init(void)
- {
- trace_printk("Can not see this in trace unless loaded for the second time\n");
- return 0;
- }
- static void ftrace_demo_exit(void)
- {
- trace_printk("Module unloading\n");
- }
- module_init(ftrace_demo_init);
- 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 程序執行:
- #!/bin/bash
- debugfs_path=/debug
- ftrace_call_path=$debugfs_path/tracing
- app_path=/home/dslab/test
- # 測試我們的 /debug 路徑是否已經建立
- if ! test -d $debugfs_path; then
- echo "I'll create the directory /debug"
- mkdir $debugfs_path
- else echo "Hum ,the /debug directory is standing by"
- fi
- # 測試debugfs是否已經掛載
- mount | grep "debugfs" > /dev/null
- if [ $? != 0 ]; then
- echo "we have not mount debugfs"
- mount -t debugfs nodev $debugfs_path
- else echo "yeah,debugfs has been mounted"
- fi
- # 測試ftrace 是否可用
- if ! test -d $ftrace_call_path; then
- echo "sorry, may be your kernel is not support for the ftrace"
- exit -1;
- else echo "Hum ,Ftrace is standing by"
- fi
- # 將目前的trace 文件清空
- echo "" > $ftrace_call_path/trace
- # 選擇 function_graph 跟蹤器
- echo "function_graph" > $ftrace_call_path/current_tracer
- # 我的Debian7 使用的3.2 內核上還有tracing_enabled ,先給它賦值 1
- echo 1 > $ftrace_call_path/tracing_enabled
- # 使用 trace_pipe 將追蹤結果重定向到/tmp/result(後臺運行,使得下面的步驟得以進行)
- cat $ftrace_call_path/trace_pipe > /tmp/result &
- # 打開追蹤
- echo 1 > $ftrace_call_path/tracing_on
- # 我在這裏執行了一個已經編譯好helloworld 程序
- exec $app_path/hello
- # 關閉追蹤
- echo 0 > $ftrace_call_path/tracing_on
運行效果如下:
- dslab@wheezy:~$ sudo ./ftrace.sh
- [sudo] password for dslab:
- Hum ,the /debug directory is standing by
- yeah,debugfs has been mounted
- Hum ,Ftrace is standing by
- Hello World
- 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/