精品欧美一区二区三区在线观看 _久久久久国色av免费观看性色_国产精品久久在线观看_亚洲第一综合网站_91精品又粗又猛又爽_小泽玛利亚一区二区免费_91亚洲精品国偷拍自产在线观看 _久久精品视频在线播放_美女精品久久久_欧美日韩国产成人在线

Linux性能分析工具,ftrace的原理與使用

系統 Linux
在Linux 內核這片深邃而神秘的技術海洋里,每一個系統進程的運行、每一次資源的調度,都像是在黑暗中上演的復雜劇目。內核開發者們時常面臨著棘手難題:系統莫名卡頓,究竟是哪個內核函數在作祟?

在Linux 內核這片深邃而神秘的技術海洋里,每一個系統進程的運行、每一次資源的調度,都像是在黑暗中上演的復雜劇目。內核開發者們時常面臨著棘手難題:系統莫名卡頓,究竟是哪個內核函數在作祟?進程調度出現異常,問題根源又在何處?在漫長的探索歷程中,開發者們急需一款強大的工具,能夠像聚光燈一樣,穿透重重迷霧,照亮內核運行的每一個角落

它能深入追蹤內核函數調用流程,為排查棘手的系統問題提供關鍵線索;還能精準剖析系統延遲信息,助力定位性能瓶頸。無論是優化內核代碼,還是提升系統整體性能,ftrace 都發揮著不可替代的作用。接下來,讓我們一同深入探索 ftrace 的核心原理,學習如何在實際場景中靈活運用它,挖掘系統潛在的性能提升點 。

Part1.ftrace 是什么?

ftrace,全稱 Function Trace,是 Linux 內核自帶的一款強大的動態跟蹤工具,誕生于 2008 年,由 Steven Rostedt 開發,并在 2.6.27 版本的內核中首次引入 。最初,它只是一個簡單的函數跟蹤器,主要用于記錄內核的函數調用流程。經過多年的發展與完善,如今 ftrace 已演變成一個功能豐富的跟蹤框架,采用插件式的設計,支持開發者添加更多類型的跟蹤功能,在 Linux 內核的開發與調試中發揮著不可或缺的作用。

從本質上來說,ftrace 是一個內核級別的工具,它深入到 Linux 內核的內部,能夠詳細地跟蹤和記錄內核函數的調用、進程的調度、中斷的處理等關鍵信息。這就好比在一個大型工廠里,ftrace 就像是一個無處不在的監控系統,能夠記錄下每一個生產環節的運行情況,從原材料的進入,到產品的組裝,再到最終的出廠,每一個步驟都逃不過它的 “眼睛”。

在 Linux 內核中,ftrace 的作用至關重要。它為內核開發者和系統管理員提供了一種深入了解系統運行時行為的有效方式。通過 ftrace,我們可以清晰地看到內核函數之間的調用關系,了解函數的執行時間,以及進程在系統中的調度情況。這些信息對于優化內核性能、調試系統故障、以及理解系統的整體運行機制都具有極高的價值。

例如,當我們懷疑某個內核模塊存在性能問題時,可以使用 ftrace 來跟蹤該模塊中函數的調用情況,找出執行時間較長的函數,進而分析原因并進行優化。又或者,當系統出現異常崩潰時,ftrace 記錄的函數調用棧信息可以幫助我們快速定位到問題的根源,確定是哪個函數在什么情況下出現了錯誤。

Ftrace 包含兩個核心組成部分:

  • Framework core(框架核心):它主要起到管理各種 tracer(追蹤器)的作用,同時利用 tracefs 文件系統對用戶空間提供配置選項以及輸出 trace 信息,像是在整個流程中扮演著 “指揮官” 的角色,協調著各部分有序工作,確保數據的準確獲取與配置的有效實施。例如,它會負責對內核進行必要的修改、完成 Tracer 的注冊以及對 Ring Buffer 的控制等操作。
  • Tracers(追蹤器):Ftrace 實現了多種類型的 tracer,像 Function tracer(函數調用追蹤器)、Function graph tracer(函數調用圖跟蹤器)等都是其中的成員。每一種 tracer 都負責著不同的功能,并且它們統一由 Framework core 管理。具體來說,tracer 負責將 trace 信息保存到 Ring Buffer 中,例如 Function tracer 可以看出哪個函數何時被調用,開發人員還可以通過過濾器指定想要跟蹤的函數;Function graph tracer 則能夠清晰展現出函數的調用層次關系以及返回情況。

圖片圖片

不同的 tracer 可以幫助開發人員從不同角度去分析內核的運行情況,以便更精準地查找問題或者進行性能分析等工作。

Part2.ftrace的工作原理

2.1插樁技術

ftrace 的強大功能離不開其背后精妙的工作原理,而插樁技術則是其實現內核跟蹤的核心基礎。插樁技術就像是在系統這個龐大的機器中安裝了許多精密的 “傳感器”,這些 “傳感器” 能夠實時捕捉系統運行時的各種信息,為我們深入了解系統內部的運行機制提供了關鍵數據。ftrace 采用了靜態插樁和動態插樁兩種方式,它們各有特點,相互配合,共同實現了高效的內核跟蹤。

圖片圖片

靜態插樁是 ftrace 實現跟蹤功能的基礎方式之一 。當我們在 Kernel 中打開 CONFIG_FUNCTION_TRACER 功能后,編譯過程中會增加一個 - pg 的編譯選項。這個編譯選項就像是一個神奇的 “指令注入器”,它會在每個函數入口處插入一條 bl mcount 跳轉指令。這意味著,當系統中的每個函數運行時,都會先跳轉到 mcount 函數。在 mcount 函數中,會判斷函數指針 ftrace_trace_function 是否被注冊,默認注冊的是空函數 ftrace_stub,只有打開 function tracer 后才會注冊具體的處理函數 ftrace_trace_function。

這種靜態插樁的方式,就像是在每個函數的必經之路上設置了一個固定的 “檢查點”,無論函數何時被調用,都必須經過這個 “檢查點”,從而實現對所有函數調用的跟蹤。然而,這種方式雖然能夠全面地跟蹤函數調用,但也帶來了一定的性能開銷,因為每個函數調用都要額外執行一次跳轉和判斷操作,這在高負載的系統中可能會對性能產生明顯的影響。

為了解決靜態插樁帶來的性能問題,開發者們推出了 Dynamic ftrace,即動態插樁技術 。動態插樁技術的核心思想是動態修改函數指令,以實現更加靈活和高效的跟蹤。在編譯時,系統會記錄所有被添加跳轉指令(即支持追蹤)的函數。

在內核啟動時,這些函數入口處的跳轉指令會被替換為 nop 指令(nop 指令是一種空操作指令,執行它不會產生任何實際的運算或操作,僅占用一個指令周期),這樣在非調試狀態下,函數的執行就不會受到額外的性能損耗,就像在高速公路上取消了所有不必要的收費站,車輛可以暢行無阻,大大提高了系統的運行效率。而當需要進行跟蹤時,根據 function tracer 的設置,系統會動態地將被調試函數的 nop 指令替換為跳轉指令,從而實現對特定函數的追蹤。

這種動態插樁的方式,就像是一個智能的 “交通管制系統”,只有在需要監控某些特定 “車輛”(函數)時,才會在其行駛路徑上設置 “檢查點”,而在其他時候,道路則保持暢通,既實現了跟蹤功能,又最大限度地減少了對系統性能的影響。

2.2數據記錄與存儲

當 ftrace 通過插樁技術在函數入口處設置好 “傳感器” 后,接下來就需要考慮如何記錄這些函數調用過程中產生的數據,并將這些寶貴的信息存儲起來,以便后續的分析和處理。

在開啟 ftrace 的跟蹤功能后,首先會打開編譯選項 -pg,為每個函數都增加跳轉指令,這就像是為每個函數都安裝了一個 “數據采集器”,當函數被調用時,“數據采集器” 就會被觸發。

然后,系統會記錄這些可追蹤的函數,并為了減少性能消耗,將跳轉函數替換為 nop 指令,就像暫時關閉了這些 “數據采集器”,讓系統能夠高效運行。而當需要進行跟蹤時,通過 flag 標志位來動態管理,將需要追蹤的函數預留的 nop 指令替換回追蹤指令,此時 “數據采集器” 重新啟動,開始記錄調試信息,包括函數的調用時間、調用參數、返回值等關鍵信息。

ftrace 將這些追蹤信息存儲到 Ring buffer 緩沖區中 。Ring buffer 是一種環形緩沖區,它就像是一個循環的 “數據倉庫”,具有固定的大小。當追蹤信息產生時,會按照順序依次寫入 Ring buffer 中。當緩沖區被填滿后,新產生的追蹤信息會覆蓋最早寫入的信息,就像一個不斷循環的傳送帶,始終保持最新的追蹤數據在緩沖區中。這種設計方式既保證了追蹤信息的實時性,又避免了因緩沖區溢出而導致的數據丟失問題。

