1. 程式人生 > >使用 ftrace 除錯 Linux 核心

使用 ftrace 除錯 Linux 核心

簡介: ftrace 是 Linux 核心中提供的一種除錯工具。使用 ftrace 可以對核心中發生的事情進行跟蹤,這在除錯 bug 或者分析核心時非常有用。本系列文章對 ftrace 進行了介紹,分為三部分。本文是第一部分,介紹了核心相關的編譯選項、使用者態訪問 ftrace 的介面、ftrace 的資料檔案,並對 ftrace 提供的跟蹤器的用途進行了介紹,以使讀者更好的瞭解和使用該工具。

ftrace 是內建於 Linux 核心的跟蹤工具,從 2.6.27 開始加入主流核心。使用 ftrace 可以除錯或者分析核心中發生的事情。ftrace 提供了不同的跟蹤器,以用於不同的場合,比如跟蹤核心函式呼叫、對上下文切換進行跟蹤、檢視中斷被關閉的時長、跟蹤核心態中的延遲以及效能問題等。系統開發人員可以使用 ftrace 對核心進行跟蹤除錯,以找到核心中出現的問題的根源,方便對其進行修復。另外,對核心感興趣的讀者還可以通過 ftrace 來觀察核心中發生的活動,瞭解核心的工作機制。

使用 ftrace ,首先要將其編譯進核心。核心原始碼目錄下的 kernel/trace/Makefile 檔案給出了 ftrace 相關的編譯選項。


清單 1. ftrace 相關的配置選項列表
				
 CONFIG_FUNCTION_TRACER 
 CONFIG_FUNCTION_GRAPH_TRACER 
 CONFIG_CONTEXT_SWITCH_TRACER 
 CONFIG_NOP_TRACER 
 CONFIG_SCHED_TRACER 
 ... 

ftrace 相關的配置選項比較多,針對不同的跟蹤器有各自對應的配置選項。不同的選項有不同的依賴關係,核心原始碼目錄下的 kernel/trace/Kconfig 檔案描述了這些依賴關係。讀者可以參考 Makefile 檔案和 Konfig 檔案,然後選中自己所需要的跟蹤器。

通常在配置核心時,使用 make menuconfig 會更直觀一些。以 2.6.33.1 版本的核心為例,要將 ftrace 編譯進核心,可以選中 Kernel hacking (圖 1 )下的 Tracers 選單項(圖 2 )。


圖 1. Kernel hacking
圖 1. Kernel hacking

圖 2. Tracers
圖 2. Tracers

進入 Tracers 選單下,可以看到核心支援的跟蹤器列表。如圖 3 所示,這裡選中了所有的跟蹤器,讀者可以根據自己的需要選中特定的跟蹤器。


圖 3. 核心支援的跟蹤器列表
圖 3. 核心支援的跟蹤器列表

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

        depends on !X86_32 || !CC_OPTIMIZE_FOR_SIZE 


圖 4. General setup
圖 4. General setup

圖 5. Optimize for size
圖 5. Optimize for size

ftrace 通過 debugfs 向用戶態提供了訪問介面,所以還需要將 debugfs 編譯進核心。啟用對 debugfs 的支援,可以直接編輯核心配置檔案 .config ,設定 CONFIG_DEBUG_FS=y ;或者在 make menuconfig 時到 Kernel hacking 選單下選中對 debugfs 檔案系統的支援,如圖 6 所示。


圖 6. debugfs 編譯選項
圖 6. debugfs 編譯選項

配置完成後,編譯安裝新核心,然後啟動到新核心。 注意,啟用 ftrace 支援後,編譯核心時會使用編譯器的 -pg 選項,這是在 kernel/trace/Makefile 檔案中定義的,如清單 2 所示。


清單 2. 啟用編譯選項 -pg
				
 ifdef CONFIG_FUNCTION_TRACER 
 ORIG_CFLAGS := $(KBUILD_CFLAGS) 
 KBUILD_CFLAGS = $(subst -pg,,$(ORIG_CFLAGS)) 
 ... 
 endif 
 ... 

使用 -pg 選項會在編譯得到的核心映像中加入大量的除錯資訊。一般情況下,只是在開發測試階段啟用 ftrace 支援,以除錯核心,修復 bug 。最終用於發行版的核心則會關閉 -pg 選項,也就無法使用 ftrace。

ftrace 通過 debugfs 向用戶態提供訪問介面。配置核心時啟用 debugfs 後會建立目錄 /sys/kernel/debug ,debugfs 檔案系統就是掛載到該目錄。要掛載該目錄,需要將如下內容新增到 /etc/fstab 檔案:

        debugfs  /sys/kernel/debug  debugfs  defaults  0  0 

或者可以在執行時掛載:

        mount  -t  debugfs  nodev  /sys/kernel/debug 

啟用核心對 ftrace 的支援後會在 debugfs 下建立一個 tracing 目錄 /sys/kernel/debug/tracing 。該目錄下包含了 ftrace 的控制和輸出檔案,如圖 7 所示。根據編譯核心時針對 ftrace 的設定不同,該目錄下實際顯示的檔案和目錄與這裡也會不同。


圖 7. tracing 目錄下的檔案
圖 7. tracing 目錄下的檔案

