一文教你使用trace查看函數(shù)調(diào)用關(guān)系|分析Linux性能
抓取函數(shù)調(diào)用流程關(guān)系
抓取函數(shù)耗時
抓取代碼片耗時
抓取函數(shù)里每個子函數(shù)時間戳
抓取事件信息
trace是內(nèi)核自帶的工具,相比于perf工具,trace只管抓trace數(shù)據(jù)并沒有分析,perf在trace數(shù)據(jù)分析方面做出了很多成果。 但是我們現(xiàn)在就想看一下底層多調(diào)用關(guān)系,所以使用trace抓一下數(shù)據(jù)是非常有必要的,還可以分析一下驅(qū)動性能。
因為trace工具是內(nèi)核自帶的,所以我們配置一下內(nèi)核就可以使用了:

trace 通過 debugfs 向用戶態(tài)提供了訪問接口,所以還需要將 debugfs 編譯進內(nèi)核。激活對 debugfs 的支持,在 make menuconfig 時到 Kernel hacking 菜單下選中對 debugfs 文件系統(tǒng)的支持:

系統(tǒng)啟動后,進入文件系統(tǒng),執(zhí)行:
掛載debugfs后即可使用trace。
tracing 目錄下文件和目錄比較多,有些是各種跟蹤器共享使用的,有些是特定于某個跟蹤器使用的。在操作這些數(shù)據(jù)文件時,使用 echo 命令來修改其值,也可以在程序中通過文件讀寫相關(guān)的函數(shù)來操作這些文件的值。
README文件提供了一個簡短的使用說明,展示了 ftrace 的操作命令序列??梢酝ㄟ^ cat 命令查看該文件以了解概要的操作流程。
current_tracer用于設(shè)置或顯示當前使用的跟蹤器;使用 echo 將跟蹤器名字寫入該文件可以切換到不同的跟蹤器。系統(tǒng)啟動后,其缺省值為 nop ,即不做任何跟蹤操作。在執(zhí)行完一段跟蹤任務(wù)后,可以通過向該文件寫入 nop 來重置跟蹤器。
available_tracers記錄了當前編譯進內(nèi)核的跟蹤器的列表,可以通過 cat 查看其內(nèi)容;寫 current_tracer 文件時用到的跟蹤器名字必須在該文件列出的跟蹤器名字列表中。
trace文件提供了查看獲取到的跟蹤信息的接口??梢酝ㄟ^ cat 等命令查看該文件以查看跟蹤到的內(nèi)核活動記錄,也可以將其內(nèi)容保存為記錄文件以備后續(xù)查看。
set_graph_function設(shè)置要清晰顯示調(diào)用關(guān)系的函數(shù),顯示的信息結(jié)構(gòu)類似于 C 語言代碼,這樣在分析內(nèi)核運作流程時會更加直觀一些。在使用 function_graph 跟蹤器時使用;缺省為對所有函數(shù)都生成調(diào)用關(guān)系序列,可以通過寫該文件來指定需要特別關(guān)注的函數(shù)。
buffer_size_kb用于設(shè)置單個 CPU 所使用的跟蹤緩存的大小。跟蹤器會將跟蹤到的信息寫入緩存,每個 CPU 的跟蹤緩存是一樣大的。跟蹤緩存實現(xiàn)為環(huán)形緩沖區(qū)的形式,如果跟蹤到的信息太多,則舊的信息會被新的跟蹤信息覆蓋掉。注意,要更改該文件的值需要先將 current_tracer 設(shè)置為 nop 才可以。
tracing_on用于控制跟蹤的暫停。有時候在觀察到某些事件時想暫時關(guān)閉跟蹤,可以將 0 寫入該文件以停止跟蹤,這樣跟蹤緩沖區(qū)中比較新的部分是與所關(guān)注的事件相關(guān)的;寫入 1 可以繼續(xù)跟蹤。
available_filter_functions記錄了當前可以跟蹤的內(nèi)核函數(shù)。對于不在該文件中列出的函數(shù),無法跟蹤其活動。
set_ftrace_filter和 set_ftrace_notrace在編譯內(nèi)核時配置了動態(tài) ftrace (選中CONFIG_DYNAMIC_FTRACE 選項)后使用。前者用于顯示指定要跟蹤的函數(shù),后者則作用相反,用于指定不跟蹤的函數(shù)。如果一個函數(shù)名同時出現(xiàn)在這兩個文件中,則這個函數(shù)的執(zhí)行狀況不會被跟蹤。這些文件還支持簡單形式的含有通配符的表達式,這樣可以用一個表達式一次指定多個目標函數(shù);注意,要寫入這兩個文件的函數(shù)名必須可以在文件 available_filter_functions 中看到。缺省為可以跟蹤所有內(nèi)核函數(shù),文件 set_ftrace_notrace 的值則為空。
available_events 當前編譯進內(nèi)核的可以監(jiān)控的事件。
set_event 跟蹤的事件類型,名字必須在available_events列出的跟蹤器名字列表中。
trace 當前包含多個跟蹤器,用于跟蹤不同類型的信息,比如進程調(diào)度、中斷關(guān)閉等。可以查看文件 available_tracers 獲取內(nèi)核當前支持的跟蹤器列表。在編譯內(nèi)核時,也可以看到內(nèi)核支持的跟蹤器對應的選項。
nop跟蹤器不會跟蹤任何內(nèi)核活動,將 nop 寫入 current_tracer 文件可以刪除之前所使用的跟蹤器,并清空之前收集到的跟蹤信息,即刷新 trace 文件。
function跟蹤器可以跟蹤內(nèi)核函數(shù)的執(zhí)行情況;可以通過文件 set_ftrace_filter 顯示指定要跟蹤的函數(shù)。function_graph跟蹤器可以顯示類似 C 源碼的函數(shù)調(diào)用關(guān)系圖,這樣查看起來比較直觀一些;可以通過文件
set_grapch_function 顯示指定要生成調(diào)用流程圖的函數(shù)。 sched_switch跟蹤器可以對內(nèi)核中的進程調(diào)度活動進行跟蹤。
irqsoff跟蹤器和preemptoff跟蹤器分別跟蹤關(guān)閉中斷的代碼和禁止進程搶占的代碼,并記錄關(guān)閉的最大時長,preemptirqsoff跟蹤器則可以看做它們的組合。
那具體怎么用呢?
【文章福利】小編推薦自己的Linux內(nèi)核技術(shù)交流群:【749907784】整理了一些個人覺得比較好的學習書籍、視頻資料共享在群文件里面,有需要的可以自行添加哦?。。。ê曨l教程、電子書、實戰(zhàn)項目及代碼)?