用戶可以通過讀取 Ring buffer 中的數據,來獲取系統的運行時行為信息,為性能分析和故障排查提供有力的支持。例如,在分析系統性能瓶頸時,開發人員可以從 Ring buffer 中讀取函數調用的時間信息,找出那些執行時間較長的函數,進而深入分析這些函數的內部實現,找出性能瓶頸所在;在排查系統故障時,開發人員可以讀取函數調用的參數和返回值信息,判斷函數是否在某些特定參數下出現異常,從而快速定位問題的根源。

2.3工作流程詳解

ftrace 的工作流程可以分為初始化、設置跟蹤選項、數據采集和輸出四個主要階段。

在初始化階段,ftrace 會進行一系列的準備工作,包括創建和初始化各種數據結構,注冊跟蹤處理函數,以及設置默認的跟蹤參數等。這個階段就像是搭建一個舞臺,為后續的跟蹤表演做好準備。在這個過程中,ftrace 會在內核中創建函數指針數組和跟蹤緩沖區,并將它們初始化為默認狀態。同時,ftrace 還會注冊一些默認的跟蹤處理函數,這些函數將負責處理各種類型的跟蹤事件。

設置跟蹤選項是 ftrace 工作流程中的一個關鍵步驟,它允許用戶根據自己的需求定制跟蹤行為。用戶可以通過tracefs文件系統下的一系列控制文件來設置跟蹤選項,比如選擇要使用的跟蹤器類型(如function跟蹤器、function_graph跟蹤器等),指定要跟蹤的函數列表,設置跟蹤過濾器等。通過這些靈活的配置選項,用戶可以精確地控制 ftrace 收集哪些信息,以及如何收集這些信息。例如,如果用戶只關心某個特定內核模塊中的函數調用情況,他可以通過設置跟蹤過濾器,讓 ftrace 只跟蹤該模塊中的函數,而忽略其他函數的調用。

數據采集階段是 ftrace 發揮其核心功能的階段。當系統運行時,ftrace 會根據用戶設置的跟蹤選項,在內核函數執行過程中實時采集相關的運行時信息。通過插樁技術,ftrace 在函數調用的關鍵位置觸發跟蹤處理函數,這些函數會收集函數的參數、返回值、調用時間等信息,并將這些信息存儲到跟蹤緩沖區中。在這個過程中,ftrace 會高效地處理大量的跟蹤數據,確保數據的準確性和完整性。

最后是數據輸出階段,用戶可以通過tracefs文件系統下的trace文件或者其他相關工具(如trace-cmd、kernelshark等)來讀取跟蹤緩沖區中的數據,并進行分析和處理。trace文件以一種可讀的文本格式存儲著跟蹤數據,用戶可以直接使用文本編輯器打開該文件,查看系統的運行時信息。而trace-cmd和kernelshark等工具則提供了更加方便和強大的數據分析功能,它們可以將跟蹤數據以圖表、圖形等形式展示出來,幫助用戶更直觀地理解系統的運行狀態,快速定位性能問題。

Part3.Ftrace的探測技術

3.1靜態探針機制

靜態探測是在內核代碼中調用 Ftrace 提供的相應接口來實現的,之所以稱之為靜態,是因為這種探測方式是在內核代碼里寫死的,會靜態編譯到內核代碼中。一旦內核編譯完成,就沒辦法再進行動態修改了。

這些在內核代碼里插入的用于探測的 tracepoint(跟蹤點),主要是為了支持 Event tracing(事件跟蹤)。并且,這些 tracepoint 有著對應的開關,而這個開關是由內核配置選項來進行控制的。值得一提的是,內核開發人員已經提前在一些關鍵的地方設置好了靜態探測點,當使用者有相應需求,開啟相關功能后,就可以查看這些地方的探測信息了,方便對內核特定關鍵環節進行分析和調試。

3.2動態探針機制

動態探測則利用了 GCC 編譯的 profile 特性來發揮作用。在 Linux 內核編譯之時,會在每個函數入口處預留數個字節。等到實際使用 Ftrace 時,就可以將之前預留的這些字節替換為所需要的指令,例如替換為能夠跳轉到需要執行探測操作代碼處的指令,從而實現探測功能。

像我們熟知的 function tracer(函數調用追蹤器)、function graph tracer(函數調用圖跟蹤器)等追蹤器,都是基于這種動態探測機制實現的。通過這樣的動態探測機制,開發人員可以更加靈活地根據具體需求去對內核函數進行跟蹤,獲取相應的運行信息,為查找性能問題、分析代碼執行邏輯等工作提供有力支持。

Part4.Ftrace如何使用

要使用 ftrace,首先就是需要將系統的 debugfs 或者 tracefs 給掛載到某個地方,幸運的是,幾乎所有的 Linux 發行版,都開啟了 debugfs/tracefs 的支持,所以我們也沒必要去重新編譯內核了。

在比較老的內核版本,譬如 CentOS 7 的上面,debugfs 通常被掛載到 /sys/kernel/debug 上面(debug 目錄下面有一個 tracing 目錄),而比較新的內核,則是將 tracefs 掛載到 /sys/kernel/tracing,無論是什么,我都喜歡將 tracing 目錄直接 link 到 /tracing。后面都會假設直接進入了 /tracing 目錄,后面,我會使用 Ubuntu 16.04 來舉例子,內核版本是 4.13 來舉例子。

在使用ftrace之前,需要內核進行支持,也就是內核需要打開編譯中的ftrace相關選項,關于怎么激活ftrace選項的問題,可以google之,下面只說明重要的設置步驟:

mkdir /debug;mount -t debugs nodev /debug; /*掛載debugfs到創建的目錄中去*/
cd /debug; cd tracing; 
/*如果沒有tracing目錄,則內核目前還沒有支持ftrace,需要配置參數,重新編譯*/。
echo nop > current_tracer;//清空tracer
echo function_graph > current_tracer;//使用圖形顯示調用關系
echo ip_rcv > set_graph_function;//設置過濾函數,可以設置多個
echo 1 > tracing_enabled開始追蹤

使用傳統的 ftrace 需要如下幾個步驟:

  • 選擇一種 tracer
  • 使能 ftrace
  • 執行需要 trace 的應用程序,比如需要跟蹤 ls,就執行 ls
  • 關閉 ftrace
  • 查看 trace 文件

用戶通過讀寫 debugfs 文件系統中的控制文件完成上述步驟。使用 debugfs,首先要掛載它。命令如下:

# mkdir /debug 
 # mount -t debugfs nodev /debug

此時您將在 /debug 目錄下看到 tracing 目錄。Ftrace 的控制接口就是該目錄下的文件。

選擇 tracer 的控制文件叫作 current_tracer 。選擇 tracer 就是將 tracer 的名字寫入這個文件,比如,用戶打算使用 function tracer,可輸入如下命令:

#echo ftrace > /debug/tracing/current_tracer

文件 tracing_enabled 控制 ftrace 的開始和結束。

#echo 1 >/debug/tracing/tracing_enable

上面的命令使能 ftrace 。同樣,將 0 寫入 tracing_enable 文件便可以停止 ftrace 。

ftrace 的輸出信息主要保存在 3 個文件中。

  • Trace,該文件保存 ftrace 的輸出信息,其內容可以直接閱讀。
  • latency_trace,保存與 trace 相同的信息,不過組織方式略有不同。主要為了用戶能方便地分析系統中有關延遲的信息。
  • trace_pipe 是一個管道文件,主要為了方便應用程序讀取 trace 內容。算是擴展接口吧。

我們使用 ls 來看看目錄下面到底有什么:

README                      current_tracer            hwlat_detector   per_cpu              set_event_pid       snapshot            trace_marker      tracing_max_latency
available_events            dyn_ftrace_total_info     instances        printk_formats       set_ftrace_filter   stack_max_size      trace_marker_raw  tracing_on
available_filter_functions  enabled_functions         kprobe_events    saved_cmdlines       set_ftrace_notrace  stack_trace         trace_options     tracing_thresh
available_tracers           events                    kprobe_profile   saved_cmdlines_size  set_ftrace_pid      stack_trace_filter  trace_pipe        uprobe_events
buffer_size_kb              free_buffer               max_graph_depth  saved_tgids          set_graph_function  trace               trace_stat        uprobe_profile
buffer_total_size_kb        function_profile_enabled  options          set_event            set_graph_notrace   trace_clock         tracing_cpumask

可以看到,里面有非常多的文件和目錄,具體的含義,大家可以去詳細的看官方文檔的解釋,后面只會重點介紹一些文件。

4.1 ftrace功能

我們可以通過 available_tracers 這個文件知道當前 ftrace 支持哪些插件。

cat available_tracers
hwlat blk mmiotrace function_graph wakeup_dl wakeup_rt wakeup function nop

通常用的最多的就是function和function_graph,當然,如果我們不想 trace 了,可以使用nop。我們首先打開function:

echo function > current_tracer
cat current_tracer
function

上面我們將 function 寫入到了 current_tracer 來開啟 function 的 trace,我通常會在 cat 下 current_tracer 這個文件,主要是防止自己寫錯了。然后 ftrace 就開始工作了,會將相關的 trace 信息放到 trace 文件里面,我們直接讀取這個文件就能獲取相關的信息。