/sys/kernel/debug/trace 目錄下檔案和目錄比較多,有些是各種跟蹤器共享使用的,有些是特定於某個跟蹤器使用的。在操作這些資料檔案時,通常使用 echo 命令來修改其值,也可以在程式中通過檔案讀寫相關的函式來操作這些檔案的值。下面只對部分檔案進行描述,讀者可以參考核心原始碼包中 Documentation/trace 目錄下的文件以及 kernel/trace 下的原始檔以瞭解其餘檔案的用途。

  • README檔案提供了一個簡短的使用說明,展示了 ftrace 的操作命令序列。可以通過 cat 命令檢視該檔案以瞭解概要的操作流程。
  • current_tracer用於設定或顯示當前使用的跟蹤器;使用 echo 將跟蹤器名字寫入該檔案可以切換到不同的跟蹤器。系統啟動後,其預設值為 nop ,即不做任何跟蹤操作。在執行完一段跟蹤任務後,可以通過向該檔案寫入 nop 來重置跟蹤器。
  • available_tracers記錄了當前編譯進核心的跟蹤器的列表,可以通過 cat 檢視其內容;其包含的跟蹤器與圖 3 中所啟用的選項是對應的。寫 current_tracer 檔案時用到的跟蹤器名字必須在該檔案列出的跟蹤器名字列表中。
  • trace檔案提供了檢視獲取到的跟蹤資訊的介面。可以通過 cat 等命令檢視該檔案以檢視跟蹤到的核心活動記錄,也可以將其內容儲存為記錄檔案以備後續檢視。
  • tracing_enabled用於控制 current_tracer 中的跟蹤器是否可以跟蹤核心函式的呼叫情況。寫入 0 會關閉跟蹤活動,寫入 1 則啟用跟蹤功能;其預設值為 1 。
  • set_graph_function設定要清晰顯示呼叫關係的函式,顯示的資訊結構類似於 C 語言程式碼,這樣在分析核心運作流程時會更加直觀一些。在使用 function_graph 跟蹤器時使用;預設為對所有函式都生成呼叫關係序列,可以通過寫該檔案來指定需要特別關注的函式。
  • buffer_size_kb用於設定單個 CPU 所使用的跟蹤快取的大小。跟蹤器會將跟蹤到的資訊寫入快取,每個 CPU 的跟蹤快取是一樣大的。跟蹤快取實現為環形緩衝區的形式,如果跟蹤到的資訊太多,則舊的資訊會被新的跟蹤資訊覆蓋掉。注意,要更改該檔案的值需要先將 current_tracer 設定為 nop 才可以。
  • tracing_on用於控制跟蹤的暫停。有時候在觀察到某些事件時想暫時關閉跟蹤,可以將 0 寫入該檔案以停止跟蹤,這樣跟蹤緩衝區中比較新的部分是與所關注的事件相關的;寫入 1 可以繼續跟蹤。
  • available_filter_functions記錄了當前可以跟蹤的核心函式。對於不在該檔案中列出的函式,無法跟蹤其活動。
  • set_ftrace_filterset_ftrace_notrace在編譯核心時配置了動態 ftrace (選中 CONFIG_DYNAMIC_FTRACE 選項)後使用。前者用於顯示指定要跟蹤的函式,後者則作用相反,用於指定不跟蹤的函式。如果一個函式名同時出現在這兩個檔案中,則這個函式的執行狀況不會被跟蹤。這些檔案還支援簡單形式的含有萬用字元的表示式,這樣可以用一個表示式一次指定多個目標函式;具體使用在後續文章中會有描述。注意,要寫入這兩個檔案的函式名必須可以在檔案 available_filter_functions 中看到。預設為可以跟蹤所有核心函式,檔案 set_ftrace_notrace 的值則為空。

ftrace 當前包含多個跟蹤器,用於跟蹤不同型別的資訊,比如程序排程、中斷關閉等。可以檢視檔案 available_tracers 獲取核心當前支援的跟蹤器列表。在編譯核心時,也可以看到核心支援的跟蹤器對應的選項,如之前圖 3 所示。

  • nop跟蹤器不會跟蹤任何核心活動,將 nop 寫入 current_tracer 檔案可以刪除之前所使用的跟蹤器,並清空之前收集到的跟蹤資訊,即重新整理 trace 檔案。
  • function跟蹤器可以跟蹤核心函式的執行情況;可以通過檔案 set_ftrace_filter 顯示指定要跟蹤的函式。
  • function_graph跟蹤器可以顯示類似 C 原始碼的函式呼叫關係圖,這樣檢視起來比較直觀一些;可以通過檔案 set_grapch_function 顯示指定要生成呼叫流程圖的函式。
  • sched_switch跟蹤器可以對核心中的程序排程活動進行跟蹤。
  • irqsoff跟蹤器和 preemptoff跟蹤器分別跟蹤關閉中斷的程式碼和禁止程序搶佔的程式碼,並記錄關閉的最大時長,preemptirqsoff跟蹤器則可以看做它們的組合。

ftrace 還支援其它一些跟蹤器,比如 initcall、ksym_tracer、mmiotrace、sysprof 等。ftrace 框架支援擴充套件新增新的跟蹤器。讀者可以參考核心原始碼包中 Documentation/trace 目錄下的文件以及 kernel/trace 下的原始檔,以瞭解其它跟蹤器的用途和如何新增新的跟蹤器。

小結

本系列文章對 ftrace 的配置和使用進行了介紹。本文是其中的第一部分,介紹了 ftrace 的編譯配置、使用者態訪問 ftrace 的介面和 ftrace 的資料檔案,並對 ftrace 所提供的部分跟蹤器的用途進行了描述。由於篇幅的限制,本文沒有對 ftrace 的具體使用和如何在程式碼中與 ftrace 進行互動進行描述,這些內容將分別在本系列文章的後續篇章中給出。

