[Android][kernel][Linux]使用ftrace抓取IO相關信息

[Android][kernel][Linux]使用ftrace抓取IO相關信息

ftrace介紹

官方介紹可以在kernel/Documentation/trace/ftrace.txt中查看,如下是截取片段後的翻譯結果:

ftrace (function trace) 是旨在幫助開發者探究內核內部運行邏輯的一個追蹤器。可用於調試或分析內核裏面的高延遲性能問題;

雖然ftrace從命名來看是針對函數的追蹤器,但是實際上它已經成爲了一個整合了多種追蹤器的工具框架。其不僅可以顯示中斷開關的耗時,也可以追蹤到任務從喚醒到加入執行隊列之間由於資源搶佔導致的時間差;

ftrace中一個最常用的使用方式是事件追蹤(event tracing)。其可以通過debugfs文件系統啓用內核中數百個靜態事件埋點,來跟蹤內核中某一特定模塊的運行情況;

ftrace常用概念

tracer

tracer - 顧名思義,追蹤器,不同追蹤器各司其職,用在不同的場景。以Android kernel 3.18爲例,目前調試的手機有如下三種追蹤器可用:

xxx:/ # cat /sys/kernel/debug/tracing/available_tracers                                                                                                                                                           
function_graph function nop

根據官方介紹,如下是翻譯後的解釋:

  1. function - 記錄kernel內部所有函數調用

  2. function_graph - 與function類似,但是function僅僅記錄函數入口,而function_graph會記錄入口與出口,從而可以生成一份類似C語言源代碼格式的圖表;

  3. nop - 'trace nothing’跟蹤器,顧名思義,當current_tracer節點爲nop時,不會啓用任何追蹤器;

要切換追蹤器很簡單,只需要將如上可用的追蹤器名字寫入current_tracer節點即可:

echo function_graph > /sys/kernel/debug/tracing/current_tracer

event

event - 事件,用於過濾需要記錄的事件,只有設置在set_event節點中的事件纔會被抓取;默認該節點內容爲空;

xxx:/ # cat /sys/kernel/debug/tracing/set_event                                                                                                                                                                   
xxx:/ # 

至於可用的event標籤,則可以通過/sys/kernel/debug/tracing/available_events節點查看,由於太多,此處就不一一列舉;

另外還有一個方式可以查看支持的event標籤,那就是/sys/kernel/debug/tracing/events/目錄下的子目錄:

xxx:/ # ls /sys/kernel/debug/tracing/events/                                                                                                                                                                      
almk       cma                 enable    filelock     i2c   jbd2            mmc           msm_vidc            power           rcu        rpm_smd     skb     thermal wil6210   
android_fs compaction          exception filemap      iommu kgsl            module        napi                printk          regmap     sched       sock    timer   workqueue 
asoc       cpufreq_interactive ext3      ftrace       ipa   kmem            msm_bus       net                 process_reclaim regulator  scm         spi     udp     writeback 
binder     devfreq             ext4      gpio         ipi   lowmemorykiller msm_cam       oom                 random          rmnet_data scsi        swiotlb ufs     xhci-hcd  
block      dwc3                f2fs      header_event irq   mdss            msm_core      pagemap             ras             rndis_ipa  sde_rotator sync    v4l2    
cfg80211   emulation           fence     header_page  jbd   migrate         msm_low_power perf_trace_counters raw_syscalls    rpm        signal      task    vmscan

因此,啓用某個event也有兩種方式:

  1. 使用echo 1 > /sys/kernel/debug/tracing/events/<$event>/enable來啓用某類標籤;
  2. 使用echo <$event> > /sys/kernel/debug/tracing/set_event來啓用某類標籤;

兩者是等效的,並且可以在另一個方式所示的節點中通過cat來確認狀態;

xxx:/ # cat /sys/kernel/debug/tracing/events/android_fs/enable                                                                                                                                                
0
xxx:/ # echo android_fs >  /sys/kernel/debug/tracing/set_event                                                                                                                                                    
xxx:/ # cat /sys/kernel/debug/tracing/events/android_fs/enable                                                                                                                                                    
1