cat trace | head -n 15
# tracer: function
#
#                              _-----=> irqs-off
#                             / _----=> need-resched
#                            | / _---=> hardirq/softirq
#                            || / _--=> preempt-depth
#                            ||| /     delay
#           TASK-PID   CPU#  ||||    TIMESTAMP  FUNCTION
#              | |       |   ||||       |         |
            bash-29409 [002] .... 16158426.215771: mutex_unlock <-tracing_set_tracer
          <idle>-0     [039] d... 16158426.215771: call_cpuidle <-do_idle
          <idle>-0     [039] d... 16158426.215772: cpuidle_enter <-call_cpuidle
          <idle>-0     [039] d... 16158426.215773: cpuidle_enter_state <-cpuidle_enter
            bash-29409 [002] .... 16158426.215773: __fsnotify_parent <-vfs_write
          <idle>-0     [039] d... 16158426.215773: sched_idle_set_state <-cpuidle_enter_state

我們可以設置只跟蹤特定的 function

echo schedule > set_ftrace_filter
cat set_ftrace_filter
schedule
cat trace | head -n 15
# tracer: function
#
#                              _-----=> irqs-off
#                             / _----=> need-resched
#                            | / _---=> hardirq/softirq
#                            || / _--=> preempt-depth
#                            ||| /     delay
#           TASK-PID   CPU#  ||||    TIMESTAMP  FUNCTION
#              | |       |   ||||       |         |
            bash-29409 [010] .... 16158462.591708: schedule <-schedule_timeout
   kworker/u81:2-29339 [008] .... 16158462.591736: schedule <-worker_thread
            sshd-29395 [012] .... 16158462.591788: schedule <-schedule_hrtimeout_range_clock
       rcu_sched-9     [010] .... 16158462.595475: schedule <-schedule_timeout
            java-23597 [006] .... 16158462.600326: schedule <-futex_wait_queue_me
            java-23624 [020] .... 16158462.600855: schedule <-schedule_hrtimeout_range_clock

當然,如果我們不想 trace schedule 這個函數,也可以這樣做:

echo '!schedule' > set_ftrace_filter

或者也可以這樣做:

echo schedule > set_ftrace_notrace

Function filter 的設置也支持 *match,match* ,*match* 這樣的正則表達式,譬如我們可以 echo '*lock*' < set_ftrace_notrace 來禁止跟蹤帶 lock 的函數,set_ftrace_notrace 文件里面這時候就會顯示:

cat set_ftrace_notrace
xen_pte_unlock
read_hv_clock_msr
read_hv_clock_tsc
update_persistent_clock
read_persistent_clock
set_task_blockstep
user_enable_block_step
...

4.2函數圖

相比于 function,function_graph 能讓我們更加詳細的去知道內核函數的上下文,我們打開 function_graph:

echo function_graph > current_tracer
cat trace | head -15
# tracer: function_graph
#
# CPU  DURATION                  FUNCTION CALLS
# |     |   |                     |   |   |   |
 10)   0.085 us    |  sched_idle_set_state();
 10)               |  cpuidle_reflect() {
 10)   0.035 us    |    menu_reflect();
 10)   0.288 us    |  }
 10)               |  rcu_idle_exit() {
 10)   0.034 us    |    rcu_dynticks_eqs_exit();
 10)   0.296 us    |  }
 10)   0.032 us    |  arch_cpu_idle_exit();
 10)               |  tick_nohz_idle_exit() {
 10)   0.073 us    |    ktime_get();
 10)               |    update_ts_time_stats() {

我們也可以只跟蹤某一個函數的堆棧

echo kfree > set_graph_function
cat trace | head -n 15
# tracer: function_graph
#
# CPU  DURATION                  FUNCTION CALLS
# |     |   |                     |   |   |   |
 16)               |  kfree() {
 16)   0.147 us    |    __slab_free();
 16)   1.437 us    |  }
 10)   0.162 us    |  kfree();
 17) @ 923817.3 us |          } /* intel_idle */
 17)   0.044 us    |          sched_idle_set_state();
 17)   ==========> |
 17)               |          smp_apic_timer_interrupt() {
 17)               |            irq_enter() {
 17)               |              rcu_irq_enter() {
 17)   0.049 us    |                rcu_dynticks_eqs_exit();

4.3事件

上面提到了 function 的 trace,在 ftrace 里面,另外用的多的就是 event 的 trace,我們可以在 events 目錄下面看支持哪些事件:

ls events/
alarmtimer  cma         ext4      fs_dax        i2c          kvm      mmc     nmi             printk        regulator  smbus       task                     vmscan     xdp
block       compaction  fib       ftrace        iommu        kvmmmu   module  oom             random        rpm        sock        thermal                  vsyscall   xen
bpf         cpuhp       fib6      gpio          irq          libata   mpx     page_isolation  ras           sched      spi         thermal_power_allocator  wbt        xhci-hcd
btrfs       dma_fence   filelock  header_event  irq_vectors  mce      msr     pagemap         raw_syscalls  scsi       swiotlb     timer                    workqueue
cgroup      enable      filemap   header_page   jbd2         mdio     napi    percpu          rcu           signal     sync_trace  tlb                      writeback
clk         exceptions  fs        huge_memory   kmem         migrate  net     power           regmap        skb        syscalls    udp                      x86_fpu

上面列出來的都是分組的,我們可以繼續深入下去,譬如下面是查看sched相關的事件:

ls events/sched/
enable                  sched_migrate_task  sched_process_exit  sched_process_wait  sched_stat_sleep  sched_switch                 sched_wakeup_new
filter                  sched_move_numa     sched_process_fork  sched_stat_blocked  sched_stat_wait   sched_wait_task              sched_waking
sched_kthread_stop      sched_pi_setprio    sched_process_free  sched_stat_iowait   sched_stick_numa  sched_wake_idle_without_ipi
sched_kthread_stop_ret  sched_process_exec  sched_process_hang  sched_stat_runtime  sched_swap_numa   sched_wakeup

對于某一個具體的事件,我們也可以查看:

ls events/sched/sched_wakeup
enable  filter  format  hist  id  trigger

不知道大家注意到了沒有,上述目錄里面,都有一個enable的文件,我們只需要往里面寫入 1,就可以開始 trace 這個事件。譬如下面就開始 tracesched_wakeup這個事件:

echo 1 > events/sched/sched_wakeup/enable
cat trace | head -15
# tracer: nop
#
#                              _-----=> irqs-off
#                             / _----=> need-resched
#                            | / _---=> hardirq/softirq
#                            || / _--=> preempt-depth
#                            ||| /     delay
#           TASK-PID   CPU#  ||||    TIMESTAMP  FUNCTION
#              | |       |   ||||       |         |
            bash-29409 [012] d... 16158657.832294: sched_wakeup: comm=kworker/u81:1 pid=29359 prio=120 target_cpu=008
   kworker/u81:1-29359 [008] d... 16158657.832321: sched_wakeup: comm=sshd pid=29395 prio=120 target_cpu=010
          <idle>-0     [012] dNs. 16158657.835922: sched_wakeup: comm=rcu_sched pid=9 prio=120 target_cpu=012
          <idle>-0     [024] dNh. 16158657.836908: sched_wakeup: comm=java pid=23632 prio=120 target_cpu=024
          <idle>-0     [022] dNh. 16158657.839921: sched_wakeup: comm=java pid=23624 prio=120 target_cpu=022
          <idle>-0     [016] dNh. 16158657.841866: sched_wakeup: comm=java pid=23629 prio=120 target_cpu=016

我們也可以 tracesched里面的所有事件:

echo 1 > events/sched/enable
cat trace | head -15
# tracer: nop
#
#                              _-----=> irqs-off
#                             / _----=> need-resched
#                            | / _---=> hardirq/softirq
#                            || / _--=> preempt-depth
#                            ||| /     delay
#           TASK-PID   CPU#  ||||    TIMESTAMP  FUNCTION
#              | |       |   ||||       |         |
            bash-29409 [010] d... 16158704.468377: sched_waking: comm=kworker/u81:2 pid=29339 prio=120 target_cpu=008
            bash-29409 [010] d... 16158704.468378: sched_stat_sleep: comm=kworker/u81:2 pid=29339 delay=164314267 [ns]
            bash-29409 [010] d... 16158704.468379: sched_wake_idle_without_ipi: cpu=8
            bash-29409 [010] d... 16158704.468379: sched_wakeup: comm=kworker/u81:2 pid=29339 prio=120 target_cpu=008
            bash-29409 [010] d... 16158704.468382: sched_stat_runtime: comm=bash pid=29409 runtime=360343 [ns] vruntime=131529875864926 [ns]
            bash-29409 [010] d... 16158704.468383: sched_switch: prev_comm=bash prev_pid=29409 prev_prio=120 prev_state=S ==> next_comm=swapper/10 next_pid=0 next_prio=120

當然也可以 trace 所有的事件:

echo 1 > events/enable
cat trace | head -15
# tracer: nop
#
#                              _-----=> irqs-off
#                             / _----=> need-resched
#                            | / _---=> hardirq/softirq
#                            || / _--=> preempt-depth
#                            ||| /     delay
#           TASK-PID   CPU#  ||||    TIMESTAMP  FUNCTION
#              | |       |   ||||       |         |
            bash-29409 [010] .... 16158761.584188: writeback_mark_inode_dirty: bdi (unknown): ino=3089 state=I_DIRTY_SYNC|I_DIRTY_DATASYNC|I_DIRTY_PAGES flags=I_DIRTY_SYNC|I_DIRTY_DATASYNC|I_DIRTY_PAGES
            bash-29409 [010] .... 16158761.584189: writeback_dirty_inode_start: bdi (unknown): ino=3089 state=I_DIRTY_SYNC|I_DIRTY_DATASYNC|I_DIRTY_PAGES flags=I_DIRTY_SYNC|I_DIRTY_DATASYNC|I_DIRTY_PAGES
            bash-29409 [010] .... 16158761.584190: writeback_dirty_inode: bdi (unknown): ino=3089 state=I_DIRTY_SYNC|I_DIRTY_DATASYNC|I_DIRTY_PAGES flags=I_DIRTY_SYNC|I_DIRTY_DATASYNC|I_DIRTY_PAGES
            bash-29409 [010] .... 16158761.584193: do_sys_open: "trace" 8241 666
            bash-29409 [010] .... 16158761.584193: kmem_cache_free: call_site=ffffffff8e862614 ptr=ffff91d241fa4000
            bash-29409 [010] .... 16158761.584194: sys_exit: NR 2 = 3

4.4trace-cmd

從上面的例子可以看到,其實使用 ftrace 并不是那么方便,我們需要手動的去控制多個文件,但幸運的是,我們有 trace-cmd,作為 ftrace 的前端,trace-cmd 能夠非常方便的讓我們進行 ftrace 的操作,譬如我們可以使用 record 命令來 trace sched 事件:

trace-cmd record -e sched

然后使用report命令來查看 trace 的數據:

trace-cmd report | head -10
version = 6
CPU 27 is empty
cpus=40
       trace-cmd-29557 [003] 16159201.985281: sched_waking:         comm=kworker/u82:3 pid=28507 prio=120 target_cpu=037
       trace-cmd-29557 [003] 16159201.985283: sched_migrate_task:   comm=kworker/u82:3 pid=28507 prio=120 orig_cpu=37 dest_cpu=5
       trace-cmd-29557 [003] 16159201.985285: sched_stat_sleep:     comm=kworker/u82:3 pid=28507 delay=137014529 [ns]
       trace-cmd-29585 [023] 16159201.985286: sched_stat_runtime:   comm=trace-cmd pid=29585 runtime=217630 [ns] vruntime=107586626253137 [ns]
       trace-cmd-29557 [003] 16159201.985286: sched_wake_idle_without_ipi: cpu=5
       trace-cmd-29595 [037] 16159201.985286: sched_stat_runtime:   comm=trace-cmd pid=29595 runtime=213227 [ns] vruntime=105364596011783 [ns]
       trace-cmd-29557 [003] 16159201.985287: sched_wakeup:         kworker/u82:3:28507 [120] success=1 CPU:005

當然,在report的時候也可以加入自己的 filter 來過濾數據,譬如下面,我們就過濾出sched_wakeup事件并且是success為 1 的。

trace-cmd report -F 'sched/sched_wakeup: success == 1'  | head -10
version = 6
CPU 27 is empty
cpus=40
       trace-cmd-29557 [003] 16159201.985287: sched_wakeup:         kworker/u82:3:28507 [120] success=1 CPU:005
       trace-cmd-29557 [003] 16159201.985292: sched_wakeup:         trace-cmd:29561 [120] success=1 CPU:007
          <idle>-0     [032] 16159201.985294: sched_wakeup:         qps_json_driver:24669 [120] success=1 CPU:032
          <idle>-0     [032] 16159201.985298: sched_wakeup:         trace-cmd:29590 [120] success=1 CPU:026
          <idle>-0     [010] 16159201.985300: sched_wakeup:         trace-cmd:29563 [120] success=1 CPU:010
       trace-cmd-29597 [037] 16159201.985302: sched_wakeup:         trace-cmd:29595 [120] success=1 CPU:039
          <idle>-0     [010] 16159201.985302: sched_wakeup:         sshd:29395 [120] success=1 CPU:010

大家可以注意下success == 1,這其實是一個對事件里面 field 進行的表達式運算了,對于不同的事件,我們可以通過查看其 format 來知道它的實際 fields 是怎樣的,譬如:

cat events/sched/sched_wakeup/format
name: sched_wakeup
ID: 294
format:
    field:unsigned short common_type;   offset:0;   size:2; signed:0;
    field:unsigned char common_flags;   offset:2;   size:1; signed:0;
    field:unsigned char common_preempt_count;   offset:3;   size:1; signed:0;
    field:int common_pid;   offset:4;   size:4; signed:1;

    field:char comm[16];    offset:8;   size:16;    signed:1;
    field:pid_t pid;    offset:24;  size:4; signed:1;
    field:int prio; offset:28;  size:4; signed:1;
    field:int success;  offset:32;  size:4; signed:1;
    field:int target_cpu;   offset:36;  size:4; signed:1;

print fmt: "comm=%s pid=%d prio=%d target_cpu=%03d", REC->comm, REC->pid,

Part5.ftrace 的使用指南

5.1準備工作

在使用 ftrace 之前,需要確保內核配置中啟用了相關選項。主要的配置選項包括CONFIG_FTRACE、CONFIG_FUNCTION_TRACER、CONFIG_FUNCTION_GRAPH_TRACER等 。這些選項在編譯內核時進行設置,如果你的系統已經安裝好了內核,并且不確定是否啟用了這些選項,可以通過查看/boot/config-$(uname -r)文件來確認。如果文件中包含類似CONFIG_FTRACE=y、CONFIG_FUNCTION_TRACER=y的配置項,就說明相應的選項已經啟用。如果沒有啟用,你可能需要重新編譯內核并開啟這些選項。

完成內核配置后,還需要掛載debugfs文件系統。debugfs是一種特殊的文件系統,用于提供內核調試信息的接口,ftrace 就是通過debugfs來實現與用戶空間的交互。掛載debugfs文件系統非常簡單,只需要執行以下命令:

mount -t debugfs nodev /sys/kernel/debug

執行上述命令后,debugfs文件系統就會被掛載到/sys/kernel/debug目錄下。在這個目錄下,你可以找到ftrace相關的控制文件和跟蹤數據文件,后續對 ftrace 的操作都將通過這些文件來完成。

5.2常用文件及操作

/sys/kernel/debug/tracing目錄下包含了許多與 ftrace 相關的文件,這些文件是我們使用 ftrace 的關鍵。下面介紹一些常用的文件及其操作:

①tracing_on:這個文件用于啟用或禁用跟蹤功能。當你將1寫入該文件時,ftrace 會開始向跟蹤緩沖區寫入數據,即啟用跟蹤;當寫入0時,跟蹤功能將被禁用 。例如,要啟用跟蹤功能,可以執行以下命令:

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

②trace:該文件以文本格式存儲著內核跟蹤緩沖區中的內容,是我們查看跟蹤結果的主要文件。你可以使用cat命令來讀取其中的內容,例如:

cat /sys/kernel/debug/tracing/trace

如果需要清空跟蹤緩沖區的內容,可以向該文件寫入空字符串,命令如下:

echo > /sys/kernel/debug/tracing/trace
  • current_tracer:通過這個文件可以指定當前要使用的跟蹤器。ftrace 支持多種跟蹤器,如function、function_graph、irqsoff等,每個跟蹤器都有其獨特的功能 。例如,要使用function跟蹤器,可以執行以下命令:
  • echo function > /sys/kernel/debug/tracing/current_tracer像available_tracers:此文件列出了當前內核中可用的跟蹤器類型。你可以使用cat命令查看其內容,以便了解系統支持哪些跟蹤器,命令如下:
cat /sys/kernel/debug/tracing/available_tracers

③set_ftrace_filter:通過向這個文件寫入函數名,可以指定只跟蹤特定的函數。例如,如果你只想跟蹤do_sys_open函數,可以執行以下命令:

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

此外,該文件還支持通配符匹配,比如echo 'irq*' > /sys/kernel/debug/tracing/set_ftrace_filter可以跟蹤所有以irq開頭的函數。

5.3實際操作示例

為了更直觀地了解 ftrace 的使用方法,我們以跟蹤/dev/tty0設備節點的tty_open和tty_write函數調用流程為例,詳細展示 ftrace 的使用過程。

首先,編寫一個簡單的應用程序serial.c,用于打開和寫入/dev/tty0設備節點:

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>

int main(int argc, char *argv[]) {
    char pstr[64] = "hello world";
    if (2 != argc) {
        printf("usage:%s /dev/ttyx\n", argv[0]);
        return -1;
    }
    int fd = open(argv[1], O_RDWR);
    write(fd, pstr, sizeof(pstr));
    close(fd);
    return 0;
}

將上述代碼保存為serial.c文件,并使用交叉編譯器將其編譯成可執行文件,例如:

aarch64-linux-gnu-gcc -o serial serial.c

接下來,編寫一個腳本trace_pid.sh,用于操作tracefs節點來配置和啟動 ftrace 跟蹤:

#!/bin/bash
tracefs=/sys/kernel/debug/tracing