比如我們抓一次spi輸出傳輸,驅(qū)動用的是kernel自帶的spidev驅(qū)動:
spidev_ioctrl 被echo到set_grapch_function 屬性里面,就可以查看整個spidev_ioctrl 函數(shù)的調(diào)用流程,但是實際上我們執(zhí)行的時候會發(fā)現(xiàn)一個事情,抓取來的數(shù)據(jù)太多了,許多無關(guān)的、我們不太關(guān)心的函數(shù)調(diào)用關(guān)系也被抓進去了,導致抓出來的數(shù)據(jù)非常亂!
所以最好是利用set_ftrace_filter進行一些過濾。
所以針對這種情況,我們應該這樣設(shè)置:
在spi傳輸里,我們主要關(guān)系spi的函數(shù)和dma的函數(shù),所以大概抓一下這些字眼即可,還可以按需自己添加過濾語句。
ps:因為spin_lock語句也帶了spi字眼,也會被誤抓進來,所以最后也把他剔除掉。
最后得出的數(shù)據(jù)就正常多了:
以上僅列舉部分信息,有興趣的小伙伴可以自行嘗試。
抓取函數(shù)耗時
有時候,也許我們也要通過程序的耗時來分析程序的性能,可以這么做:
同樣也是以spidev驅(qū)動為例:
這樣可以非常直觀的看到spidev_ioctrl 執(zhí)行的耗時。
其中, 帶左側(cè)時間顯示一塊:
延遲比較大的部分,會有+、#等特殊標號:

抓取代碼片耗時
有時,我們需要抓取一大片多個函數(shù)耗時??梢赃@么做:
以i2c傳輸為例,抓取twi_set_start到sunxi_i2c_handler函數(shù)的時間:
這樣我們即可抓取Linux里面執(zhí)行i2cget的時候去讀取0x50地址器件時i2c傳輸 twi_set_start 函數(shù)到 sunxi_i2c_handler 函數(shù)的耗時,即兩個時間戳相減:
可以看得出耗時為24us。
當然,我們也可以使用do_gettimeofday函數(shù)來統(tǒng)計耗時,不過比較麻煩,需要在檢測的地方手動添加:
timeval 結(jié)構(gòu)體定義:
甚至也可以用ktime_to_us來實現(xiàn):
方法是多種多樣的。
抓取函數(shù)里每個子函數(shù)時間戳
在第一點里面,抓取函數(shù)調(diào)用流程里面只能看到每個子函數(shù)的耗時,不能看到時間戳,那么如何能看到每個子函數(shù)的時間戳呢?和第一點的手段類似:
區(qū)別操作就是添加了:echo funcgraph-tail > trace_options
主要是在函數(shù)結(jié)束顯示函數(shù)名。這樣方便使用grep找出函數(shù)的執(zhí)行時間,默認disable:
hide: echo nofuncgraph-tail > trace_options
show: echo funcgraph-tail > trace_options
最后結(jié)果非常直觀:
graph_ent代表函數(shù)開始執(zhí)行,graph_ret代表函數(shù)結(jié)束。
抓取事件信息
有時候我們需要根據(jù)事件來抓取信息,就可以使用set_event了??梢?cat available_events 查看獲得支持的跟蹤event列表,這個支持的比較多,配置相對簡單,只需向set_event寫相應值即可。
比如我們要查看spi的事件,可以先看下available_event 里有無spi事件:
可以看出,我們這里支持七個spi事件,其對應的頭文件在:Linux-4.9/include/trace/events/spi.h文件。感興趣的可以查看里面的內(nèi)容實現(xiàn)。這里我們把所有的spi事件都打印出來:
即可看到我們需要跟蹤的spi事件:
原文作者:人人極客社區(qū)