簡介: ftrace 是 Linux 核心中提供的一種除錯工具。使用 ftrace 可以對核心中發生的事情進行跟蹤,這在除錯 bug 或者分析核心時非常有用。本系列文章對 ftrace 進行了介紹,分為三部分。本文是第二部分,通過示例介紹瞭如何使用 ftrace 中的跟蹤器,並對不同格式的跟蹤資訊進行了描述,同時還對如何使用 ftrace 除錯指定核心模組進行了介紹。通過對本文的學習,讀者可以瞭解如何在實踐中使用 ftrace 。

使用 ftrace 提供的跟蹤器來除錯或者分析核心時需要如下操作:

  • 切換到目錄 /sys/kernel/debug/tracing/ 下
  • 檢視 available_tracers 檔案,獲取當前核心支援的跟蹤器列表
  • 關閉 ftrace 跟蹤,即將 0 寫入檔案 tracing_enabled
  • 啟用 ftrace_enabled ,否則 function 跟蹤器的行為類似於 nop;另外,啟用該選項還可以讓一些跟蹤器比如 irqsoff 獲取更豐富的資訊。建議使用 ftrace 時將其啟用。要啟用 ftrace_enabled ,可以通過 proc 檔案系統介面來設定:
    echo 1 > /proc/sys/kernel/ftrace_enabled 

  • 將所選擇的跟蹤器的名字寫入檔案 current_tracer
  • 將要跟蹤的函式寫入檔案 set_ftrace_filter ,將不希望跟蹤的函式寫入檔案 set_ftrace_notrace。通常直接操作檔案 set_ftrace_filter 就可以了
  • 啟用 ftrace 跟蹤,即將 1 寫入檔案 tracing_enabled。還要確保檔案 tracing_on 的值也為 1,該檔案可以控制跟蹤的暫停
  • 如果是對應用程式進行分析的話,啟動應用程式的執行,ftrace 會跟蹤應用程式執行期間核心的運作情況
  • 通過將 0 寫入檔案 tracing_on 來暫停跟蹤資訊的記錄,此時跟蹤器還在跟蹤核心的執行,只是不再向檔案 trace 中寫入跟蹤資訊;或者將 0 寫入檔案 tracing_enabled 來關閉跟蹤
  • 檢視檔案 trace 獲取跟蹤資訊,對核心的執行進行分析除錯

接下來將對跟蹤器的使用以及跟蹤資訊的格式通過例項加以講解。

function 跟蹤器可以跟蹤核心函式的呼叫情況,可用於除錯或者分析 bug ,還可用於瞭解和觀察 Linux 核心的執行過程。清單 1 給出了使用 function 跟蹤器的示例。


