Ftrace 是一個內(nèi)核中的追蹤器,用于幫助系統(tǒng)開發(fā)者或設(shè)計者查看內(nèi)核運行情況,它可以被用來調(diào)試或者分析延遲/性能問題。最早 ftrace 是一個 function tracer,僅能夠記錄內(nèi)核的函數(shù)調(diào)用流程。如今 ftrace 已經(jīng)成為一個framework,采用 plugin 的方式支持開發(fā)人員添加更多種類的 trace 功能。
一、Ftrace 的內(nèi)核配置
ftrace 相關(guān)的配置選項列表
CONFIG_FUNCTION_TRACER
CONFIG_FUNCTION_GRAPH_TRACER
CONFIG_CONTEXT_SWITCH_TRACER
CONFIG_NOP_TRACER
CONFIG_SCHED_TRACER
在內(nèi)核的Menuconfig中查看更加直觀:
Kernel hacking --->
Tracers ─>
[*] Kernel Function Tracer
[*] Kernel Function Graph Tracer (NEW)
... (下面還有幾個追蹤器的選項,可以根據(jù)自己的需要選擇)
注: 如果是在 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 向用戶態(tài)提供了訪問接口,所以還需要將 debugfs 編譯進內(nèi)核。激活對 debugfs 的支持,可以直接編輯內(nèi)核配置文件 .config ,設(shè)置CONFIG_DEBUG_FS=y ;或者在 make menuconfig 時到 Kernel hacking 菜單下選中對 debugfs 文件系統(tǒng)的支持:
Kernel hacking --->
-*- Debug Filesystem
二、Ftrace 的基本使用步驟
2.1. 掛載Debugfs:
Ftrace 通過 debugfs 向用戶態(tài)提供訪問接口。配置內(nèi)核時激活 debugfs 后會創(chuàng)建目錄 /sys/kernel/debug ,debugfs 文件系統(tǒng)就是掛載
到該目錄。
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 系統(tǒng)啟動自動掛載:
要在系統(tǒng)啟動自動掛載debugfs,需要將如下內(nèi)容添加到 /etc/fstab 文件:
debugfs /sys/kernel/debug debugfs defaults 0 0
2.2. 選擇一種 tracer:
#
cat current_tracer // 查看當(dāng)前追蹤器
nop // no option
# cat available_tracers // 查看當(dāng)前內(nèi)核中可用跟蹤器
blk function_graph function nop
# echo function_graph > current_tracer // 我們選用 function_graph 追蹤器
2.3. 打開關(guān)閉追蹤
在老一點版本的內(nèi)核上tracing目錄下有tracing_enabled,需要給tracing_enabled和tracing_on同時賦值 1 才能打開追蹤,而在比較新的內(nèi)核上已經(jīng)去掉 tracing_enabled ,我們只需要控制tracing_on 即可打開關(guān)閉追蹤。
#echo 1 > tracing_on
// 打開跟蹤
# echo 0 > tracing_on // 關(guān)閉跟蹤
# echo 1 > tracing_on; run_test; echo 0 > tracing_on // 打開跟蹤后做一件事再關(guān)閉跟蹤,所以此次跟蹤的結(jié)果基本上是這個運行的程序的跟蹤結(jié)果,但是肯定會包括很多雜訊,所以后面會介紹跟蹤某個 pid 的或者跟蹤某個函數(shù)。
注:對于為什么去掉 tracing_enabled 我問過 Ftrace 的維護人Steven
Rostedt,他說使用 tracing_on 可以快速的打開 Ftrace 的追蹤,這讓 tracing_enabled 顯得很輕量級或者說顯得比較冗余,下面可以會說到,我們寫內(nèi)核程序時可以使用Ftrace 提供的內(nèi)核函數(shù) tracing_on() or tracing_off() 直接打開追蹤,這其實就是使用的 tracing_on ,所以在新內(nèi)核中 tracing_enabled
這個看起來比較冗余的選項已經(jīng)被刪除。
2.4. 查看追蹤結(jié)果
ftrace 的輸出信息主要保存在 3 個文件中。
* trace,該文件保存 ftrace 的輸出信息,其內(nèi)容可以直接閱讀。
* latency_trace,保存與 trace 相同的信息,不過組織方式略有不同。主要為了用戶能方便地分析系統(tǒng)中有關(guān)延遲的信息。
* trace_pipe 是一個管道文件,主要為了方便應(yīng)用程序讀取 trace 內(nèi)容。算是擴展接口吧。
所以可以直接查看 trace 追蹤文件,也可以在追蹤之前使用trace_pipe 將追蹤結(jié)果直接導(dǎo)向其他的文件。
比如: # cat trace_pipe > /tmp/log & // 使用trace_pipe 將跟蹤結(jié)果導(dǎo)入
/tmp/log 里,我們可以直接 “ cat /tmp/log” 查看跟蹤信息。
當(dāng)然也可以直接查看trace文件 #cat trace 或者使用
cat trace > /tmp/log 將跟蹤信息導(dǎo)入 /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函數(shù)),此時即可追蹤當(dāng)前這個進程。
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. 設(shè)置追蹤過濾器
將要跟蹤的函數(shù)寫入文件 set_ftrace_filter ,將不希望跟蹤的函數(shù)寫入文件 set_ftrace_notrace。通常直接操作文件 set_ftrace_filter 就可以了.
四、 Ftrace 提供的函數(shù)使用
內(nèi)核頭文件 include/linux/kernel.h 中描述了 ftrace 提供的工具函數(shù)的原型,這些函數(shù)包括 trace_printk、tracing_on/tracing_off 等。
4.1. 使用 trace_printk 打印跟蹤信息
ftrace 提供了一個用于向 ftrace 跟蹤緩沖區(qū)輸出跟蹤信息的工具函數(shù),叫做 trace_printk(),它的使用方式與 printk() 類似。可以通過 trace 文件讀取該函數(shù)的輸出。從頭文件 include/linux/kernel.h 中可以看到,在激活配置 CONFIG_TRACING 后,trace_printk() 定義為宏:
#define trace_printk(fmt, args...) \
...
所以在使用時:(例子是在一個內(nèi)核模塊中添加打印信息)
- #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 控制跟蹤信息的記錄
在跟蹤過程中,有時候在檢測到某些事件發(fā)生時,想要停止跟蹤信息的記錄,這樣,跟蹤緩沖區(qū)中較新的數(shù)據(jù)是與該事件有關(guān)的。在用戶態(tài),可以通過向文件 tracing_on 寫入 0 來停止記錄跟蹤信息,寫入 1 會繼續(xù)記錄跟蹤信息。而在內(nèi)核代碼中,可以通過函數(shù) tracing_on() 和 tracing_off() 來做到這一點,它們的行為類似于對 /sys/kernel/debug/tracing
下的文件 tracing_on 分別執(zhí)行寫 1 和 寫 0 的操作。
使用這兩個函數(shù),會對跟蹤信息的記錄控制地更準(zhǔn)確一些,這是因為在用戶態(tài)寫文件 tracing_on 到實際暫停跟蹤,中間由于上下文切換、系統(tǒng)調(diào)度控制等可能已經(jīng)經(jīng)過較長的時間,這樣會積累大量的跟蹤信息,而感興趣的那部分可能會被覆蓋掉了。
實際代碼中,可以通過特定條件(比如檢測到某種異常狀況,等等)來控制跟蹤信息的記錄,函數(shù)的使用方式類似如下的形式:
if (condition)
tracing_on() or tracing_off()
跟蹤模塊運行狀況時,使用 ftrace 命令操作序列在用戶態(tài)進行必要的設(shè)置,而在代碼中則可以通過 traceing_on() 控制在進入特定代碼區(qū)域時開啟跟蹤信息,并在遇到某些條件時通過 tracing_off() 暫停;讀者可以在查看完感興趣的信息后,將 1 寫入 tracing_on 文件以繼續(xù)記錄跟蹤信息。實踐中,可以通過宏來控制是否將對這些函數(shù)的調(diào)用編譯進內(nèi)核模塊,這樣可以在調(diào)試時將其開啟,在最終發(fā)布時將其關(guān)閉。
用戶態(tài)的應(yīng)用程序可以通過直接讀寫文件 tracing_on 來控制記錄跟蹤信息的暫停狀態(tài),以便了解應(yīng)用程序運行期間內(nèi)核中發(fā)生的活動。
五、 簡單的 Ftrace 腳本案例:
我在下面使用一個腳本執(zhí)行 Ftrace 的操作,自動追蹤 HelloWorld 程序執(zhí)行:
- #!/bin/bash
- debugfs_path=/debug
- ftrace_call_path=$debugfs_path/tracing
- app_path=/home/dslab/test
-
- # 測試我們的 /debug 路徑是否已經(jīng)建立
- 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是否已經(jīng)掛載
- 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 內(nèi)核上還有tracing_enabled ,先給它賦值 1
- echo 1 > $ftrace_call_path/tracing_enabled
- # 使用 trace_pipe 將追蹤結(jié)果重定向到/tmp/result(后臺運行,使得下面的步驟得以進行)
- cat $ftrace_call_path/trace_pipe > /tmp/result &
- # 打開追蹤
- echo 1 > $ftrace_call_path/tracing_on
- # 我在這里執(zhí)行了一個已經(jīng)編譯好helloworld 程序
- exec $app_path/hello
- # 關(guān)閉追蹤
- 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內(nèi)核中的ftrace 文檔,路徑為:linux-source-3.2/Documentation/trace/ftrace.txt (還有個ftrace-design.txt也很值得學(xué)習(xí))
【2】ftrace 簡介 : http://www.ibm.com/developerworks/cn/linux/l-cn-ftrace/
【3】使用 ftrace 調(diào)試 Linux 內(nèi)核:
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/
|