# 停止跟蹤
echo 0 > $tracefs/tracing_on
# 清空trace下的輸出
echo > $tracefs/trace
# 將tty_open和tty_write加入函數圖功能
echo "tty_open tty_write" > $tracefs/set_graph_function
# 每行都會顯示TASK/PID
echo 1 > $tracefs/options/funcgraph-proc
# 結束花括號后增加函數尾部注釋
echo 1 > $tracefs/options/funcgraph-tail
# 啟動跟蹤
echo 1 > $tracefs/tracing_on
# 追蹤本進程PID
echo $$ > $tracefs/set_ftrace_pid
# 啟用function_graph跟蹤功能
echo function_graph > $tracefs/current_tracer
# 啟動跟蹤
echo 1 > $tracefs/tracing_on

# 執行應用程序
./serial /dev/tty0

將上述腳本保存為trace_pid.sh文件,并賦予其可執行權限:

chmod +x trace_pid.sh

最后,執行腳本:

./trace_pid.sh

執行完成后,通過查看/sys/kernel/debug/tracing/trace文件,就可以看到tty_open和tty_write函數的調用流程信息,類似如下輸出:

# tracer: function_graph
#
# CPU  TASK/PID         DURATION      FUNCTION CALLS
# |     |    |           |   |        |   |   |   |
  0)   serial-xxxx)    serial-xxx    0.000 us    |   tty_open();
  0)   serial-xxxx)    serial-xxx    0.000 us    |   tty_write();

通過這些信息,我們可以清晰地了解到tty_open和tty_write函數的調用順序、執行時間以及它們與其他函數之間的關系,從而幫助我們分析和優化相關的代碼邏輯。

5.4進階使用技巧

掌握了 ftrace 的基本使用方法后,我們還可以學習一些進階技巧,以更靈活、高效地使用 ftrace 進行性能分析。

(1)查看函數調用棧:在分析復雜的系統問題時,查看函數調用棧信息非常有用。ftrace 提供了相關的功能來滿足這一需求。你可以通過設置trace_options文件中的stacktrace選項來啟用函數調用棧的記錄。例如:

echo stacktrace > /sys/kernel/debug/tracing/trace_options

啟用后,在trace文件的輸出中,每個函數調用記錄都會附帶其調用棧信息,幫助你更好地理解函數的調用關系和執行上下文。

(2)跟蹤短時間執行的命令:對于一些執行時間非常短的命令,普通的跟蹤方式可能無法捕捉到完整的信息。這時,可以使用trace-cmd工具結合-p function_graph選項來進行跟蹤。trace-cmd是一個基于 ftrace 的命令行工具,它提供了更便捷的操作方式和更豐富的功能。例如,要跟蹤ls命令的執行過程,可以執行以下命令:

trace-cmd record -p function_graph -- ls

執行完成后,使用trace-cmd report命令查看跟蹤結果,就可以看到ls命令在執行過程中涉及的內核函數調用信息。

(3)過濾技巧:ftrace 的過濾功能非常強大,除了前面提到的通過set_ftrace_filter文件按函數名進行過濾外,還可以結合模塊名進行更精準的過濾。例如,要跟蹤ext3模塊中所有以write開頭的函數,可以執行以下命令:

echo 'write*:mod:ext3' > /sys/kernel/debug/tracing/set_ftrace_filter

此外,還可以通過設置set_ftrace_notrace文件來排除某些函數的跟蹤,例如:

echo 'do_not_trace_function' > /sys/kernel/debug/tracing/set_ftrace_notrace

(4)用戶態內核態聯動:在實際的性能分析中,有時需要同時跟蹤用戶態和內核態的函數調用,以全面了解系統的運行情況。ftrace 通過uprobes和kprobes機制實現了用戶態和內核態的聯動跟蹤。uprobes用于在用戶空間的函數中插入探測點,kprobes則用于在內核函數中插入探測點。例如,要在用戶態的main函數和內核態的do_sys_open函數中插入探測點,可以按照以下步驟操作:

首先,獲取main函數的地址。假設應用程序為test,可以使用readelf命令獲取其地址:

readelf -s test | grep main

然后,將探測點信息寫入uprobe_events文件,例如:

echo "p:uprobes/my_main /path/to/test:main" > /sys/kernel/debug/tracing/uprobe_events

對于內核態的do_sys_open函數,將探測點信息寫入kprobe_events文件:

echo "p:kprobes/my_do_sys_open do_sys_open" > /sys/kernel/debug/tracing/kprobe_events

最后,啟用跟蹤并查看結果:

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

通過這種方式,就可以同時跟蹤用戶態和內核態的函數調用,為深入分析系統性能問題提供更全面的數據支持。

(5)靈活控制 trace 開關:在實際應用中,有時需要根據特定的條件來靈活控制 ftrace 的跟蹤開關。例如,當某個特定的函數被調用時,自動開啟或關閉跟蹤。ftrace 提供了觸發器(triggers)功能來實現這一需求。你可以通過set_ftrace_filter文件設置觸發器,例如:

echo "do_fault:traceoff" > /sys/kernel/debug/tracing/set_ftrace_filter

上述命令表示當do_fault函數被調用時,關閉跟蹤功能。你還可以設置觸發次數,例如:

echo "do_trap:traceoff:3" > /sys/kernel/debug/tracing/set_ftrace_filter

這表示當do_trap函數被調用時,最多關閉跟蹤功能 3 次。通過這種靈活的觸發機制,我們可以根據實際需求更精準地控制 ftrace 的跟蹤行為,提高性能分析的效率和準確性。

Part6.Ftrace的應用場景

6.1性能優化方面

在系統性能優化工作中,Ftrace 是一個得力的助手。例如,當開發人員想要優化某個軟件系統的性能時,可利用 Ftrace 來找出系統中的性能瓶頸所在。

假設開發一款大型的網絡服務應用,在高并發場景下響應時間較長,懷疑是某些核心模塊的函數執行效率問題。這時就可以使用 Ftrace 的 function 追蹤器,通過在 /sys/kernel/debug/tracing 目錄下操作,將 function 寫入 current_tracer 文件啟用該追蹤器。如果只想關注特定模塊(比如網絡通信模塊)里涉及的函數,還能通過 set_ftrace_filter 文件來指定,像 echo 'module:network_communication*' > set_ftrace_filter 這樣的命令(假設網絡通信模塊相關函數命名有特定前綴),就可以篩選出對應函數進行跟蹤。

然后開啟追蹤功能,讓系統在高并發場景下運行一段時間后,查看 trace 文件或者實時通過 trace_pipe 文件來獲取跟蹤信息。從這些信息中,可以清晰看到每個被跟蹤函數的調用時間戳、所在 CPU 等情況,進而對比分析出哪些函數的執行耗時較長。比如發現 handle_network_request 函數每次執行時間都遠超預期,那么開發人員就可以針對這個函數進行代碼優化,比如檢查算法復雜度是否過高、是否存在頻繁的資源申請釋放等情況,通過優化這個函數來提升整個網絡服務應用在高并發場景下的性能表現。

6.2故障排查方面

在系統出現故障,尤其是內核相關故障時,Ftrace 能發揮重要作用。比如系統突然崩潰,開發人員需要弄清楚崩潰前的函數調用情況來定位問題根源。

Ftrace 可以記錄崩潰前的函數調用棧,為開發人員提供崩潰時的上下文信息。例如,當系統出現內核崩潰,懷疑是某個驅動模塊在執行過程中出現了異常導致的。通過之前配置好的 Ftrace(確保已經掛載好相關文件系統并且設置好了合適的追蹤器等,如使用 function_graph 追蹤器來詳細查看函數調用關系和執行流程),在系統下次復現崩潰問題前開啟追蹤功能,等崩潰發生后,查看記錄下來的跟蹤數據。

從 trace 文件中,能夠看到在崩潰前各個函數是如何依次被調用的,哪個函數可能是最后執行的,或者有沒有出現反復調用某個函數卻無法返回等異常情況。像是看到 driver_init_function 函數調用后,緊接著就出現了一系列系統報錯并最終崩潰,那開發人員就可以重點排查這個驅動初始化函數內部的代碼邏輯,檢查是否存在內存越界訪問、空指針引用等常見的導致內核崩潰的問題,大大提高定位內核故障問題的效率,幫助更快地修復系統故障。

6.3內核開發方面

在內核開發過程中,Ftrace 的應用十分廣泛且重要。

比如開發一個新的內核模塊,需要驗證函數調用的正確性。可以啟用 Ftrace 的 function 追蹤器,在模塊加載和運行過程中,跟蹤相關函數的調用情況。查看 trace 文件里記錄的函數被調用的順序、時間以及對應的參數情況(如果有相關記錄的話),來確認是否和預期的函數調用邏輯一致。若開發的是一個和系統調用相關的內核功能,想要了解其執行過程中涉及的系統調用細節,利用 Ftrace 的系統調用跟蹤功能,就能清晰看到具體觸發了哪些系統調用,每個系統調用的傳入參數以及返回結果等情況,方便判斷是否符合設計預期,有沒有出現不該出現的系統調用或者參數傳遞錯誤等問題。