清單 1. function 跟蹤器使用示例
[cpp] view plaincopyprint?
  1. <PRE class=cpp name="code">               
  2.  [[email protected] tracing]# pwd   
  3.  /sys/kernel/debug/tracing   
  4.  [[email protected] tracing]# echo 0 > tracing_enabled   
  5.  [[email protected] tracing]# echo 1 > /proc/sys/kernel/ftrace_enabled   
  6.  [[email protected] tracing]# echo function > current_tracer   
  7.  [[email protected] tracing]# echo 1 > tracing_on   
  8.  [[email protected] tracing]# echo 1 > tracing_enabled   
  9.  # 讓核心執行一段時間,這樣 ftrace 可以收集一些跟蹤資訊,之後再停止跟蹤
  10.  [[email protected] tracing]# echo 0 > tracing_enabled   
  11.  [[email protected] tracing]# cat trace | head -10   
  12.  # tracer: function 
  13.  # 
  14.  #         TASK-PID    CPU#    TIMESTAMP  FUNCTION 
  15.  #            | |       |          |         | 
  16.           <idle>-0     [000] 20654.426521: _raw_spin_lock <-scheduler_tick   
  17.           <idle>-0     [000] 20654.426522: task_tick_idle <-scheduler_tick   
  18.           <idle>-0     [000] 20654.426522: cpumask_weight <-scheduler_tick   
  19.           <idle>-0     [000] 20654.426523: cpumask_weight <-scheduler_tick   
  20.           <idle>-0     [000] 20654.426523: run_posix_cpu_timers <-update_process_times   
  21.  <idle>-0 [000] 20654.426524: hrtimer_forward <-tick_sched_timer   
  22. </PRE><BR>  
  23. <BR>  
  24. <PRE></PRE>  
  25. <P>trace 檔案給出的資訊格式很清晰。首先,欄位“tracer:”給出了當前所使用的跟蹤器的名字,這裡為 function 跟蹤器。然後是跟蹤資訊記錄的格式,TASK 欄位對應任務的名字,PID 欄位則給出了任務的程序 ID,欄位 CPU# 表示執行被跟蹤函式的 CPU 號,這裡可以看到 idle 程序執行在 0 號 CPU 上,其程序 ID 是 0 ;欄位 TIMESTAMP 是時間戳,其格式為“<secs>.<usecs><CODE>”</CODE>,表示執行該函式時對應的時間戳;FUNCTION  
  26.  一列則給出了被跟蹤的函式,函式的呼叫者通過符號 “<-” 標明,這樣可以觀察到函式的呼叫關係。</P>  
  27. <DIV class=ibm-alternate-rule>  
  28. <HR>  
  29. </DIV>  
  30. <P class="ibm-ind-link ibm-back-to-top"></P>  
  31. <P><A name=major3><SPAN class=atitle>function_graph 跟蹤器</SPAN></A></P>  
  32. <P>在 function 跟蹤器給出的資訊中,可以通過 FUNCTION 列中的符號 “<-” 來檢視函式呼叫關係,但是由於中間會混合不同函式的呼叫,導致看起來非常混亂,十分不方便。function_graph 跟蹤器則可以提供類似 C 程式碼的函式呼叫關係資訊。通過寫檔案 set_graph_function 可以顯示指定要生成呼叫關係的函式,預設會對所有可跟蹤的核心函式生成函式呼叫關係圖。清單 2 給出了使用 function_grapch 跟蹤器的示例,示例中將核心函式 __do_fault 作為觀察物件,該函式在核心運作過程中會被頻繁呼叫。</P>  
  33. <A name=listing2><STRONG>清單 2. function_graph 跟蹤器使用示例</STRONG></A><SPAN style="FONT-SIZE: 16px"></SPAN>  
  34. <P>在檔案 trace 的輸出資訊中,首先給出的也是當前跟蹤器的名字,這裡是 function_graph 。接下來是詳細的跟蹤資訊,可以看到,函式的呼叫關係以類似 C 程式碼的形式組織。</P>  
  35. <P>CPU 欄位給出了執行函式的 CPU 號,本例中都為 1 號 CPU。DURATION 欄位給出了函式執行的時間長度,以 us 為單位。FUNCTION CALLS 則給出了呼叫的函式,並顯示了呼叫流程。注意,對於不呼叫其它函式的函式,其對應行以“;”結尾,而且對應的 DURATION 欄位給出其執行時長;對於呼叫其它函式的函式,則在其“}”對應行給出了執行時長,該時間是一個累加值,包括了其內部呼叫的函式的執行時長。DURATION 欄位給出的時長並不是精確的,它還包含了執行 ftrace 自身的程式碼所耗費的時間,所以示例中將內部函式時長累加得到的結果會與對應的外圍呼叫函式的執行時長並不一致;不過通過該欄位還是可以大致瞭解函式在時間上的執行開銷的。清單  
  36.  2 中最後通過 echo 命令重置了檔案 set_graph_function 。</P>  
  37. <DIV class=ibm-alternate-rule>  
  38. <HR>  
  39. </DIV>  
  40. <P class="ibm-ind-link ibm-back-to-top"></P>  
  41. <P><A name=major4><SPAN class=atitle>sched_switch 跟蹤器</SPAN></A></P>  
  42. <P>sched_switch 跟蹤器可以對程序的排程切換以及之間的喚醒操作進行跟蹤,如清單 3 所示。</P>  
  43. <A name=listing3><STRONG>清單 3. sched_switch 跟蹤器使用示例</STRONG></A><SPAN style="FONT-SIZE: 16px"></SPAN><PRE class=cpp name="code">                  
  44.  [[email protected] tracing]# pwd   
  45.  /sys/kernel/debug/tracing   
  46.  [[email protected] tracing]# echo 0 > tracing_enabled   
  47.  [[email protected] tracing]# echo 1 > /proc/sys/kernel/ftrace_enabled   
  48.  [[email protected] tracing]# echo sched_switch > current_tracer   
  49.  [[email protected] tracing]# echo 1 > tracing_on   
  50.  [[email protected] tracing]# echo 1 > tracing_enabled   
  51.  # 讓核心執行一段時間,這樣 ftrace 可以收集一些跟蹤資訊,之後再停止跟蹤
  52.  [[email protected] tracing]# echo 0 > tracing_enabled   
  53.  [[email protected] tracing]# cat trace | head -10   
  54.  # tracer: sched_switch 
  55.  # 
  56.  #  TASK-PID    CPU#    TIMESTAMP  FUNCTION 
  57.  #     | |       |          |         | 
  58.       bash-1408  [000] 26208.816058:   1408:120:S   + [000]  1408:120:S bash   
  59.       bash-1408  [000] 26208.816070:   1408:120:S   + [000]  1408:120:S bash   
  60.       bash-1408  [000] 26208.816921:   1408:120:R   + [000]     9:120:R events/0   
  61.       bash-1408  [000] 26208.816939:   1408:120:R ==> [000]     9:120:R events/0   
  62.   events/0-9     [000] 26208.817081:      9:120:R   + [000]  1377:120:R gnome-terminal  
  63.   events/0-9     [000] 26208.817088:      9:120:S ==> [000]  1377:120:R gnome-terminal  
  64. </PRE><BR>  
  65. <BR>  
  66. <P>在 sched_swich 跟蹤器獲取的跟蹤資訊中記錄了程序間的喚醒操作和排程切換資訊,可以通過符號‘ + ’和‘ ==> ’區分;喚醒操作記錄給出了當前程序喚醒執行的程序,程序排程切換記錄中顯示了接替當前程序執行的後續程序。</P>  
  67. <P>描述程序狀態的格式為“Task-PID:Priority:Task-State”。以示例跟蹤資訊中的第一條跟蹤記錄為例,可以看到程序 bash 的 PID 為 1408 ,其對應的核心態優先順序為 120 ,當前狀態為 S(可中斷睡眠狀態),當前 bash 並沒有喚醒其它程序;從第 3 條記錄可以看到,程序 bash 將程序 events/0 喚醒,而在第 4 條記錄中發生了程序排程,程序 bash 切換到程序 events/0 執行。</P>  
  68. <P>在 Linux 核心中,程序的狀態在核心標頭檔案 include/linux/sched.h 中定義,包括可執行狀態 TASK_RUNNING(對應跟蹤資訊中的符號‘ R ’)、可中斷阻塞狀態 TASK_INTERRUPTIBLE(對應跟蹤資訊中的符號‘ S ’)等。同時該標頭檔案也定義了使用者態程序所使用的優先順序的範圍,最小值為 MAX_USER_RT_PRIO(值為 100 ),最大值為 MAX_PRIO - 1(對應值為 139 ),預設為 DEFAULT_PRIO(值為 120 );在本例中,程序優先順序都是預設值  
  69.  120 。</P>  
  70. <DIV class=ibm-alternate-rule>  
  71. <HR>  
  72. </DIV>  
  73. <P class="ibm-ind-link ibm-back-to-top"><BR>  
  74. </P>  
  75. <P><A name=major5><SPAN class=atitle>irqsoff 跟蹤器</SPAN></A></P>  
  76. <P>當關閉中斷時,CPU 會延遲對裝置的狀態變化做出反應,有時候這樣做會對系統性能造成比較大的影響。irqsoff 跟蹤器可以對中斷被關閉的狀況進行跟蹤,有助於發現導致較大延遲的程式碼;當出現最大延遲時,跟蹤器會記錄導致延遲的跟蹤資訊,檔案 tracing_max_latency 則記錄中斷被關閉的最大延時。</P>  
  77. <BR>  
  78. <A name=listing4><STRONG>清單 4. irqsoff 跟蹤器使用示例</STRONG></A><BR>  
  79. <SPAN style="FONT-FAMILY: monospace"><SPAN style="WHITE-SPACE: pre"></SPAN></SPAN><PRE class=cpp name="code">                 
  80.  [[email protected] tracing]# pwd   
  81.  /sys/kernel/debug/tracing   
  82.  [[email protected] tracing]# echo 0 > tracing_enabled   
  83.  [[email protected] tracing]# echo 1 > /proc/sys/kernel/ftrace_enabled   
  84.  [[email protected] tracing]# echo irqsoff > current_tracer   
  85.  [[email protected] tracing]# echo 1 > tracing_on   
  86.  [[email protected] tracing]# echo 1 > tracing_enabled   
  87.  # 讓核心執行一段時間,這樣 ftrace 可以收集一些跟蹤資訊,之後再停止跟蹤
  88.  [[email protected] tracing]# echo 0 > tracing_enabled   
  89.  [[email protected] tracing]# cat trace | head -35   
  90.  # tracer: irqsoff 
  91.  # 
  92.  # irqsoff latency trace v1.1.5 on 2.6.33.1 
  93.  # -------------------------------------------------------------------- 
  94.  # latency: 34380 us, #6/6, CPU#1 | (M:desktop VP:0, KP:0, SP:0 HP:0 #P:2) 
  95.  #    ----------------- 
  96.  #    | task: -0 (uid:0 nice:0 policy:0 rt_prio:0) 
  97.  #    ----------------- 
  98.  #  => started at: reschedule_interrupt 
  99.  #  => ended at:   restore_all_notrace 
  100.  # 
  101.  # 
  102.  #                  _------=> CPU#            
  103.  #                 / _-----=> irqs-off        
  104.  #                | / _----=> need-resched    
  105.  #                || / _---=> hardirq/softirq 
  106.  #                ||| / _--=> preempt-depth   
  107.  #                |||| /_--=> lock-depth       
  108.  #                |||||/     delay             
  109.  #  cmd    pid    |||||| time  |   caller      
  110.  #    \   /       ||||||   \   |   /           
  111.   <idle>-0       1dN... 4285us!: trace_hardirqs_off_thunk <-reschedule_interrupt   
  112.   <idle>-0       1dN... 34373us+: smp_reschedule_interrupt <-reschedule_interrupt   
  113.   <idle>-0       1dN... 34375us+: native_apic_mem_write <-smp_reschedule_interrupt   
  114.   <idle>-0       1dN... 34380us+: trace_hardirqs_on_thunk <-restore_all_notrace   
  115.   <idle>-0       1dN... 34384us : trace_hardirqs_on_caller <-restore_all_notrace   
  116.   <idle>-0       1dN... 34384us : <stack trace>   
  117.  => trace_hardirqs_on_thunk   
  118.  [[email protected] tracing]# cat tracing_max_latency   
  119.  34380 </PRE><BR>  
  120. <BR>  
  121. <P>從清單 4 中的輸出資訊中,可以看到當前 irqsoff 延遲跟蹤器的版本資訊。接下來是最大延遲時間,以 us 為單位,本例中為 34380 us ,檢視檔案 tracing_max_latency 也可以獲取該值。從“task:”欄位可以知道延遲發生時正在執行的程序為 idle(其 pid 為 0 )。中斷的關閉操作是在函式 reschedule_interrupt 中進行的,由“=> started at:”標識,函式 restore_all_ontrace 重新激活了中斷,由“=> ended at:”標識;中斷關閉的最大延遲發生在函式  
  122.  trace_hardirqs_on_thunk 中,這個可以從最大延遲時間所在的記錄項看到,也可以從延遲記錄資訊中最後的“=>”標識所對應的記錄行知道這一點。</P>  
  123. <P>在輸出資訊中,irqs-off、need_resched 等欄位對應於程序結構 struct task_struct 的欄位或者狀態標誌,可以從標頭檔案 arch/<platform>/include/asm/thread_info.h 中檢視程序支援的狀態標誌,include/linux/sched.h 則給出了結構 struct task_struct 的定義。其中,irqs-off 欄位顯示是否中斷被禁止,為‘ d ’表示中斷被禁止;need_resched 欄位顯示為‘ N ’表示設定了程序狀態標誌  
  124.  TIF_NEED_RESCHED。hardirq/softirq 欄位表示當前是否發生硬體中斷 / 軟中斷;preempt-depth 表示是否禁止程序搶佔,例如在持有自旋鎖的情況下程序是不能被搶佔的,本例中程序 idle 是可以被其它程序搶佔的。結構 struct task_struct 中的 lock_depth 欄位是與大核心鎖相關的,而最近的核心開發工作中有一部分是與移除大核心鎖相關的,這裡對該欄位不再加以描述。</P>  
  125. <P>另外,還有用於跟蹤禁止程序搶佔的跟蹤器 preemptoff 和跟蹤中斷 / 搶佔禁止的跟蹤器 preemptirqsoff,它們的使用方式與輸出資訊格式與 irqsoff 跟蹤器類似,這裡不再贅述。</P>  
  126. <DIV class=ibm-alternate-rule>  
  127. <HR>  
  128. </DIV>  
  129. <P class="ibm-ind-link ibm-back-to-top"><BR>  
  130. </P>  
  131. <P><A name=major6><SPAN class=atitle>跟蹤指定模組中的函式</SPAN></A></P>  
  132. <P>前面提過,通過檔案 set_ftrace_filter 可以指定要跟蹤的函式,預設目標為所有可跟蹤的核心函式;可以將感興趣的函式通過 echo 寫入該檔案。為了方便使用,set_ftrace_filter 檔案還支援簡單格式的萬用字元。</P>  
  133. <UL>  
  134. <LI><STRONG>begin*</STRONG>選擇所有名字以 begin 字串開頭的函式 <LI><STRONG>*middle*</STRONG>選擇所有名字中包含 middle 字串的函式 <LI><STRONG>*end</STRONG>選擇所有名字以 end 字串結尾的函式 </LI></UL>  
  135. <P>需要注意的是,這三種形式不能組合使用,比如“begin*middle*end”實際的效果與“begin”相同。另外,使用萬用字元表示式時,需要用單引號將其括起來,如果使用雙引號,shell 可能會對字元‘ * ’進行擴充套件,這樣最終跟蹤的物件可能與目標函式不一樣。</P>  
  136. <P>通過該檔案還可以指定屬於特定模組的函式,這要用到 mod 指令。指定模組的格式為:</P>  
  137. <TABLE border=0 cellSpacing=0 cellPadding=0 width="100%" sizcache="0" sizset="6"><TBODY sizcache="0" sizset="6"><TR sizcache="0" sizset="6"><TD class=code-outline sizcache="0" sizset="6"><PRE class=displaycode name="code">        echo ':mod:[module_name]' > set_ftrace_filter   
  138. </PRE></TD></TR></TBODY></TABLE>  
  139. <BR>  
  140. <P>下面給出了一個指定跟蹤模組 ipv6 中的函式的例子。可以看到,指定跟蹤模組 ipv6 中的函式會將檔案 set_ftrace_filter 的內容設定為只包含該模組中的函式。</P>  
  141. <BR>  
  142. <A name=listing5><STRONG>清單 5. 指定跟蹤 ipv6 模組中的函式</STRONG></A><BR>  
  143. <SPAN style="FONT-SIZE: 16px"></SPAN><PRE class=cpp name="code">                  
  144.  [[email protected] tracing]# pwd   
  145.  /sys/kernel/debug/tracing   
  146.  [[email protected] tracing]# echo ':mod:ipv6' > set_ftrace_filter   
  147.  [[email protected] tracing]# cat set_ftrace_filter | head -5   
  148.  ipv6_opt_accepted   
  149.  inet6_net_exit   
  150.  ipv6_gro_complete   
  151.  inet6_create   
  152.  ipv6_addr_copy   
  153. </PRE><BR>  
  154. <BR>  
  155. <DIV class=ibm-alternate-rule>  
  156. <HR>  
  157. </DIV>  
  158. <P class="ibm-ind-link ibm-back-to-top"><BR>  
  159. </P>  
  160. <P><A name=major7><SPAN class=atitle>小結</SPAN></A></P>  
  161. <P>本系列文章對 ftrace 的配置和使用進行了介紹。本文是其中的第二部分,介紹了 ftrace 所提供的跟蹤器的使用,重點對不同格式的跟蹤資訊進行了描述,並對如何跟蹤指定模組中的函式進行了介紹。當前 Linux 核心中 ftrace 支援多個跟蹤器,由於篇幅限制,這裡不能對其全部加以描述。讀者可參照參考資料中給出的資訊對 ftrace 跟蹤器進行更深入的瞭解。下一篇文章將對如何在程式碼中通過 ftrace 提供的工具函式與 ftrace 互動進行描述。</P>  
  162. <BR>  
  163. <BR>  
  164. <PRE></PRE>  