filter

filter - 過濾器,用於過濾函數,避免抓取的trace無效信息過多;
可用的filter很多,可從節點/sys/kernel/debug/tracing/available_filter_functions中查看;

cat /sys/kernel/debug/tracing/available_filter_functions

默認不進行過濾,若需要過濾函數,則將對應的函數名寫入/sys/kernel/debug/tracing/set_ftrace_filter,例如:

echo vfs_write > /sys/kernel/debug/tracing/set_ftrace_filter

抓取

ftrace一般抓取信息量很大,因此建議增大buffer以確保信息完整:

echo 32768 > /sys/kernel/debug/tracing/buffer_size_kb

抓取前後建議清理cache,以保證結果不受緩存影響:

echo 3 > /proc/sys/vm/drop_caches

如果不想保留之前抓取的trace,則可以將之前的記錄清空:

echo "" >  /sys/kernel/debug/tracing/trace

準備就緒後,就可以復現問題並抓取了:

echo 1 > /sys/kernel/debug/tracing/tracing_on

抓取時儘量保證操作單一,操作不要太複雜,否則trace可閱讀性會比較差;
復現完畢後,通過如下指令關閉ftrace錄製:

echo 0 > /sys/kernel/debug/tracing/tracing_on

最後,將錄製的trace文件拉出並使用文本編輯器打開閱讀:

adb pull /sys/kernel/debug/tracing/trace ~/Desktop/trace.txt

trace文件一般比較大,建議使用比較省內存的文本編輯器打開(圖形化工具推薦sublime text);

示例

比如,如果遇到了文件寫入速度慢的問題,想確認一下到底是哪一階段出現了問題,那麼可以通過如下步驟抓取:

echo 'mmc android_fs block sched_switch sched_wakeup sched_blocked_reason *****'  > /sys/kernel/debug/tracing/set_event
echo 'function_graph' > /sys/kernel/debug/tracing/current_tracer
echo 32768 > /sys/kernel/debug/tracing/buffer_size_kb
echo vfs_write > /sys/kernel/debug/tracing/set_ftrace_filter
echo "" >  /sys/kernel/debug/tracing/trace
echo 3 > /proc/sys/vm/drop_caches
echo 1 > /sys/kernel/debug/tracing/tracing_on
dd if=/dev/zero of=/sdcard/test count=1
echo 0 > /sys/kernel/debug/tracing/tracing_on

注意:抓取會導致讀寫性能明顯下降,因此時間的絕對值不能作爲參考,只能通過對比相對大小來定位問題;

此處由於我只抓取了vfs層的寫入,因此trace文件很小,並且很容易定位到對應位置:

 ------------------------------------------

 4)               |  vfs_write() {
 4)               |    vfs_write() {
 4)               |      /* android_fs_datawrite_start: entry_name /media/0/test, offset 0, bytes 512, cmdline dd, pid 3024, i_size 0, ino 8751 */
 4)               |      /* android_fs_datawrite_end: ino 8751, offset 0, bytes 512 */
 4) ! 124.479 us  |    }
 4) ! 138.073 us  |  }
 4)               |  vfs_write() {
 4)               |  /* sched_wakeup: comm=kworker/4:4 pid=2944 prio=120 success=1 target_cpu=004 */
 4)               |  /* sched_switch: prev_comm=dd prev_pid=3024 prev_prio=120 prev_state=R+ ==> next_comm=kworker/4:4 next_pid=2944 next_prio=120 */
 ------------------------------------------
 4)    dd-3024     =>  kworker-2944 
 ------------------------------------------

後記

最近由於工作需要,在研究IO方面的問題,因此需要深度研究IO的流程,用ftrace抓取函數調用可以做到事半功倍,比一句一句跟代碼,或者一句一句打log要高效得多;
更多用法,還請自己多多發掘;

文筆有限,若有紕漏,還請多多指教;

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