再舉例來說,在對內核中某個復雜的任務調度模塊進行開發優化時,通過 Ftrace 的相關調度追蹤器(像 sched_switch 追蹤器等),可以觀察到不同進程在調度過程中的切換情況、各個進程等待調度的時間等信息,基于這些信息來優化任務調度算法,確保內核能高效合理地進行任務調度,使得系統整體運行更加流暢穩定,由此可見 Ftrace 對于內核開發工作的重要支撐作用。

6.4Ftrace 使用的注意事項

⑴性能影響

在使用 Ftrace 時,需要留意它可能對系統性能產生的影響。由于 Ftrace 的工作機制是對內核函數進行跟蹤記錄相關信息,尤其是當開啟它去跟蹤大量函數時,這種數據收集和記錄的操作會占用一定的系統資源,比如會消耗 CPU 的運算能力以及占用內存空間來存儲跟蹤數據等。

例如,在一個高負載運行且本身對性能要求極為苛刻的服務器環境中,如果不加選擇地啟用 Ftrace 去跟蹤眾多函數,可能會導致系統響應速度變慢,影響正常業務的開展。所以,建議大家僅在確實有必要進行內核相關分析、調試或者性能排查等情況下才啟用 Ftrace,避免因不必要的跟蹤給系統性能帶來不良影響。

⑵安全問題

安全方面至關重要,對于 Ftrace 而言,要確保只有獲得授權的用戶能夠訪問 Ftrace 相關文件。因為 Ftrace 涉及到內核層面的信息跟蹤,如果權限把控不當,惡意用戶有可能通過獲取這些跟蹤文件中的數據,分析出系統內核的運行邏輯、函數調用關系等關鍵信息,進而找到系統潛在的漏洞或者實施其他攻擊行為。

比如,在一個企業內部的多用戶服務器環境中,若沒有對 Ftrace 文件設置合理的訪問權限,可能會出現普通用戶甚至外部非法用戶獲取到敏感的內核跟蹤數據,這無疑會給整個系統帶來嚴重的安全隱患。所以,在使用過程中,一定要嚴格配置好權限,保障使用的安全性。

⑶數據存儲管理

Ftrace 在運行過程中,跟蹤數據會快速增加。這是因為它會持續記錄內核函數調用等相關信息,隨著時間的推移以及跟蹤函數數量的增多,所占用的存儲空間會越來越大。

倘若不及時進行清理,當存儲空間被大量的跟蹤數據占滿后,不僅會影響后續 Ftrace 繼續正常記錄數據。

Part7.Ftrace應用案例

7.1性能優化

假設我們正在開發一個文件系統相關的內核模塊,近期發現系統在進行大量文件讀寫操作時,性能出現了明顯的下降。為了找出性能瓶頸,我們決定使用 ftrace 來進行深入分析。

首先,我們需要確保 ftrace 已經正確配置并啟用。通過前面介紹的方法,掛載 debugfs 文件系統,并進入 /sys/kernel/debug/tracing 目錄。

接下來,我們使用 Function graph tracer 來跟蹤文件系統相關函數的調用情況。因為 Function graph tracer 能夠以圖形化的方式展示函數調用關系和執行時間,這對于我們分析性能瓶頸非常有幫助。我們執行以下命令:

echo 0 > tracing_on  # 停止追蹤
echo nop > current_tracer  # 清除當前追蹤器
echo function_graph > current_tracer  # 啟用function_graph跟蹤器

然后,我們設置只跟蹤與文件系統相關的函數,這里以 “vfs_read” 和 “vfs_write” 函數為例,執行以下命令:

echo vfs_read > set_graph_function
echo vfs_write >> set_graph_function

完成上述設置后,我們開始進行文件讀寫操作,模擬實際的業務場景。操作完成后,我們停止追蹤,執行命令:

echo 0 > tracing_on

接著,我們查看 trace 文件,獲取跟蹤結果:

cat trace

在 trace 文件中,我們可以看到類似以下的信息:

# tracer: function_graph
#
# CPU  DURATION                  FUNCTION CALLS
# |     |   |                     |   |   |   |
0)  1234.567 us |  vfs_read() {
0)  10.123 us |    generic_file_read() {
0)  5.678 us |      do_generic_file_read() {
0)  3.456 us |        page_cache_sync_readahead() {
0)  1.234 us |          ra_submit() {
0)  0.567 us |            submit_bio() {
0)  0.234 us |              generic_make_request() {
0)  0.123 us |                blk_queue_bio() {
0)  0.067 us |                  blk_mq_submit_bio() {
0)  0.034 us |                    blk_mq_alloc_request() {
0)  0.012 us |                      kmem_cache_alloc() {
0)  0.005 us |                        slab_alloc() {
0)  0.002 us |                         ...
0)  0.002 us |                        }
0)  0.005 us |                      }
0)  0.012 us |                    }
0)  0.034 us |                  }
0)  0.067 us |                }
0)  0.123 us |              }
0)  0.234 us |            }
0)  0.567 us |          }
0)  1.234 us |        }
0)  3.456 us |      }
0)  5.678 us |    }
0)  1234.567 us |  }

從這些信息中,我們可以清晰地看到函數的調用層次和每個函數的執行時間。通過分析,我們發現 “page_cache_sync_readahead” 函數的執行時間較長,進一步查看其內部調用的函數,發現 “ra_submit” 函數以及其下的一系列函數調用也占用了不少時間。經過深入研究代碼邏輯,我們發現 “ra_submit” 函數在某些情況下會進行不必要的磁盤 I/O 操作,導致性能下降。

針對這個問題,我們對代碼進行了優化,減少了不必要的磁盤 I/O 操作。再次使用 ftrace 進行跟蹤測試,發現 “vfs_read” 和 “vfs_write” 函數的執行時間明顯縮短,系統的文件讀寫性能得到了顯著提升。

7.2故障排查

某一天,運維人員發現生產系統出現了響應延遲的問題,用戶反饋在訪問系統時,頁面加載速度明顯變慢,一些操作甚至需要等待很長時間才能完成。為了找出問題的根源,我們決定使用 ftrace 來進行故障排查。

首先,我們懷疑是中斷相關的問題導致了系統響應延遲,因為當中斷被禁止時,系統無法及時響應外部事件,可能會導致響應延遲。所以我們使用 Irqsoff tracer 來跟蹤中斷禁止的情況。進入 /sys/kernel/debug/tracing 目錄,執行以下命令:

echo 0 > tracing_on  # 停止追蹤
echo nop > current_tracer  # 清除當前追蹤器
echo irqsoff > current_tracer  # 啟用irqsoff跟蹤器

然后,我們等待系統響應延遲問題再次出現,在問題出現期間,ftrace 會記錄下中斷禁止的相關信息。問題出現后,我們停止追蹤,執行命令:

echo 0 > tracing_on

接著,查看 trace 文件,獲取跟蹤結果:

cat trace

在 trace 文件中,我們看到了類似以下的信息:

# tracer: irqsoff
#
# WORST IRQSOFF LATENCY: 12345 us
#
#              _-----=> irqs-off
#             / _----=> need-resched
#            | / _---=> hardirq/softirq
#            || / _--=> preempt-depth
#            ||| / _-=> migrate-disable
#            |||| /     delay
#           TASK-PID     CPU#  |||||  TIMESTAMP  FUNCTION
#              | |         |   |||||     |         |
my_task-1234[000] d.h. 123456.789012: irqsoff_delay: 12345 us
my_task-1234[000] d.h. 123456.789012:   <stack trace>
 => 0xffffffffc0123456
 => some_function_1
 => some_function_2
 => some_function_3

從這些信息中,我們可以看到中斷禁止的最長延遲時間為 12345 微秒,并且記錄了導致中斷禁止的函數調用棧。通過分析函數調用棧,我們發現 “some_function_3” 函數在執行過程中禁止了中斷,并且禁止時間較長,很可能是這個函數導致了系統響應延遲。

進一步查看 “some_function_3” 函數的代碼,發現該函數在進行一些復雜的數據處理時,為了保證數據的一致性,錯誤地禁止了中斷,而且處理過程中存在一些低效的算法,導致執行時間過長。我們對該函數進行了優化,將數據處理過程中的中斷禁止時間盡量縮短,并優化了算法,提高了執行效率。

經過優化后,再次觀察系統運行情況,響應延遲問題得到了有效解決,用戶反饋系統恢復正常。通過這個案例,我們可以看到 ftrace 在故障排查中的強大作用,它能夠幫助我們快速定位問題的根源,為解決系統故障提供有力的支持。

Part8.ftrace 與其他性能分析工具對比

在 Linux 性能分析的工具庫中,ftrace 并非孤立存在,它與其他工具如 perf、SystemTap、LTTng 等共同構成了一個全面的性能分析生態系統,各自在不同的場景中發揮著獨特的作用。

8.1 ftrace 與perf對比

perf 是 Linux 內核提供的另一個強大的性能分析工具,它與 ftrace 既有相似之處,也有明顯的區別。

從功能上看,perf 更側重于性能統計和事件采樣,它能夠收集 CPU 性能計數器、硬件性能事件等數據,幫助用戶分析系統的性能瓶頸,比如找出 CPU 使用率高的函數、分析緩存命中率等。而 ftrace 則專注于函數調用跟蹤和內核事件跟蹤,能夠詳細記錄內核函數的調用順序、執行時間,以及進程的調度情況等信息 。例如,當我們想要了解某個應用程序在 CPU 上的運行效率,分析其指令執行次數、緩存未命中次數等性能指標時,perf 是一個很好的選擇;而當我們需要深入探究內核中函數之間的調用關系,以及函數的執行流程時,ftrace 則能提供更詳細的信息。