[cpp] view plaincopyprint?
  1. [[email protected] tracing]# pwd   
  2. /sys/kernel/debug/tracing   
  3. [[email protected] tracing]# echo 0 > tracing_enabled   
  4. [[email protected] tracing]# echo 1 > /proc/sys/kernel/ftrace_enabled   
  5. [[email protected] tracing]# echo function > current_tracer   
  6. [[email protected] tracing]# echo 1 > tracing_on   
  7. [[email protected] tracing]# echo 1 > tracing_enabled   
  8. # 讓核心執行一段時間,這樣 ftrace 可以收集一些跟蹤資訊,之後再停止跟蹤
  9. [[email protected] tracing]# echo 0 > tracing_enabled   
  10. [[email protected] tracing]# cat trace | head -10   
  11. # tracer: function 
  12. #         TASK-PID    CPU#    TIMESTAMP  FUNCTION 
  13. #            | |       |          |         | 
  14.          <idle>-0     [000] 20654.426521: _raw_spin_lock <-scheduler_tick   
  15.          <idle>-0     [000] 20654.426522: task_tick_idle <-scheduler_tick   
  16.          <idle>-0     [000] 20654.426522: cpumask_weight <-scheduler_tick   
  17.          <idle>-0     [000] 20654.426523: cpumask_weight <-scheduler_tick   
  18.          <idle>-0     [000] 20654.426523: run_posix_cpu_timers <-update_process_times   
  19. <idle>-0 [000] 20654.426524: hrtimer_forward <-tick_sched_timer   
				
 [[email protected] tracing]# pwd 
 /sys/kernel/debug/tracing 
 [[email protected] tracing]# echo 0 > tracing_enabled 
 [[email protected] tracing]# echo 1 > /proc/sys/kernel/ftrace_enabled 
 [[email protected] tracing]# echo function > current_tracer 
 [[email protected] tracing]# echo 1 > tracing_on 
 [[email protected] tracing]# echo 1 > tracing_enabled 

 # 讓核心執行一段時間,這樣 ftrace 可以收集一些跟蹤資訊,之後再停止跟蹤

 [[email protected] tracing]# echo 0 > tracing_enabled 
 [[email protected] tracing]# cat trace | head -10 
 # tracer: function 
 # 
 #         TASK-PID    CPU#    TIMESTAMP  FUNCTION 
 #            | |       |          |         | 
          <idle>-0     [000] 20654.426521: _raw_spin_lock <-scheduler_tick 
          <idle>-0     [000] 20654.426522: task_tick_idle <-scheduler_tick 
          <idle>-0     [000] 20654.426522: cpumask_weight <-scheduler_tick 
          <idle>-0     [000] 20654.426523: cpumask_weight <-scheduler_tick 
          <idle>-0     [000] 20654.426523: run_posix_cpu_timers <-update_process_times 
 <idle>-0 [000] 20654.426524: hrtimer_forward <-tick_sched_timer 
