精品欧美一区二区三区在线观看 _久久久久国色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技術棧公眾號

免费成人在线网站| 国产精品片aa在线观看| 亚洲国产精品视频| 久久久久se| 夜夜狠狠擅视频| 狠狠88综合久久久久综合网| 精品亚洲国产视频| 亚洲欧美aaa| 黄视频免费在线看| 1区2区3区国产精品| 国产精品一区视频| 亚洲最大成人av| 99在线观看免费视频精品观看| 有码中文亚洲精品| 欧美一级大片免费看| 自拍偷自拍亚洲精品被多人伦好爽 | 日本乱人伦a精品| 亚洲av熟女国产一区二区性色| 美女色狠狠久久| 亚洲国产另类精品专区| 亚洲欧美丝袜| 色综合久久网女同蕾丝边| 久久66热re国产| 日韩免费av片在线观看| 青娱乐在线视频免费观看| 精品国产一区探花在线观看| 日韩美女一区二区三区四区| 日本中文字幕高清| 伊人色综合一区二区三区影院视频 | 2019中文字幕在线| www.com.av| 精品国产精品久久一区免费式| 欧美一级在线视频| 538任你躁在线精品免费| 在线看片国产福利你懂的| 一区二区三区精品| 四虎精品欧美一区二区免费| 春暖花开成人亚洲区| 99re热这里只有精品视频| 91嫩草免费看| 国产美女www爽爽爽视频| 日韩电影在线一区二区| 欧亚精品在线观看| 波多野结衣国产| 一本久道久久久| 欧美精品久久久久a| 欧美日韩三级在线观看| 亚洲成av人片乱码色午夜| 日韩中文字幕不卡视频| 欧美成人短视频| 精品少妇av| 亚洲欧洲视频在线| 少妇真人直播免费视频| 精品在线观看入口| 亚洲欧美日韩天堂一区二区| 国产又黄又粗又猛又爽的视频| 成人性生交大片免费看中文视频 | 亚洲欧美色图| 久久综合88中文色鬼| 性色国产成人久久久精品| 久久精品高清| 久久国产精品久久国产精品| 日韩视频中文字幕在线观看| 亚洲最新色图| 欧美激情乱人伦| 精品国产免费观看| 久久综合九色| 一区二区三区不卡在线观看 | 日日鲁鲁鲁夜夜爽爽狠狠视频97 | 美女毛片在线看| 久久综合久久鬼色| 欧美一进一出视频| 日韩在线资源| 亚洲一区精品在线| 国产免费一区二区三区视频| 怡红院成人在线| 欧美在线观看视频一区二区三区| 日韩av在线中文| 国产麻豆一区二区三区| 亚洲国产美女精品久久久久∴| 人妻无码中文久久久久专区| 国产99久久精品一区二区300| 在线成人激情黄色| 国产免费无码一区二区视频| 亚洲精选国产| 国产精品吹潮在线观看| 国产特级aaaaaa大片| 成人激情免费电影网址| 欧洲在线视频一区| а天堂中文在线官网| 偷拍一区二区三区| 久久撸在线视频| 8x国产一区二区三区精品推荐| 日韩大片免费观看视频播放| www亚洲色图| 狠狠爱成人网| 国产日韩精品综合网站| 日日夜夜精品免费| 中文字幕电影一区| 丁香花在线影院观看在线播放| 综合毛片免费视频| 欧美一区二区三区人| 一级欧美一级日韩片| 久久精品av| 2019中文在线观看| av一级黄色片| 亚洲国产精品二十页| 无码 制服 丝袜 国产 另类| 高清在线一区| 日韩av网站大全| 国产精品白丝喷水在线观看| 噜噜噜在线观看免费视频日韩| 91九色国产社区在线观看| 日本在线视频1区| 一区二区三区鲁丝不卡| 色哟哟精品视频| 欧美三级午夜理伦三级在线观看| 久久精品视频在线播放| 人妻 日韩精品 中文字幕| 国产成人精品www牛牛影视| 亚洲欧美国产不卡| 激情开心成人网| 亚洲精品久久久久久下一站| 国产在线一卡二卡| 日韩av不卡一区二区| 久久久久久艹| av资源网在线播放| 日韩一区二区三区四区| 亚洲熟女少妇一区二区| 玖玖玖国产精品| 精品无码久久久久久久动漫| 三级福利片在线观看| 欧美精品一二三| 美国一级黄色录像| 玖玖玖国产精品| 欧美激情视频一区二区三区| hd国产人妖ts另类视频| 日韩美女主播在线视频一区二区三区| 黄色香蕉视频在线观看| 看片的网站亚洲| 亚洲日本japanese丝袜| 国产精品亚洲一区二区三区在线观看| 日韩精品在线影院| 免费看日韩毛片| 91亚洲大成网污www| 欧美深夜福利视频| 色天天色综合| 欧美一区在线直播| 偷拍自拍在线视频| 日韩欧美在线第一页| mm131美女视频| 水野朝阳av一区二区三区| 青青草原成人| av成人免费看| 久热爱精品视频线路一| 亚洲AV无码精品国产| 一区二区三区视频在线观看| 性一交一黄一片| 国产在线日韩| 激情五月综合色婷婷一区二区| ****av在线网毛片| 亚洲精品一区久久久久久| 亚洲精品中文字幕乱码三区91| 久久蜜桃一区二区| 视频二区在线播放| 91精品成人| 国产福利一区二区三区在线观看| 丁香花电影在线观看完整版| 亚洲第一网站男人都懂| 69成人免费视频| 中文字幕精品一区| 欧美色图校园春色| 国产一区二区三区久久久久久久久| 久久精品午夜一区二区福利| 日本精品不卡| 亚洲精品一区中文字幕乱码| 亚欧视频在线观看| 欧美国产综合色视频| www.日本久久| 中文高清一区| 亚洲欧洲久久| 97se亚洲国产一区二区三区| 8x拔播拔播x8国产精品| 69视频在线| 精品成人一区二区三区| 999视频在线| 亚洲青青青在线视频| 天堂www中文在线资源| 石原莉奈在线亚洲二区| 好吊色视频988gao在线观看| 欧美变态挠脚心| 国产欧美亚洲视频| av丝袜在线| 色婷婷综合成人av| 日本美女一级视频| 欧美日韩视频在线一区二区| 国产在线拍揄自揄拍| 欧美韩日一区二区三区| 视频免费在线观看| 久久精品久久综合| 午夜肉伦伦影院| 欧美在线播放| 日韩久久在线| 欧美a级网站| 91免费观看| 成人在线黄色| 91高清视频免费| 18+视频在线观看| 一区二区三区美女xx视频| 亚洲精品97久久中文字幕无码| 欧美亚洲综合久久| 国产精品一区二区6| 亚洲男帅同性gay1069| 日韩在线免费观看av| 成人在线视频一区二区| 成人亚洲免费视频| 玖玖玖国产精品| 国产淫片免费看| 国内精品久久久久国产盗摄免费观看完整版| 日本在线观看一区二区| 久久久久观看| 国产精品对白一区二区三区| 99久久这里有精品| 国产美女高潮久久白浆| 亚洲天堂av在线| 久久久久久有精品国产| 中文字幕伦理免费在线视频 | 亚洲国产成人精品久久久国产成人一区| 国产suv精品一区二区33| 亚洲一区二三区| 杨钰莹一级淫片aaaaaa播放| 国产精品视频免费看| av黄色在线免费观看| 99国产精品久久久久| 日本在线不卡一区二区| 国产精品一区二区在线播放| 91看片破解版| 六月丁香婷婷久久| 色片在线免费观看| 免费一级欧美片在线观看| 免费日韩视频在线观看| 欧美综合二区| avav在线看| 久久国产66| 日韩精品免费播放| 日本最新不卡在线| 污污动漫在线观看| 久久国产日韩欧美精品| 中文字幕第38页| 久久福利视频一区二区| 亚洲欧美日本一区二区三区| 久久精品国产99国产精品| 在线免费视频一区| 精品一区二区免费在线观看| 青青草久久伊人| 国模一区二区三区白浆| 日批视频在线看| fc2成人免费人成在线观看播放 | 中文字幕在线一区| 侵犯稚嫩小箩莉h文系列小说| 亚洲男人都懂的| 久久精品国产亚洲av麻豆色欲| 亚洲成人激情av| 三级视频在线观看| 日本韩国欧美在线| 91亚洲精品国偷拍自产在线观看 | 国产精品久久在线观看| 国产91在线播放精品| 成人午夜激情免费视频| 亚洲超碰在线观看| 精品在线视频一区二区| 精品日韩免费| 日韩人妻一区二区三区蜜桃视频| 亚洲国产导航| 国产美女三级视频| 奇米色777欧美一区二区| 三级性生活视频| 成人av网站大全| 精品人妻无码一区| 亚洲欧美日韩国产手机在线| 亚洲国产精品午夜在线观看| 日韩欧美在线中文字幕| 亚洲一级特黄毛片| 精品福利二区三区| 国产精品视频二区三区| 欧美另类在线播放| 亚洲www免费| 97久久精品午夜一区二区| 校园春色另类视频| 日韩人妻精品一区二区三区| 亚洲永久在线| 亚洲天堂av一区二区三区| 91视频.com| 午夜免费激情视频| 色综合久久精品| 国产成人精品无码高潮| 亚洲色图美腿丝袜| 欧美v亚洲v| 成人国产精品久久久久久亚洲| 好吊妞国产欧美日韩免费观看网站| 日韩福利在线| 在线播放亚洲| 五月天激情播播| 91免费观看视频在线| 国产va在线播放| 欧亚一区二区三区| 天天躁日日躁狠狠躁喷水| 日韩有码在线播放| 神马电影网我不卡| 国产成人一区二区三区免费看| 欧美综合久久| 亚欧无线一线二线三线区别| 国产精品一区久久久久| 黑人と日本人の交わりビデオ| 亚洲va欧美va天堂v国产综合| 国产精品毛片一区二区在线看舒淇 | 欧美暴力调教| 国产精品污www一区二区三区| 国产亚洲一区二区三区啪| 高清欧美精品xxxxx| 狠狠色狠狠色合久久伊人| 我不卡一区二区| 狠狠色噜噜狠狠狠狠97| 亚洲精品久久久久久无码色欲四季| 综合国产在线视频| 老司机2019福利精品视频导航| 国产精品一 二 三| 国产精品啊v在线| 久久黄色片网站| 国产精品天美传媒沈樵| 日本三级一区二区三区| 亚洲欧美国产精品| 九色porny丨入口在线| 国产高清精品一区| 国自产拍偷拍福利精品免费一| 伊人成人免费视频| 国产精品国产成人国产三级| 中文字幕精品一区二区精| 亚洲欧美中文在线视频| 范冰冰一级做a爰片久久毛片| 久久99精品久久久久久久青青日本 | 国产成人av免费在线观看| 欧美日韩一区二区三区高清| 第一视频专区在线| 国产精品成人v| 欧美激情在线精品一区二区三区| 人妻精品无码一区二区三区| 9色porny自拍视频一区二区| 日本亚洲欧美在线| 亚洲精品成人久久| 2022成人影院| 日韩精品伦理第一区| 蜜臀99久久精品久久久久久软件| 538精品视频| 欧美久久久一区| 制服丝袜中文字幕在线| 国产精品久久波多野结衣| 亚洲大胆视频| 粉嫩av蜜桃av蜜臀av| 在线看不卡av| 在线观看免费黄色| 91精品视频观看| 欧美黄色大片网站| 欧美做受高潮中文字幕| 五月天中文字幕一区二区| 视频在线观看你懂的| 91精品国产高清| 国产精品片aa在线观看| 91精品久久久久久久久青青| 精品国产青草久久久久96| 亚洲成人av在线| 电影天堂国产精品| 在线视频精品一区| 国产成人亚洲精品青草天美| 日韩久久精品视频| 亚洲最新av网址| 日韩精品三级| 国产a级一级片| 中文字幕中文字幕在线一区| www.97超碰| 日本不卡高字幕在线2019| 欧美a级片视频| 亚洲免费观看在线| 色网综合在线观看| 在线观看操人| 久久亚洲高清| 国产专区综合网| 成人精品免费在线观看| www.日韩不卡电影av| 国产女人18毛片水真多18精品| 狠狠操精品视频| 亚洲综合久久av| 在线视频三区| 极品校花啪啪激情久久| 麻豆精品在线播放| 香蕉免费毛片视频| 精品国产依人香蕉在线精品| 国产成人av毛片| 污视频在线观看免费网站| 色素色在线综合|