在使用方式上,perf 通過命令行工具進行操作,支持多種命令選項和參數組合,以滿足不同的分析需求。它的輸出結果通常是經過統計和匯總的數據,需要一定的分析能力才能從中提取有價值的信息。而 ftrace 主要通過tracefs文件系統與用戶交互,用戶通過修改/sys/kernel/debug/tracing目錄下的文件來配置和啟動跟蹤,操作相對直觀簡單。ftrace 的輸出結果是詳細的跟蹤日志,記錄了系統運行時的各種事件,用戶可以直接查看這些日志來了解系統的運行情況。

8.2 ftrace 與 SystemTap 對比

SystemTap 是一個動態跟蹤工具,它允許用戶使用腳本語言編寫復雜的跟蹤腳本,實現對內核和用戶空間代碼的深度分析。與 ftrace 相比,SystemTap 的優勢在于其強大的腳本功能和靈活的自定義能力。用戶可以通過編寫 SystemTap 腳本,在系統運行時動態地插入探測點,收集各種系統狀態信息,并進行復雜的數據分析和處理。例如,使用 SystemTap 可以編寫一個腳本,實時監測系統中所有進程的內存使用情況,并在內存使用率超過一定閾值時發出警報 。

然而,SystemTap 的復雜性也帶來了一些挑戰。由于其腳本語言相對復雜,學習成本較高,對于初學者來說上手難度較大。而且,SystemTap 的腳本在運行時需要動態加載內核模塊,這可能會對系統的穩定性產生一定的影響,在一些對穩定性要求極高的生產環境中,使用 SystemTap 需要謹慎考慮。相比之下,ftrace 設計輕量且功能直接,它不需要額外加載內核模塊,對系統的影響較小,具有更好的穩定性和可靠性 。

8.3 ftrace與LTTng對比

LTTng 是一個高性能的事件跟蹤框架,它提供了高效的二進制接口和獨立的緩沖設計,適合進行深入的系統級跟蹤和分析。LTTng 支持多種類型的事件跟蹤,包括內核事件、用戶空間事件等,并且能夠在高負載的情況下保持較低的性能開銷 。

在性能方面,LTTng 經過優化,能夠在不顯著影響系統性能的前提下,收集大量的跟蹤數據,這使得它在一些對性能要求苛刻的場景中表現出色。而 ftrace 雖然也具有較低的系統開銷,但在處理大規模數據收集時,可能不如 LTTng 高效。在數據存儲和分析方面,LTTng 采用二進制格式存儲跟蹤數據,這種格式占用空間小,便于快速存儲和傳輸,但也需要專門的工具來解析和分析數據。ftrace 則以文本格式存儲跟蹤數據,數據可讀性強,用戶可以直接使用文本編輯器查看和分析數據,但文本格式的數據占用空間相對較大,在處理大量數據時可能會面臨存儲和傳輸的挑戰。

8.4工具選擇建議

在實際的性能分析工作中,選擇合適的工具至關重要。如果您只是想簡單了解系統的性能概況,分析 CPU、內存等資源的使用情況,那么 top、vmstat 等基本的性能分析工具就可以滿足需求;如果您需要深入分析系統的性能瓶頸,找出熱點函數和性能關鍵路徑,perf 是一個不錯的選擇;當您關注內核函數的調用流程、進程的調度情況,以及需要對內核行為進行詳細的跟蹤和調試時,ftrace 則是首選工具;如果您需要進行復雜的自定義分析,編寫腳本實現特定的跟蹤需求,SystemTap 可以提供強大的支持;而對于那些對性能要求極高,需要在高負載環境下進行系統級跟蹤和分析的場景,LTTng 則更具優勢 。

在很多情況下,單一的工具可能無法滿足所有的分析需求,我們可以結合使用多種工具,充分發揮它們各自的優勢。例如,在分析一個復雜的性能問題時,可以先使用 perf 進行性能統計和采樣,找出可能存在問題的熱點區域,然后使用 ftrace 對這些熱點區域的內核函數調用進行詳細跟蹤,進一步分析問題的根源;還可以使用 SystemTap 編寫腳本來收集更多的上下文信息,或者使用 LTTng 進行更深入的系統級跟蹤,以全面了解系統的運行狀態,最終找到解決性能問題的有效方案。

責任編輯:武曉燕 來源: 深度Linux
相關推薦

2025-08-04 03:05:00

2021-09-06 07:45:08

LinuxLinux內核

2025-05-22 10:15:59

JITWatchJava

2017-06-12 18:48:00

Android性能分析工具

2025-04-01 02:00:22

2022-09-28 14:13:03

Linux工具

2011-04-02 10:29:20

Linux工具

2012-08-01 10:50:48

性能測試測試架構

2013-03-21 11:20:00

性能測試性能調優測試

2017-05-17 15:09:46

Linux分析性能工具

2022-01-24 16:06:58

Linux 5.17RTLA工具

2013-03-06 10:24:12

ksar工具系統性能

2021-06-07 14:57:46

開源開源工具Linux

2018-11-27 11:35:32

systemtapMySQL調試工具

2022-07-15 08:52:03

Linux優化

2021-04-12 14:50:25

Linux工具命令

2011-08-15 22:10:08

Oracle性能分析工

2015-09-14 10:41:51

PHP性能分析微觀分析

2015-08-18 11:44:02

PHP性能分析宏觀分析

2021-06-17 08:59:45

React前端優化
點贊
收藏

51CTO技術棧公眾號