trace 檔案給出的資訊格式很清晰。首先,欄位“tracer:”給出了當前所使用的跟蹤器的名字,這裡為 function 跟蹤器。然後是跟蹤資訊記錄的格式,TASK 欄位對應任務的名字,PID 欄位則給出了任務的程序 ID,欄位 CPU# 表示執行被跟蹤函式的 CPU 號,這裡可以看到 idle 程序執行在 0 號 CPU 上,其程序 ID 是 0 ;欄位 TIMESTAMP 是時間戳,其格式為“<secs>.<usecs>,表示執行該函式時對應的時間戳;FUNCTION 一列則給出了被跟蹤的函式,函式的呼叫者通過符號 “<-” 標明,這樣可以觀察到函式的呼叫關係。
在 function 跟蹤器給出的資訊中,可以通過 FUNCTION 列中的符號 “<-” 來檢視函式呼叫關係,但是由於中間會混合不同函式的呼叫,導致看起來非常混亂,十分不方便。function_graph 跟蹤器則可以提供類似 C 程式碼的函式呼叫關係資訊。通過寫檔案 set_graph_function 可以顯示指定要生成呼叫關係的函式,預設會對所有可跟蹤的核心函式生成函式呼叫關係圖。清單 2 給出了使用 function_grapch 跟蹤器的示例,示例中將核心函式 __do_fault 作為觀察物件,該函式在核心運作過程中會被頻繁呼叫。
在檔案 trace 的輸出資訊中,首先給出的也是當前跟蹤器的名字,這裡是 function_graph 。接下來是詳細的跟蹤資訊,可以看到,函式的呼叫關係以類似 C 程式碼的形式組織。
CPU 欄位給出了執行函式的 CPU 號,本例中都為 1 號 CPU。DURATION 欄位給出了函式執行的時間長度,以 us 為單位。FUNCTION CALLS 則給出了呼叫的函式,並顯示了呼叫流程。注意,對於不呼叫其它函式的函式,其對應行以“;”結尾,而且對應的 DURATION 欄位給出其執行時長;對於呼叫其它函式的函式,則在其“}”對應行給出了執行時長,該時間是一個累加值,包括了其內部呼叫的函式的執行時長。DURATION 欄位給出的時長並不是精確的,它還包含了執行 ftrace 自身的程式碼所耗費的時間,所以示例中將內部函式時長累加得到的結果會與對應的外圍呼叫函式的執行時長並不一致;不過通過該欄位還是可以大致瞭解函式在時間上的執行開銷的。清單 2 中最後通過 echo 命令重置了檔案 set_graph_function 。
sched_switch 跟蹤器可以對程序的排程切換以及之間的喚醒操作進行跟蹤,如清單 3 所示。
[cpp] view plaincopyprint?
  1. [[email protected] tracing]# pwd   
  2. /sys/kernel/debug/tracing   
  3. [[email protected] tracing]# echo 0 > tracing_enabled   
  4. [[email protected] tracing]# echo 1 > /proc/sys/kernel/ftrace_enabled   
  5. [[email protected] tracing]# echo sched_switch > current_tracer   
  6. [[email protected] tracing]# echo 1 > tracing_on   
  7. [[email protected] tracing]# echo 1 > tracing_enabled   
  8. # 讓核心執行一段時間,這樣 ftrace 可以收集一些跟蹤資訊,之後再停止跟蹤
  9. [[email protected] tracing]# echo 0 > tracing_enabled   
  10. [[email protected] tracing]# cat trace | head -10   
  11. # tracer: sched_switch 
  12. #  TASK-PID    CPU#    TIMESTAMP  FUNCTION 
  13. #     | |       |          |         | 
  14.      bash-1408  [000] 26208.816058:   1408:120:S   + [000]  1408:120:S bash   
  15.      bash-1408  [000] 26208.816070:   1408:120:S   + [000]  1408:120:S bash   
  16.      bash-1408  [000] 26208.816921:   1408:120:R   + [000]     9:120:R events/0   
  17.      bash-1408  [000] 26208.816939:   1408:120:R ==> [000]     9:120:R events/0   
  18.  events/0-9     [000] 26208.817081:      9:120:R   + [000]  1377:120:R gnome-terminal  
  19.  events/0-9     [000] 26208.817088:      9:120:S ==> [000]  1377:120:R gnome-terminal  
				
 [[email protected] tracing]# pwd 
 /sys/kernel/debug/tracing 
 [[email protected] tracing]# echo 0 > tracing_enabled 
 [[email protected] tracing]# echo 1 > /proc/sys/kernel/ftrace_enabled 
 [[email protected] tracing]# echo sched_switch > current_tracer 
 [[email protected] tracing]# echo 1 > tracing_on 
 [[email protected] tracing]# echo 1 > tracing_enabled 

 # 讓核心執行一段時間,這樣 ftrace 可以收集一些跟蹤資訊,之後再停止跟蹤

 [[email protected] tracing]# echo 0 > tracing_enabled 
 [[email protected] tracing]# cat trace | head -10 
 # tracer: sched_switch 
 # 
 #  TASK-PID    CPU#    TIMESTAMP  FUNCTION 
 #     | |       |          |         | 
      bash-1408  [000] 26208.816058:   1408:120:S   + [000]  1408:120:S bash 
      bash-1408  [000] 26208.816070:   1408:120:S   + [000]  1408:120:S bash 
      bash-1408  [000] 26208.816921:   1408:120:R   + [000]     9:120:R events/0 
      bash-1408  [000] 26208.816939:   1408:120:R ==> [000]     9:120:R events/0 
  events/0-9     [000] 26208.817081:      9:120:R   + [000]  1377:120:R gnome-terminal
  events/0-9     [000] 26208.817088:      9:120:S ==> [000]  1377:120:R gnome-terminal