韩国三级日本三级少妇99| 欧美无乱码久久久免费午夜一区| 96久久精品| 日韩 欧美 亚洲| 欧美久久综合网| 日韩亚洲电影在线| 欧美日韩亚洲第一| 黄页视频在线播放| 成人av电影在线| 国产精品亚洲自拍| 国产无精乱码一区二区三区| 国产精品一区二区av交换| 69久久99精品久久久久婷婷 | 欧美熟妇另类久久久久久多毛| 欧美hdxxx| 国产精品乱码一区二区三区软件 | 欧美美女搞黄| 国产成人综合网| 国产精品国产三级国产aⅴ9色| 久青草免费视频| 97久久夜色精品国产| 精品视频久久久久久| 熟妇无码乱子成人精品| 成人福利片在线| 欧美日韩免费网站| 伊人再见免费在线观看高清版| 福利在线视频导航| 99久久精品免费观看| 亚洲最大av网站| 在线免费观看视频网站| 免费中文字幕日韩欧美| 97精品国产aⅴ7777| 丁香花五月激情| 欧美r级电影| 亚洲欧美国产高清va在线播| 亚洲女则毛耸耸bbw| 国产精品国产三级在线观看| 欧美私模裸体表演在线观看| 999香蕉视频| 亚洲涩涩在线| 精品久久久久久亚洲国产300| 国产真实老熟女无套内射| 亚洲精品传媒| 国产精品视频免费| 日韩av一区二区三区在线观看 | 91在线国产观看| 国产激情美女久久久久久吹潮| 国产乱淫a∨片免费视频| 蜜臀a∨国产成人精品| 国产精品成人一区| 日韩中文字幕高清| 免费一级片91| 国产精品网址在线| 91久久国语露脸精品国产高跟| 欧美a一区二区| 国产精品一区二区三区久久| 艳妇乳肉豪妇荡乳av无码福利| 日韩福利视频导航| 国产精品专区一| 国产一区二区三区黄片| 激情五月激情综合网| 91美女高潮出水| av综合在线观看| 国产精品69毛片高清亚洲| 999精品视频一区二区三区| 性欧美一区二区三区| 国产suv精品一区二区三区| 成人自拍爱视频| 手机看片福利在线| 2021久久国产精品不只是精品| 欧美成熟毛茸茸复古| 福利视频在线看| 亚洲同性gay激情无套| 狠狠干视频网站| 国产av天堂无码一区二区三区| 中文字幕av网站| 国产黑人绿帽在线第一区| 男女男精品视频网站| 99久久激情| 欧美黑人一级爽快片淫片高清| 国产一级片视频| 亚洲女同同性videoxma| 国产精品劲爆视频| 国产高清免费av| 91免费在线播放| 亚洲草草视频| av毛片午夜不卡高**水| 色先锋aa成人| 中文字幕色网站| 色婷婷综合久久久久久| 日韩专区中文字幕| 欧美三日本三级少妇99| 久久国产乱子精品免费女| 不卡一区二区三区视频| 国际av在线| 一区二区在线观看av| 欧美 日韩 国产一区| 亚洲精品乱码日韩| 亚洲激情视频网| 韩国一级黄色录像| 国产日韩综合| 91在线免费视频| 欧美色视频免费| 一区二区三区在线视频免费观看| 日批视频在线免费看| 亚洲成a人片777777久久| 日韩精品免费一线在线观看| 999精品在线视频| 久久精品亚洲| 精品999在线观看| free性欧美hd另类精品| 欧美色综合网站| 一本色道综合久久欧美日韩精品 | 亚洲国产欧美不卡在线观看| 超碰在线最新网址| 6080国产精品一区二区| 美女被到爽高潮视频| 手机亚洲手机国产手机日韩| 91大神在线播放精品| 亚洲精品.www| 亚洲精品成人少妇| 中文字幕永久有效| 禁果av一区二区三区| 91av在线免费观看| 狠狠躁夜夜躁av无码中文幕| 亚洲乱码中文字幕| 国产精品嫩草影院8vv8| 欧美精品一二| 国产精品久久久久久久av电影| 日韩中文字幕免费在线观看| 亚洲另类在线视频| 五月天丁香花婷婷| 99九九热只有国产精品| 国产乱人伦真实精品视频| 国产在线观看网站| 在线观看一区不卡| 日韩精品国内| 国产一区二区播放| 麻豆精品一区二区三区| 日韩影视精品| 日本肉肉一区| 丝袜情趣国产精品| 亚洲自拍偷拍另类| 中文字幕在线不卡一区| 久热精品在线观看视频| 日韩在线精品| 91精品国产综合久久香蕉最新版 | 日韩一区欧美一区| 亚洲免费成人在线视频| 91成人看片| 91久久偷偷做嫩草影院| 青青在线视频| 亚洲福利视频在线| 国产乡下妇女做爰视频| 91免费版在线看| 精品久久久久久久免费人妻| gogogo高清在线观看一区二区| 国产精品91在线观看| 日韩在线观看www| 欧美绝品在线观看成人午夜影视| 欧美肥妇bbwbbw| 国产精品18久久久久久久久| 日本wwwcom| 午夜a一级毛片亚洲欧洲| 国产福利精品在线| 午夜不卡视频| 91精品国产免费久久综合| 免费在线一区二区三区| 99视频精品全部免费在线| 国产一级片黄色| 国产精品久久久久蜜臀| av色综合网| 亚洲日本天堂| 久久久久999| 欧美自拍偷拍一区二区| 狠狠色狠色综合曰曰| 女教师淫辱の教室蜜臀av软件| 国产精品一二三区在线| 日韩在线综合网| 日韩精品久久久久久久电影99爱| 91久久大香伊蕉在人线| 亚洲欧美se| 久久国产精品久久久久久久久久| 亚州av在线播放| 666欧美在线视频| 青青草av在线播放| 国产精品高清亚洲| 亚洲av成人片色在线观看高潮| 日韩激情在线观看| 久久www视频| 精品国产美女| 好吊色欧美一区二区三区视频| 日韩一区二区三区在线免费观看 | av男人的天堂在线| 91精品国产福利| 天天操天天操天天操天天| 国产精品久久久久久久久动漫| 手机免费看av片| 另类调教123区| 日韩精品视频一区二区在线观看| 91亚洲一区| 久久久99国产精品免费| 国产一区二区三区视频在线| 日本一欧美一欧美一亚洲视频| 菠萝蜜视频国产在线播放| 亚洲男人天堂视频| 亚洲爆乳无码一区二区三区| 欧美日韩在线亚洲一区蜜芽| www.天天色| 亚洲精品久久7777| 91无套直看片红桃在线观看| 久久亚洲精精品中文字幕早川悠里| 久久6免费视频| 日韩二区在线观看| 国产美女网站在线观看| 综合五月婷婷| 宅男av一区二区三区| 国产一区网站| 欧美亚洲免费在线| 国产伦精品一区二区三区免费优势| 成人精品久久久| 99久久久国产精品免费调教网站| 668精品在线视频| 丁香高清在线观看完整电影视频 | 美女福利视频网| 久久免费午夜影院| 精品一区二区视频在线观看| 国产精品69久久久久水密桃| 色婷婷一区二区三区av免费看| 久久xxxx| 男女午夜激情视频| aⅴ色国产欧美| 国产原创popny丨九色| 在线日韩电影| 国产一区二区三区在线免费| 91精品国偷自产在线电影 | 99在线|亚洲一区二区| 一级性生活视频| 一区二区三区毛片免费| 国产精品亚洲天堂| 久久久五月天| 欧美少妇一区二区三区| 久久久久久久久99精品大| 一区二区三区四区视频在线观看| 欧洲美女日日| 在线观看成人av电影| 91偷拍一区二区三区精品| 在线免费观看一区二区三区| 图片区亚洲欧美小说区| 在线无限看免费粉色视频| 91精品一区二区三区综合在线爱 | 国产伦精品一区二区三区四区免费 | 亚洲国产精品日韩专区av有中文| 中文字幕欧美人与畜| 中文字幕一区二区三区在线视频| 亚洲激情免费视频| 欧美三级在线| 国产美女网站在线观看| 久久人人超碰| 日本免费色视频| 国产一区不卡在线| 亚洲精品无码一区二区| 99久久国产免费看| 性欧美一区二区| 一区在线观看免费| 久久久久亚洲AV成人| 亚洲成人av在线电影| 日本中文字幕第一页| 国产suv精品一区二区三区| 国内精品中文字幕| 污污在线观看| 91av在线播放视频| 国产精品麻豆成人av电影艾秋| 国产精品露脸av在线| 日韩黄色av| 欧美精品国产精品久久久| 色999日韩| 欧美精品久久久久久久久久久| 欧美专区一区二区三区| 中文字幕第22页| 99久久国产综合色|国产精品| 免费成人深夜天涯网站| 亚洲自拍偷拍综合| 波多野结衣一区二区三区在线| 欧美一区二区三区婷婷月色 | 国产一区二区三区在线看| 麻豆av在线免费看| 97成人超碰免| 91成人app| 蜜桃在线一区二区三区精品| 久久久久久久久99精品大| 免费av观看网址| 国内不卡的二区三区中文字幕| 中文成人无字幕乱码精品区| 国产精品传媒视频| 亚洲AV无码成人精品区东京热| 91精品在线一区二区| 桃花色综合影院| 日韩中文字幕视频在线| 亚洲精华液一区二区三区| 91久久国产自产拍夜夜嗨| 精品产国自在拍| 五月丁香综合缴情六月小说| 国产资源精品在线观看| 中文字幕成人动漫| 亚洲国产美女搞黄色| 韩国av中国字幕| 四虎影视成人精品国库在线观看| 亚洲aⅴ男人的天堂在线观看| 香蕉久久精品日日躁夜夜躁| 8x8ⅹ国产精品一区二区二区| 青青草国产精品97视觉盛宴| 麻豆国产精品一区| 一区二区三区 在线观看视频 | 免费观看30秒视频久久| 一级黄色片毛片| 亚洲美女在线一区| 一级黄在线观看| 国产亚洲美女久久| 波多野结衣高清视频| 亚洲日本在线观看视频| 欧美黑人巨大精品一区二区| av在线不卡精品| 欧美日韩一区二区三区在线视频| 欧美午夜电影在线观看 | 最新日韩一区| 久久久国产精品一区二区三区| 国产一区清纯| www日本在线观看| 亚洲免费视频中文字幕| 一炮成瘾1v1高h| 色av吧综合网| 欧美亚洲人成在线| 亚洲欧洲三级| 久久草av在线| www.黄色com| 欧美日韩精品专区| 日本激情在线观看| 国产日韩在线播放| 日韩国产一区二区| 色噜噜狠狠永久免费| 中文字幕乱码亚洲精品一区| 7799精品视频天天看| 亚洲丝袜一区在线| 日韩在线免费| 欧美日韩精品一区二区三区蜜桃 | 亚洲影视在线观看| 好吊色视频在线观看| 日韩丝袜情趣美女图片| 亚洲夜夜综合| 国产91精品入口17c| 日韩视频二区| 国产美女精品久久| 91精品办公室少妇高潮对白| 18视频免费网址在线观看| 成人免费在线视频网站| 国产一区欧美| 丰满少妇一区二区三区| 91福利精品第一导航| 日本中文字幕伦在线观看| 91精品在线观看视频| 国内成人在线| 国产成人av一区二区三区不卡| 欧美午夜在线一二页| 成人ww免费完整版在线观看| 国产精品免费一区二区三区四区| 在线亚洲免费| 人妻无码一区二区三区免费| 日韩天堂在线观看| 黑人巨大精品欧美一区二区桃花岛| 日韩中文一区| 国产乱人伦偷精品视频不卡| 久草精品视频在线观看| 国产午夜一区二区| 精品国产亚洲一区二区三区| 国产男女免费视频| 欧美国产欧美综合| 精品黑人一区二区三区在线观看 | 日韩经典在线观看| 亚洲色图综合久久| 日本成人精品| 欧美牲交a欧美牲交aⅴ免费真| 国产精品三级电影| 农村少妇久久久久久久| 国产精品福利在线观看| 欧美日韩国产成人精品| 久久久久久国产免费a片| 日韩一级片在线观看| 深夜成人影院| 亚洲人午夜精品| 亚洲 欧美 激情 另类| 国产玖玖精品视频| 一区二区精品| 一区二区视频免费看| 亚洲美女www午夜| 欧美高清hd| 国产精品天天av精麻传媒| 艳妇臀荡乳欲伦亚洲一区| eeuss影院www在线播放| 国产伦精品一区二区三区视频黑人|