在 sched_swich 跟蹤器獲取的跟蹤資訊中記錄了程序間的喚醒操作和排程切換資訊,可以通過符號‘ + ’和‘ ==> ’區分;喚醒操作記錄給出了當前程序喚醒執行的程序,程序排程切換記錄中顯示了接替當前程序執行的後續程序。
描述程序狀態的格式為“Task-PID:Priority:Task-State”。以示例跟蹤資訊中的第一條跟蹤記錄為例,可以看到程序 bash 的 PID 為 1408 ,其對應的核心態優先順序為 120 ,當前狀態為 S(可中斷睡眠狀態),當前 bash 並沒有喚醒其它程序;從第 3 條記錄可以看到,程序 bash 將程序 events/0 喚醒,而在第 4 條記錄中發生了程序排程,程序 bash 切換到程序 events/0 執行。
在 Linux 核心中,程序的狀態在核心標頭檔案 include/linux/sched.h 中定義,包括可執行狀態 TASK_RUNNING(對應跟蹤資訊中的符號‘ R ’)、可中斷阻塞狀態 TASK_INTERRUPTIBLE(對應跟蹤資訊中的符號‘ S ’)等。同時該標頭檔案也定義了使用者態程序所使用的優先順序的範圍,最小值為 MAX_USER_RT_PRIO(值為 100 ),最大值為 MAX_PRIO - 1(對應值為 139 ),預設為 DEFAULT_PRIO(值為 120 );在本例中,程序優先順序都是預設值 120 。
當關閉中斷時,CPU 會延遲對裝置的狀態變化做出反應,有時候這樣做會對系統性能造成比較大的影響。irqsoff 跟蹤器可以對中斷被關閉的狀況進行跟蹤,有助於發現導致較大延遲的程式碼;當出現最大延遲時,跟蹤器會記錄導致延遲的跟蹤資訊,檔案 tracing_max_latency 則記錄中斷被關閉的最大延時。
[cpp] view plaincopyprint?
  1. [[email protected] tracing]# pwd   
  2. /sys/kernel/debug/tracing   
  3. [[email protected] tracing]# echo 0 > tracing_enabled   
  4. [[email protected] tracing]# echo 1 > /proc/sys/kernel/ftrace_enabled   
  5. [[email protected] tracing]# echo irqsoff > current_tracer   
  6. [[email protected] tracing]# echo 1 > tracing_on   
  7. [[email protected] tracing]# echo 1 > tracing_enabled   
  8. # 讓核心執行一段時間,這樣 ftrace 可以收集一些跟蹤資訊,之後再停止跟蹤
  9. [[email protected] tracing]# echo 0 > tracing_enabled   
  10. [[email protected] tracing]# cat trace | head -35   
  11. # tracer: irqsoff 
  12. # irqsoff latency trace v1.1.5 on 2.6.33.1 
  13. # -------------------------------------------------------------------- 
  14. # latency: 34380 us, #6/6, CPU#1 | (M:desktop VP:0, KP:0, SP:0 HP:0 #P:2) 
  15. #    ----------------- 
  16. #    | task: -0 (uid:0 nice:0 policy:0 rt_prio:0) 
  17. #    ----------------- 
  18. #  => started at: reschedule_interrupt 
  19. #  => ended at:   restore_all_notrace 
  20. #                  _------=> CPU#            
  21. #                 / _-----=> irqs-off        
  22. #                | / _----=> need-resched    
  23. #                || / _---=> hardirq/softirq 
  24. #                ||| / _--=> preempt-depth   
  25. #                |||| /_--=> lock-depth       
  26. #                |||||/     delay             
  27. #  cmd    pid    |||||| time  |   caller