【原創】Ftrace使用及實現機制

Ftrace使用及實現機制

版權聲明:本文爲本文爲博主原創文章,轉載請註明出處 https://www.cnblogs.com/wsg1100 如有錯誤,歡迎指正。

一、使用ftrace

ftrace 即function tracer,最初是用來 trace 內核中的函數,在 2008 年的時候被合入了 Linux 內核,當時的版本是 Linux2.6.x。現在 ftrace 的功能不僅僅是function tracer,更加豐富了,可觀測內核很多信息。本文分爲兩個部分,第一部分介紹ftrace的使用,大部分來源於Linux內核ftrace文檔,第二部分介紹ftrace的實現原理。


1.掛載

ftrace 的所有操作在 tracefs 這個虛擬文件系統中完成, tracefs 的掛載點在 /sys/kernel/debug/tracing 下:

# cat /proc/mounts | grep tracefs
tracefs /sys/kernel/debug/tracing tracefs rw,relatime 0 0

如果沒有自動掛載,則需要手動掛載:

# mount -t tracefs nodev /sys/kernel/debug/tracing

掛載後/sys/kernel/debug/tracing 目錄下的文件如下:

# cd /sys/kernel/debug/tracing
# ls
available_events            events                    README                  set_graph_function  trace_marker_raw
available_filter_functions  free_buffer               saved_cmdlines          set_graph_notrace   trace_options
available_tracers           function_profile_enabled  saved_cmdlines_size     snapshot            trace_pipe
buffer_percent              hwlat_detector            saved_tgids             stack_max_size      trace_stat
buffer_size_kb              instances                 set_event               stack_trace         tracing_cpumask
buffer_total_size_kb        kprobe_events             set_event_notrace_pid   stack_trace_filter  tracing_max_latency
current_tracer              kprobe_profile            set_event_pid           synthetic_events    tracing_on
dynamic_events              max_graph_depth           set_ftrace_filter       timestamp_mode      tracing_thresh
dyn_ftrace_total_info       options                   set_ftrace_notrace      trace               uprobe_events
enabled_functions           per_cpu                   set_ftrace_notrace_pid  trace_clock         uprobe_profile
error_log                   printk_formats            set_ftrace_pid          trace_marker

2. 關鍵文件介紹

tracefs 虛擬文件系統下的文件操作和我們常用的 Linux proc 和 sys 虛擬文件系統的操作差不多。通過對某個文件的 echo 操作,我們可以向內核的 ftrace 系統發送命令,然後 cat 某個文件得到 ftrace 的返回結果。

/sys/kernel/debug/tracing 目錄各文件及作用如下,根據具體場景使用:

current_tracer

設置或顯示當前tracer配置。

available_tracers

內核支持的tracer配置,內核編譯時配置,將裏面的配置寫入current_tracer即設置設置當前trace功能。

tracing_on

設置是否將trace寫入tracer緩衝區,寫0到該文件禁用trace寫入緩衝區,寫1啓用,無論寫0還是寫1,內核trace都在運行,都有trace開銷。

trace:

該文件以人類可讀的格式保存跟蹤的輸出。注意,正在讀取(打開)此文件時會暫時禁用跟蹤。

trace_pipe:

輸出與“trace”文件相同,但此文件應使用實時跟蹤進行流式處理。從此文件讀取將阻塞,直到檢索到新數據。 與“trace”文件不同,此文件是消費者。 一旦從該文件讀取數據,就會消耗該數據。 “trace”文件是靜態的,如果跟蹤器沒有添加更多數據,則每次讀取時都會顯示相同的信息。 此文件在讀取時不會禁用跟蹤。

trace_options:

此文件允許用戶控制上述輸出文件之一中顯示的數據量。

options:

這是一個目錄,其中包含每個可用跟蹤選項的文件(也在trace_options中)。 也可以通過將“1”或“0”分別寫入帶有選項名稱的相應文件來設置或清除選項。

tracing_max_latency:

一些跟蹤器記錄了最大延遲。 例如,禁用中斷的最長時間。此文件中保存最長時間。 最大跟蹤也將得到優化,並通過trace顯示。 如果延遲大於此文件中的值(以微秒爲單位),則只會記錄新的最大跟蹤。
通過echo寫入一個時間值,除非它大於此文件中的時間,否則不會記錄任何延遲。

tracing_thresh:

只要延遲大於此文件中的數字,某些延遲跟蹤器就會記錄跟蹤。僅當文件包含大於0的數字時纔有效。(以微秒爲單位)

buffer_size_kb

這將設置或顯示每個CPU緩衝區大小。 默認情況下,跟蹤緩衝區的大小與每個CPU的大小相同。 顯示的數字是CPU緩衝區的大小,而不是所有緩衝區的總大小。 跟蹤緩衝區分配在一個內存頁面(內核用於分配的內存塊,通常爲4 KB)。

buffer_total_size_kb:

這將顯示所有跟蹤緩衝區的總大小。

free_buffer:

用於釋放緩衝區,讓一個跟蹤進程打開這個文件,當進程退出時,該文件的文件描述符將被關閉,這樣,環形緩衝區將被“釋放”。

tracing_cpumask:

這是一個掩碼,用於指定跟蹤的CPU。格式是表示CPU的十六進制字符串。

set_ftrace_filter:

set_ftrace_notrace:

這與set_ftrace_filter的效果相反。 此處添加的任何功能都不會被跟蹤。 如果set_ftrace_filterset_ftrace_notrace中都存在函數,則不會跟蹤該函數。

set_ftrace_pid:

讓函數跟蹤器僅跟蹤其PID在此文件中列出的線程。如果設置了“function-fork”選項,那麼當該文件中列出PID的任務分fork時,子進程的PID將自動添加到該文件中,並且該子進程將被跟蹤。 此選項還將導致退出的任務的PID從文件中刪除。

set_event_pid:

此文件中列出的需要event跟蹤的任務PID。注意,sched_switchsched_wake_up還將跟蹤此文件中列出的事件。如果要跟蹤此文件中PID的任務的子進程,還需要啓用“event-fork”選項。 任務退出時該文件內的PID將被刪除。

set_graph_function:

函數圖形跟蹤器將跟蹤此文件中列出的函數及其調用的函數。 (有關詳細信息,請參閱“動態ftrace”部分)。

set_graph_notrace:

set_graph_function類似,但在函數被命中時將禁用函數圖形跟蹤,直到它退出函數。這使得可以忽略由特定函數調用的跟蹤函數。

max_graph_depth:

與函數圖形跟蹤器一起使用。 這是它將追溯到函數的最大深度。 將其設置爲值1將僅顯示從用戶空間調用的第一個內核函數。

available_filter_functions:

這列出了ftrace已處理並可跟蹤的功能。這些是可以傳遞給“set_ftrace_filter”或“set_ftrace_notrace”的函數名稱。 (有關詳細信息,請參閱下面的“動態ftrace”部分。)

dyn_ftrace_total_info:

此文件用於調試目的。 已轉換爲nops且可以跟蹤的函數數。

enabled_functions:

function_profile_enabled:

設置後,它將啓用具有功能跟蹤器或功能圖形跟蹤器的所有功能。 它將保留被調用函數數量的直方圖,如果配置了函數圖形跟蹤器,它還將跟蹤這些函數所花費的時間。 直方圖內容可以顯示在文件中: 
trace_stats/function<cpu>(function0,function1等)。

trace_stats:

包含不同跟蹤統計信息的目錄。

kprobe_events:

啓用動態跟蹤點。 請參閱kprobetrace.txt。

kprobe_profile:

動態跟蹤點統計信息。 請參閱kprobetrace.txt。

printk_formats:

這適用於讀取原始格式文件的工具。 如果環形緩衝區中的事件引用了一個字符串,則只有指向該字符串的指針被記錄到緩衝區而不是字符串本身。 這可以防止工具知道該字符串是什麼。 此文件顯示字符串的字符串和地址,允許工具將指針映射到字符串。

saved_cmdlines:

除非事件專門保存任務comm,否則只有任務的pid記錄在跟蹤事件中。 Ftrace對通信進行pid映射緩存,以嘗試顯示事件的通信。 如果未列出comm的pid,則輸出中將顯示“<...>”。
如果選項“record-cmd”設置爲“0”,則在錄製期間不會保存任務通信。 默認情況下,它啓用.

saved_cmdlines_size:

默認情況下,會保存128個通訊(請參閱上面的“saved_cmdlines”)。 要增加或減少緩存的通信量,請將要緩存的通信數回顯到此文件中。

saved_tgids:

如果設置了“record-tgid”選項,則在每個調度上下文切換時,任務的任務組ID將保存在將線程的PID映射到其TGID的表中。 默認情況下,禁用“record-tgid”選項。

snapshot:

這將顯示“Snapshot”緩衝區,還允許用戶拍攝當前運行跟蹤的快照。 有關詳細信息,請參閱下面的“Snapshot”部分。

stack_max_size:

激活堆棧跟蹤器時,將顯示它遇到的最大堆棧大小。請參閱下面的“堆棧跟蹤”部分。

stack_trace:

這將顯示激活堆棧跟蹤器時遇到的最大堆棧的堆棧跟蹤跟蹤。請參閱下面的“堆棧跟蹤”部分。

stack_trace_filter:

這與“set_ftrace_filter”類似,但它限制了堆棧跟蹤器將檢查的功能。

trace_clock:

只要將事件記錄到環形緩衝區中,就會添加“timestamp”。這個時間戳來自指定的時鐘。默認情況下,ftrace使用“local”時鐘。這個時鐘非常快且嚴格按照cpu計算,但在某些系統上,它可能與其他CPU相比不是單調的。換句話說,本地時鐘可能與其他CPU上的本地時鐘不同步。

通常用於跟蹤的時鐘如下(帶有方括號的時鐘是有效的。):

# cat trace_clock 
[local] global counter uptime perf mono mono_raw boot x86-tsc

local:默認時鐘,但可能不在CPU之間同步.

global:此時鐘與所有CPU同步,但可能比本地時鐘慢一點。

counter:這根本不是時鐘,而是一個原子計數器。它逐個計數,但與所有CPU同步。當您需要準確瞭解不同CPU上相互發生的訂單事件時,這非常有用。

uptime 這使用jiffies計數器,時間戳相對於啓動後的時間。

perf: 這使得ftrace使用perf使用的相同時鐘。最終perf將能夠讀取ftrace緩衝區,這將有助於交錯數據。

x86-tsc:架構可以定義自己的時鐘。例如,x86在此使用自己的TSC週期時鐘。

ppc-tb:這使用powerpc時基寄存器值。這在CPU之間是同步的,並且如果已知tb_offset,還可以用於跨管理程序/來賓關聯事件。

mono:這使用快速單調時鐘(CLOCK_MONOTONIC),它是單調遞增的,可以進行NTP速率調整。

mono_raw:這是原始的單調時鐘(CLOCK_MONOTONIC_RAW),它是單調遞增的,但不受任何速率調整和滴答,速率與硬件時鐘源相同。

boot:這是啓動時鐘(CLOCK_BOOTTIME),基於快速單調時鐘,但也佔用了掛起時間。由於時鐘訪問被設計用於在掛起路徑中進行跟蹤,因此如果在更新快速單聲道時鐘之前計算掛起時間之後訪問時鐘,則可能產生一些副作用。在這種情況下,時鐘更新似乎比通常情況稍早發生。

要設置時鐘,只需將時鐘名稱回顯到此文件中即可。

echo global> trace_clock

trace_marker:
這是一個非常有用的文件,用於將用戶空間與內核中發生的事件同步。 將字符串寫入此文件將寫入ftrace緩衝區。在應用程序中,在應用程序啓動時打開此文件,並僅引用文件的文件描述符非常有用。

	void trace_write(const char *fmt, ...)
	{
		va_list ap;
		char buf[256];
		int n;

		if (trace_fd < 0)
			return;

		va_start(ap, fmt);
		n = vsnprintf(buf, 256, fmt, ap);
		va_end(ap);

		write(trace_fd, buf, n);
	}

//	開始:
		trace_fd = open("trace_marker", WR_ONLY);

trace_marker_raw:

這與上面的trace_marker類似,但是用於將二進制數據寫入其中,其中可以使用工具來解析來自trace_pipe_raw的數據。

uprobe_events:

在程序中添加動態跟蹤點。 請參閱uprobetracer.txt

uprobe_profile:

Uprobe統計。 請參閱uprobetrace.txt

instances:

這是一種製作多個跟蹤緩衝區的方法,可以在不同的緩衝區中記錄不同的事件。 請參閱下面的“實例”部分。

events:

這是跟蹤事件目錄。 它包含已編譯到內核中的事件跟蹤點(也稱爲靜態跟蹤點)。 它顯示了存在哪些事件跟蹤點以及它們如何按系統分組。 各種級別都有“啓用”文件,可以在寫入“1”時啓用跟蹤點。有關更多信息,請參閱events.txt。

set_event:

通過在事件中回顯到此文件,將啓用該事件。有關更多信息,請參閱events.txt。

available_events:

可以在跟蹤中啓用的事件列表,有關更多信息,請參閱events.txt。

hwlat_detector:

硬件延遲檢測器的目錄。請參閱下面的“硬件延遲檢測器”部分。

per_cpu:

這是一個包含跟蹤per_cpu信息的目錄。

per_cpu/cpu0/buffer_size_kb:

ftrace緩衝區定義爲per_cpu。 也就是說,每個CPU都有一個單獨的緩衝區,允許以原子方式完成寫入,並且不受cache bouncing的影響。 這些緩衝區可能有不同大小的緩衝區 此文件類似於buffer_size_kb文件,但它僅顯示或設置特定CPU的緩衝區大小。 (這裏是cpu0)。

per_cpu/cpu0/trace:

這類似於“跟蹤”文件,但它只顯示特定於CPU的數據。 如果寫入,它只清除特定的CPU緩衝區。

per_cpu/ CPU0/ trace_pipe

這類似於“trace_pipe”文件,並且是一個消耗讀取,但它只顯示(和使用)特定於CPU的數據。

per_cpu/ CPU0/ trace_pipe_raw

對於可以解析ftrace環形緩衝區二進制格式的工具,trace_pipe_raw文件可用於直接從環形緩衝區中提取數據。 通過使用splice()系統調用,緩衝區數據可以快速傳輸到文件或服務器正在收集數據的網絡。與trace_pipe一樣,這是一個消費者,其中多次讀取將始終產生不同的數據。

per_cpu/ CPU0/snapshot

這類似於主“快照”文件,但只會對當前CPU(如果支持)進行快照。 它僅顯示給定CPU的快照內容,如果寫入,則僅清除此CPU緩衝區。

per_cpu/cpu0/snapshot_raw:

與trace_pipe_raw類似,但將從給定CPU的快照緩衝區中讀取二進制格式。

per_cpu/cpu0/stats:
這會顯示有關環形緩衝區的某些統計信息:

entries:仍在緩衝區中的事件數。

overrun: 緩衝區已滿時由於覆蓋而丟失的事件數。

commit overrun:應該始終爲零。
如果在嵌套事件(環形緩衝區重入)中發生瞭如此多的事件,則它會被設置,它會填充緩衝區並開始丟棄事件。
bytes:字節實際讀取(未覆蓋)。
oldest event ts:緩衝區中最舊的時間戳
now ts:當前時間戳
dropped events:由於覆蓋選項關閉而導致事件丟失。
read events:讀取的事件數.

3.current_tracer列表

以下是可配置的current_tracer列表:

function
函數調用跟蹤器來跟蹤所有內核函數。

**function_graph **
與函數跟蹤器類似,除了函數跟蹤器探測其條目上的函數,而函數圖形跟蹤器跟蹤函數的進入和退出。 然後它提供了繪製類似於C代碼源的函數調用圖的能力。

blk
塊跟蹤器。 blktrace用戶應用程序使用的跟蹤器。

hwlat
硬件延遲跟蹤器用於檢測硬件是否產生任何延遲。 請參閱下面的“硬件延遲檢測器”部分。

irqsoff
跟蹤禁用中斷的區域並以最長的最大延遲保存跟蹤。請參閱tracing_max_latency。 記錄新的最大值時,它將替換舊的跡線。 最好在啓用延遲格式選項的情況下查看此跟蹤,這在選擇跟蹤器時會自動發生。

preemptoff
與irqsoff類似,但跟蹤並記錄禁用搶佔的時間。

preemptirqsoff
與irqsoff和preemptoff類似,但跟蹤並記錄禁用irqs和/或搶佔的最大時間。

wakeup
跟蹤並記錄最高優先級任務在被喚醒後進行調度所需的最大延遲。按照普通開發人員的預期跟蹤所有任務。

wakeup_rt
跟蹤並記錄僅執行RT任務所需的最大延遲(如當前“喚醒”所做的那樣)。 這對那些對RT任務的喚醒時間感興趣的人很有用。

wakeup_dl
跟蹤並記錄SCHED_DEADLINE任務被喚醒所需的最大延(如wakeupwakeup_rt)。

mmiotrace
用於跟蹤二進制模塊的特殊跟蹤器。 它將跟蹤模塊對硬件的所有調用。 它所寫的所有內容以及從I / O中讀取的內容。

branch
在跟蹤內核中可能/不可能的調用時,可以配置此跟蹤器。 它將追蹤一個可能的和不太可能的分支何時被擊中,以及它在預測正確時是否正確。

nop
這是“無痕跡”的示蹤劑。 要從跟蹤中刪除所有跟蹤器,只需將“nop”寫道到current_tracer

4.trace輸出示例

4.1 trace輸出格式

下面簡單瞭解下ftrace的使用。ftrace的輸出結果都可以通過 cat trace 這個命令得到,在缺省狀態下 ftrace 的 tracer 是 nop,也就是 ftrace 什麼都不做。因此,我們從cat trace中也看不到別的,只是顯示了 trace 輸出格式。

root@work-machine:/sys/kernel/debug/tracing# cat trace
# tracer: nop
#
# entries-in-buffer/entries-written: 0/0   #P:8
#
#                                _-----=> irqs-off
#                               / _----=> need-resched
#                              | / _---=> hardirq/softirq
#                              || / _--=> preempt-depth
#                              ||| / _-=> migrate-disable
#                              |||| /     delay
#           TASK-PID     CPU#  |||||  TIMESTAMP  FUNCTION
#              | |         |   |||||     |         |

這裏以function tracer爲例,function tracer 只是 ftrace 最基本的功能,是我們平常調試 Linux 內核問題時最常用到的功能,執行 echo function > current_tracer 來告訴 ftrace,我要啓用 function tracer.

root@work-machine:/sys/kernel/debug/tracing# cat current_tracer
nop
root@work-machine:/sys/kernel/debug/tracing# cat available_tracers
hwlat blk mmiotrace function_graph wakeup_dl wakeup_rt wakeup function nop
root@work-machine:/sys/kernel/debug/tracing# echo function > current_tracer
root@work-machine:/sys/kernel/debug/tracing# cat current_tracer
function

在啓動了 function tracer 之後,我們再查看一下 trace 的輸出。這時候我們就會看到大量的輸出,每一行的輸出就是當前內核中被調用到的內核函數。

root@work-host:/sys/kernel/debug/tracing# cat trace
# tracer: function
#
# entries-in-buffer/entries-written: 358096/10552611   #P:8
#
#                                _-----=> irqs-off
#                               / _----=> need-resched
#                              | / _---=> hardirq/softirq
#                              || / _--=> preempt-depth
#                              ||| / _-=> migrate-disable
#                              |||| /     delay
#           TASK-PID     CPU#  |||||  TIMESTAMP  FUNCTION
#              | |         |   |||||     |         |
          <idle>-0       [000] *.... 46719.316253: handle_irq_pipelined_prepare <-arch_pipeline_entry
          <idle>-0       [000] *.~.. 46719.316289: arch_handle_irq <-arch_pipeline_entry
          <idle>-0       [000] *.~.. 46719.316289: generic_pipeline_irq_desc <-arch_handle_irq
          <idle>-0       [000] *.~.. 46719.316289: handle_fasteoi_irq <-generic_pipeline_irq_desc
          <idle>-0       [000] *.~.. 46719.316290: irq_may_run <-handle_fasteoi_irq
          <idle>-0       [000] *.~.. 46719.316290: handle_oob_irq <-handle_fasteoi_irq
          <idle>-0       [000] *.~.. 46719.316290: mask_irq.part.0 <-handle_fasteoi_irq
          <idle>-0       [000] *.~.. 46719.316290: mask_ioapic_irq <-mask_irq.part.0
          <idle>-0       [000] *.~.. 46719.316294: io_apic_sync <-mask_ioapic_irq
......

第2行標題及tracer的配置爲function (跟蹤函數)

第4行 顯示緩衝區中的事件數以及寫入的條目總數。\(358096/10552611\),由於緩衝區填滿而丟失的條目數(\(10552611- 358096=10,194,515\)事件丟失),接着是處理器數量:8

下面是記錄的事件的內容:任務名稱“",它的PID爲:0,運行在”000“號CPU上,後面是延遲的時間戳。格式:<秒>.<微秒>,跟蹤到函數handle_irq_pipelined_prepare()及父函數arch_pipeline_entry(),時間戳是進入handle_irq_pipelined_prepare()時的時間。

4.2 Latency trace輸出格式

root@work-host:/sys/kernel/debug/tracing# cat trace
# tracer: irqsoff
#
# irqsoff latency trace v1.1.5 on 4.4.182
# --------------------------------------------------------------------
# latency: 2674 us, #28/28, CPU#6 | (M:preempt VP:0, KP:0, SP:0 HP:0 #P:8)
#    -----------------
#    | task: compiz-3483 (uid:1000 nice:0 policy:0 rt_prio:0)
#    -----------------
#  => started at: __wake_up
#  => ended at:   __wake_up
#
#
#                  _------=> CPU#            
#                 / _-----=> irqs-off        
#                | / _----=> need-resched    
#                || / _---=> hardirq/softirq 
#                ||| / _--=> preempt-depth   
#                |||| /     delay            
#  cmd     pid   ||||| time  |   caller      
#     \   /      |||||  \    |   /         
  compiz-3483    6d.s1    0us : _raw_spin_lock_irqsave <-__wake_up
  compiz-3483    6d.s1    0us : preempt_count_add <-_raw_spin_lock_irqsave
	......
  compiz-3483    6d.s3    8us#: __x2apic_send_IPI_mask <-x2apic_send_IPI_mask
  compiz-3483    6d.s3 2669us : ttwu_stat <-try_to_wake_up
  compiz-3483    6d.s3 2671us : __rcu_read_lock <-ttwu_stat
  compiz-3483    6d.s3 2671us : __rcu_read_unlock <-ttwu_stat
  compiz-3483    6d.s3 2672us : _raw_spin_unlock_irqrestore <-try_to_wake_up
  compiz-3483    6d.s3 2673us : preempt_count_sub <-_raw_spin_unlock_irqrestore
  compiz-3483    6d.s2 2674us : _raw_spin_unlock_irqrestore <-__wake_up
  compiz-3483    6d.s2 2674us : _raw_spin_unlock_irqrestore <-__wake_up
  compiz-3483    6d.s2 2675us+: trace_hardirqs_on <-__wake_up
  compiz-3483    6d.s2 2727us : <stack trace>
 => rcu_gp_kthread_wake
 => note_gp_changes
 => rcu_process_callbacks
 => __do_softirq
 => irq_exit
 => smp_apic_timer_interrupt
 => apic_timer_interrupt
 => __wake_up_sync_key
 => sock_def_readable
 => unix_stream_sendmsg
 => sock_sendmsg
 => sock_write_iter
 => do_iter_readv_writev
 => do_readv_writev
 => vfs_writev
 => SyS_writev
 => entry_SYSCALL_64_fastpat

第2行tracer的配置爲irqsoff,tracer版本以及內核版本
下面一行給出最大延遲latency:2674us, 跟蹤條目和總數\(28/28\),VP,KP,SP和HP始終爲零,保留供以後使用。#P是在線CPU的數量(#P:8)。

延遲發生時運行的任務:compiz,任務PID:3483
分別禁用和啓用中斷導致延遲的:開始位置:__wake_up; 結束位置:__wake_up

內容及含義:

cmd:跟蹤中進程的名稱

pid:該進程的PID

CPU#:這個進程運行在這個CPU上

irq-off:
'd'中斷禁用,'.'其它。注意:如果架構不支持某種方式讀取irq標誌變量,在這裏會打印'X'

need-resched:

'N' TIF_NEED_RESCHEDPREEMPT_NEED_RESCHED兩者都置位

'n' 只有TIF_NEED_RESCHED設置

'p' 只有PREEMPT_NEED_RESCHED設置

'.' 其它

hardirq/softirq:

'Z' --NMI發生在硬件中斷內部

'z' --NMI運行

**'H' **--在一個softirq內發生了硬件中斷。

**'h' **--硬中斷運行

**'s' **--軟中斷運行

'.'- -正常上下文

preempt-depth:
preempt_disable的級別
以上內容對內核開發人員來說最有意義。

time:
啓用 latency-format 選項時,跟蹤文件輸出包括相對於跟蹤開始的時間戳。 這與禁用延遲格式時的輸出不同,後者包括絕對時間戳。

delay:

不同時間戳前後的差異,主要爲了容易找到前後時間差大的。
'$' - 超過1秒

'@' - 大於100ms

'*' - 大於10ms

'#' - 大於1000us

'!' - 大於100us

'+' - 大於10us

' ' - 小於等於10us

其餘部分與'trace'文件相同(4.1 trace輸出格式)。
請注意,延遲跟蹤器通常以反向跟蹤結束,以便輕鬆找到延遲發生的位置。

4.3 trace_options

trace_options文件(或options目錄)用於控制在跟蹤輸出中打印的內容,要查看哪些有效,cat這個文件:

root@work-host:/sys/kernel/debug/tracing# cat trace_options
print-parent
nosym-offset
nosym-addr
noverbose
noraw
nohex
nobin
noblock
trace_printk
annotate
nouserstacktrace
nosym-userobj
noprintk-msg-only
context-info
nolatency-format
record-cmd
norecord-tgid
overwrite
nodisable_on_free
irq-info
markers
noevent-fork
nopause-on-trace
hash-ptr
function-trace
nofunction-fork
nodisplay-graph
nostacktrace
notest_nop_accept
notest_nop_refuse

要禁用某一個options,將這這一項前添加no,然後echo到trace_options

echo noprint-parent > trace_options

啓用options:

echo sym-offset > trace_options

以下是可用options及含義:

  • print-parent: 在函數跟蹤上,顯示調用(父)函數以及正在跟蹤的函數,如。
#echo print-parent > trace_options
	bash-4000  [01]  1477.606694: simple_strtoul <-kstrtoul
#echo noprint-parent > trace_options
   	bash-4000  [01]  1477.606694: simple_strtoul
  • sym-offset:不僅顯示函數名稱,還顯示函數中的偏移量:
#echo sym-offset > trace_options
	<idle>-0       [000] d.h.. 50029.070372: irq_to_desc+0x0/0x20 <-do_irq_inband+0x10/0x30
#echo nosym-offset > trace_options
	<idle>-0       [000] d.h.. 50029.070372: irq_to_desc <-do_irq_inband
  • sym-addr: 這也將顯示函數地址和函數名稱,地址是個準確的地址。
#echo sym-addr > trace_options
<idle>-0       [001] d.h.. 50064.441407: irq_to_desc <ffffffff8db023e0> <-do_irq_inband <ffffffff8da6fc00>
#echo nosym-addr > trace_options
<idle>-0       [000] d.h.. 50029.070372: irq_to_desc <-do_irq_inband
  • verbose: 這將在啓用延遲格式選項時處理跟蹤文件。
	 bash  4000 1 0 00000000 00010a95 [58127d26] 1720.415ms (+0.000ms): simple_strtoul (kstrtoul)
  • raw:這將顯示原始數字。 此選項最適用於可以比原始數據更好地轉換原始數據的用戶應用
    程序。
  • hex: 與raw類似,但數字將採用十六進制格式。
  • bin: 這將打印出原始二進制格式。
  • block: 設置後,讀取trace_pipe將不會在輪詢時阻塞
  • trace_printk: 可以禁止trace_printk()寫入緩衝區。
  • annotate: 當CPU緩衝區已滿並且一個CPU緩衝區最近有很多事件時,有時會讓人感到困惑,因此更短的時間框架,另一個CPU可能只有一些事件,這使得它具有較舊的事件。 報告跟蹤時,它首先顯示最早的事件,並且可能看起來只有一個CPU運行(具有最早事件的CPU)。 設置annotate選項後,將在新的CPU緩衝區啓動時顯示:
	<idle>-0     [001] dNs4 21169.031481: wake_up_idle_cpu <-add_timer_on
	<idle>-0     [001] dNs4 21169.031482: _raw_spin_unlock_irqrestore <-add_timer_on
	<idle>-0     [001] .Ns4 21169.031484: sub_preempt_count <-_raw_spin_unlock_irqrestore
##### CPU 2 buffer started ####
	<idle>-0     [002] .N.1 21169.031484: rcu_idle_exit <-cpu_idle
	<idle>-0     [001] .Ns3 21169.031484: _raw_spin_unlock <-clocksource_watchdog
	<idle>-0     [001] .Ns3 21169.031485: sub_preempt_count <-_raw_spin_unlock
  • userstacktrace: 此選項可更改跟蹤。 它在每個跟蹤事件之後記錄當前用戶空間線程的堆棧跟蹤。

  • sym-userobj: 啓用用戶堆棧跟蹤時,查找該地址所屬的對象,並打印相對地址。 這在ASLR打開時特別有用,不然在應用程序不運行後,就沒有機會將地址解析爲object/file/line
    讀取trace,trace_pipe時執行查找。 例:

a.out-1623  [000] 40874.465068: /root/a.out[+0x480] <-/root/a.out[+0x494] <- /root/a.out[+0x4a8] <- /lib/libc-2.7.so[+0x1e1a6]
  • printk-msg-only : 設置後,trace_printk()將僅顯示格式而不顯示其參數(如果使用trace_bprintk()或trace_bputs()來保存trace_printk())。

  • context-info-- 僅顯示事件數據。 隱藏通信,PID,時間戳,CPU和其他有用數據。

  • latency-format: 此選項可更改跟蹤輸出。 啓用後,跟蹤會顯示有關延遲的其他信息,如“延遲跟蹤格式”中所述。

  • record-cmd: 啓用任何事件或跟蹤器後,將在sched_switch跟蹤點中啓用掛鉤,以使用映射的pids和comms填充comm緩存。 但這可能會導致一些開銷,如果您只關心pid而不關心任務的名稱,則禁用此選項可以降低跟蹤的影響。 請參閱“saved_cmdlines”。

  • record-tgid:`啓用任何事件或跟蹤器時,將在sched_switch跟蹤點中啓用hook,以填充映射到pids的映射線程組ID(TGID)的緩存。 請參閱“saved_tgids”。

  • overwrite:這可以控制跟蹤緩衝區已滿時發生的情況。 如果爲“1”(默認值),則丟棄並覆蓋最舊的事件。 如果爲“0”,則丟棄最新事件。 (有關溢出和丟棄的信息,請參閱per_cpu/cpu0/stats)

  • disable_on_free 當free_buffer關閉時,跟蹤將停止(tracing_on設置爲0)。

  • irq-info 顯示中斷,搶佔計數,需要重新設置數據。 禁用時,trace如下所示:

# tracer: function
#
# entries-in-buffer/entries-written: 144405/9452052   #P:4
#
#           TASK-PID   CPU#      TIMESTAMP  FUNCTION
#              | |       |          |         |
          <idle>-0     [002]  23636.756054: ttwu_do_activate.constprop.89 <-try_to_wake_up
          <idle>-0     [002]  23636.756054: activate_task <-ttwu_do_activate.constprop.89
          <idle>-0     [002]  23636.756055: enqueue_task <-activate_task
  • markers 設置後,trace_marker是可寫的(僅限root)。 禁用時,trace_marker將在寫入時出現EINVAL錯誤。
  • event-fork 設置後,具有set_event_pid中列出的PID的任務將在這些任務fork時將其子進程的PID添加到set_event_pid。 此外,當set_event_pid中具有PID的任務退出時,它們的PID將從文件中刪除。
  • function-trace 如果啓用此選項,則latency跟蹤器將啓用函數跟蹤(默認)。 禁用時,latency跟蹤器不跟蹤函數。 這樣可以在執行延遲測試時降低跟蹤器的開銷。
  • function-fork函數跟蹤時跟蹤子進程。
  • display-graph latency後,延遲跟蹤器(irqsoff,wakeup等)將使用函數圖形跟蹤而不是函數跟蹤。`
  • stacktrace 設置後,在記錄任何跟蹤事件後記錄堆棧跟蹤`
  • branch 使用跟蹤器啓用分支跟蹤。這使分支跟蹤器與當前設置的跟蹤器一起使用。 使用nop跟蹤器啓用此功能與啓用branch跟蹤器相同。

注意:某些跟蹤器有自己的選項。 它們僅在trace處於活動狀態時出現在此文件中。 它們始終顯示在options目錄中。

4.4 以下是每個tracer選項

1.function tracer的選項

func_stack_trace--設置後,記錄的每個功能之後記錄堆棧跟蹤。 注意! 在啓用此功能之前限制記錄的函數,使用“set_ftrace_filter”否則系統性能將嚴重降低。 記住在清除功能過濾器之前禁用此選項。

2.function_graph tracer的選項:
由於function_graph跟蹤器的輸出略有不同,因此它有自己的選項來控制顯示的內容。

funcgraph-overrun-- 設置後,在跟蹤每個函數後圖形顯示堆棧的“溢出”。溢出是指調用的堆棧深度大於爲每個任務保留的堆棧深度。
每個任務都有一個固定的函數數組,可以在調用圖中進行跟蹤。如果調用的深度超過該值,則不會跟蹤該函數。溢出是由於超過此陣列而錯過的功能數。

funcgraph-cpu-- 設置時,顯示發生跟蹤的CPU的CPU編號。

funcgraph-overhead-- 設置時,如果函數花費的時間超過一定量,則會顯示延遲標記。

funcgraph-proc--與其他跟蹤器不同,默認情況下不顯示進程的命令行,而是僅在上下文切換期間跟蹤和執行任務時。啓用此選項後,每行都會顯示每個進程的命令。

funcgraph-duration-- 在每個函數的末尾(返回),函數中的時間持續時間以微秒爲單位顯示。

funcgraph-abstime-- 設置後,每行顯示時間戳。

funcgraph-irqs-- 禁用時,不會跟蹤中斷內發生的函數

funcgraph-tail--設置後,return事件將包含它所代表的函數。默認情況下,這是關閉的,只顯示一個結束的大括號“}”以返回一個函數。

sleep-time-- 運行函數圖形跟蹤器時,包括任務在其函數中計劃的時間。啓用後,它將計算任務作爲函數調用的一部分進行計劃的時間。

graph-time-- 使用函數圖形跟蹤器運行函數分析器時,包括調用嵌套函數的時間。如果未設置此項,則爲函數報告的時間將僅包括函數本身執行的時間,而不是其調用的函數的時間。

3.blk tracer的選項:

blk_classic--顯示更簡約的輸出。

5 示例

5.1 function

4.1 trace輸出格式已經使用了function tracer, 使用ftrace前,確保設置了ftrace_enabled; 否則是一個nop

root@work-machine:/sys/kernel/debug/tracing#sysctl kernel.ftrace_enabled=1  #啓用ftrace
root@work-machine:/sys/kernel/debug/tracing#echo function > current_tracer
root@work-machine:/sys/kernel/debug/tracing#echo 1 > tracing_on
root@work-machine:/sys/kernel/debug/tracing#usleep 1
root@work-machine:/sys/kernel/debug/tracing#echo 0 > tracing_on
root@work-machine:/sys/kernel/debug/tracing#cat trace

注意:function tracer使用環形緩衝區來存儲trace條目。 最新數據可能會覆蓋最舊的數據。 使用echo來停止跟蹤是不夠的,可能會覆蓋要記錄的數據。 因此,有時最好直接從程序中禁用跟蹤。 可以在感興趣的部分終止trace,要直接從C程序禁用跟蹤,可以使用以下代碼片段:

int trace_fd;
[...]
int main(int argc, char *argv[]) {
	[...]
	trace_fd = open(tracing_file("tracing_on"), O_WRONLY);
	[...]
	if (condition_hit()) {
		write(trace_fd, "0", 1);
	}
	[...]
}

你肯定會覺得function trace 輸出的函數太多了,查看起來太困難了。別擔心,下面我給你說個技巧,我們可以利用 ftrace 裏的 filter 參數做篩選,詳見[5.11.1 Filter commands](5.11.1 Filter commands)。

5.2 irqsoff

當中斷禁用時,CPU無法對其他外部事件作出反應(除了NMI和SMI),irqsoff 能夠跟蹤禁用中斷的時間。 當達到新的最大延遲時,跟蹤器會保存導致該延遲點的trace,以便每次達到新的最大值時,將丟棄舊的已保存trace並保存新的trace。要重置最大值,echo 0到tracing_max_latency文件。

root@work-machine:/sys/kernel/debug/tracing#echo 0 > options/function-trace #設置本次trace 不跟蹤函數
root@work-machine:/sys/kernel/debug/tracing#echo irqsoff > current_tracer	#設置本次trace爲irqsoff
root@work-machine:/sys/kernel/debug/tracing#echo 1 > tracing_on				#開始記錄到緩衝區
root@work-machine:/sys/kernel/debug/tracing#echo 0 > tracing_max_latency	#重置延遲最大值
#測試內容
root@work-machine:/sys/kernel/debug/tracing#echo 0 > tracing_on		#停止寫入緩衝區
root@work-machine:/sys/kernel/debug/tracing#cat trace		#查看trace

上面沒有設置函數跟蹤,如果需要設置函數跟蹤,這樣可以看到那段時間所有函數調用(啓用函數跟蹤會產生額外的開銷):

root@work-machine:/sys/kernel/debug/tracing#echo 1 > options/function-trace	 #本次trace 設置函數跟蹤

5.3 preemptoff

禁用搶佔時,我們可能會收到中斷,但任務無法搶佔,優先級較高的任務必須等待搶佔啓用才能搶佔優先級較低的任務。
preemptoff tracer跟蹤禁用搶佔的位置。 它記錄了搶佔被禁用的最大延遲。操作與上面類似。

root@work:/sys/kernel/debug/tracing#echo 0 > options/function-trace
root@work:/sys/kernel/debug/tracing#echo preemptoff > current_tracer
root@work:/sys/kernel/debug/tracing#echo 1 > tracing_on
root@work:/sys/kernel/debug/tracing#echo 0 > tracing_max_latency
#跟蹤過程中執行操作
root@work:/sys/kernel/debug/tracing#echo 0 > tracing_on
root@work:/sys/kernel/debug/tracing#cat trace

同樣,上面沒有設置函數跟蹤,如果需要設置函數跟蹤,這樣可以看到那段時間所有函數調用(啓用函數跟蹤會產生額外的開銷):

root@work:/sys/kernel/debug/tracing#echo 1 > options/function-trace	 #本次trace 設置函數跟蹤

5.4 preemptirqsoff

知道禁用中斷的位置或禁用搶佔的最長時間是有幫助的。 但有時我們想知道何時禁用搶佔 中斷。
考慮以下代碼:

local_irq_disable();
call_function_with_irqs_off();
preempt_disable();
call_function_with_irqs_and_preemption_off();
local_irq_enable();
call_function_with_preemption_off();
preempt_enable();

irqsoff tracer將記錄call_function_with_irqs_off()call_function_with_irqs_and_preemption_off()的總長度。
preemptoff tracer將記錄call_function_with_irqs_and_preemption_off()call_function_with_preemption_off()的總長度。

但是,兩者都不會跟蹤禁用中斷 或 搶佔的時間。 這個總時間是我們無法安排的時間。 要記錄此時間,使用preemptirqsoff跟蹤器。

root@work:/sys/kernel/debug/tracing#echo 0 > options/function-trace
root@work:/sys/kernel/debug/tracing#echo preemptirqsoff > current_tracer
root@work:/sys/kernel/debug/tracing#echo 1 > tracing_on
root@work:/sys/kernel/debug/tracing#echo 0 > tracing_max_latency
#跟蹤過程中的操作
root@work:/sys/kernel/debug/tracing#echo 0 > tracing_on
root@work:/sys/kernel/debug/tracing#cat trace

5.5 wakeup

非實時任務喚醒時,實際喚醒的任務所花費的時間。

root@work:/sys/kernel/debug/tracing#echo 0 > options/function-trace
root@work:/sys/kernel/debug/tracing#echo wakeup > current_tracer
root@work:/sys/kernel/debug/tracing#echo 1 > tracing_on
root@work:/sys/kernel/debug/tracing#echo 0 > tracing_max_latency
root@work:/sys/kernel/debug/tracing#chrt -f 5 sleep 1   #模擬睡眠喚醒
root@work:/sys/kernel/debug/tracing#echo 0 > tracing_on
root@work:/sys/kernel/debug/tracing#cat trace

5.6 wakeup_rt

在實時環境中,瞭解喚醒最高優先級任務所需的喚醒時間非常重要。這也稱爲“調度延遲”。
實時環境對最壞情況的延遲感興趣。wakeup_rt跟蹤器只記錄RT任務的最壞喚醒情況。不記錄非RT任務,因爲跟蹤器僅記錄一個最壞的情況,並且跟蹤不可預測的非RT任務,將覆蓋RT任務的最壞情況延遲。

root@work:/sys/kernel/debug/tracing#echo 0 > options/function-trace
root@work:/sys/kernel/debug/tracing#echo wakeup_rt > current_tracer
root@work:/sys/kernel/debug/tracing#echo 1 > tracing_on
root@work:/sys/kernel/debug/tracing#echo 0 > tracing_max_latency
root@work:/sys/kernel/debug/tracing#chrt -f 5 sleep 1   #模擬RT任務睡眠
root@work:/sys/kernel/debug/tracing#echo 0 > tracing_on
root@work:/sys/kernel/debug/tracing#cat trace

注意:trace數據顯示的是內部優先級(99-rtprio)
如:<idle>-0 3d..3 5us : 0:120:R ==> [003] 2389: 94:R sleep

0:120:R表示空閒任務PID:0,正在以優先級級0(120-120)運行'R',sleep任務的優先級是RT優先級5(99-5=94),也是運行狀態'R'.

如果需要設置函數跟蹤,這樣可以看到那段時間所有函數調用(啓用函數跟蹤會產生額外的開銷):

root@work:/sys/kernel/debug/tracing#echo 1 > options/function-trace	 #本次trace 設置函數跟蹤

5.7 使用event進行延遲trace

由於函數跟蹤會導致更大的延遲,但不使用函數跟蹤,就看不到延遲內發生時的情況,很難知道導致它的原因。 有另外的方法,那就是啓用events。

root@work:/sys/kernel/debug/tracing#echo 0 > options/function-trace
root@work:/sys/kernel/debug/tracing#echo wakeup_rt > current_tracer
root@work:/sys/kernel/debug/tracing#echo 1 > events/enable	#啓用events
root@work:/sys/kernel/debug/tracing#echo 1 > tracing_on
root@work:/sys/kernel/debug/tracing#echo 0 > tracing_max_latency
root@work:/sys/kernel/debug/tracing#chrt -f 5 sleep 1  #模擬實時任務
root@work:/sys/kernel/debug/tracing#echo 0 > tracing_on
root@work:/sys/kernel/debug/tracing#cat trace

5.8 硬件延遲檢測器

通過啓用“hwlat”跟蹤器來執行硬件延遲trace。
注意,此“hwlat”將影響系統的性能,因爲它會定期使CPU始終忙於禁用中斷。

root@work:/sys/kernel/debug/tracing#echo hwlat > current_tracer
root@work:/sys/kernel/debug/tracing#sleep 100
root@work:/sys/kernel/debug/tracing#cat trace

5.9 trace單線程

通過寫入set_ftrace_pid,來跟蹤單個線程。 例如:

root@work:/sys/kernel/debug/tracing#echo 3111 > set_ftrace_pid
root@work:/sys/kernel/debug/tracing#cat set_ftrace_pid  	#查看trace的線程pid
	3111
root@work:/sys/kernel/debug/tracing#echo function > current_tracer
root@work:/sys/kernel/debug/tracing#cat trace | head

如果要在執行時跟蹤函數,可以使用類似這樣的簡單程序:

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

#define _STR(x) #x
#define STR(x) _STR(x)
#define MAX_PATH 256

const char *find_tracefs(void)
{
	static char tracefs[MAX_PATH+1];
	static int tracefs_found;
	char type[100];
	FILE *fp;

	if (tracefs_found)
		return tracefs;

	if ((fp = fopen("/proc/mounts","r")) == NULL) {
		perror("/proc/mounts");
		return NULL;
	}

	while (fscanf(fp, "%*s %"
		STR(MAX_PATH)
		"s %99s %*s %*d %*d\n",
	tracefs, type) == 2) {
		if (strcmp(type, "tracefs") == 0)
			break;
		}
		fclose(fp);

		if (strcmp(type, "tracefs") != 0) {
			fprintf(stderr, "tracefs not mounted");
			return NULL;
	}

	strcat(tracefs, "/tracing/");
	tracefs_found = 1;

	return tracefs;
}

const char *tracing_file(const char *file_name)
{
	static char trace_file[MAX_PATH+1];
	snprintf(trace_file, MAX_PATH, "%s/%s", find_tracefs(), file_name);
	return trace_file;
}

int main (int argc, char **argv)
{
	if (argc < 1)
		exit(-1);

	if (fork() > 0) {
		int fd, ffd;
		char line[64];
		int s;

		ffd = open(tracing_file("current_tracer"), O_WRONLY);
		if (ffd < 0)
			exit(-1);
		write(ffd, "nop", 3);

		fd = open(tracing_file("set_ftrace_pid"), O_WRONLY);
		s = sprintf(line, "%d\n", getpid());
		write(fd, line, s);

		write(ffd, "function", 8);

		close(fd);
		close(ffd);

		execvp(argv[1], argv+1);
	}
	return 0;
}

或者使用腳本:

#!/bin/bash

tracefs=`sed -ne 's/^tracefs \(.*\) tracefs.*/\1/p' /proc/mounts`
echo nop > $tracefs/tracing/current_tracer
echo 0 > $tracefs/tracing/tracing_on
echo $$ > $tracefs/tracing/set_ftrace_pid
echo function > $tracefs/tracing/current_tracer
echo 1 > $tracefs/tracing/tracing_on
exec "$@"

5.10 function graph tracer

類似於函數跟蹤器,它在函數調用出入口上插入probe函數。 這是通過在每個task_struct中使用動態分配的返回地址堆棧來完成的。 在函數入口上,跟蹤器會覆蓋每個跟蹤的函數的返回地址以設置自定義probe。 因此,原始返回地址存儲在task_struct中的返回地址堆棧中。
兩端的probe函數的有特殊功能,例如:

  • 衡量一個函數的執行時間
  • 具有可靠的調用堆棧來繪製函數調用圖

函數圖trace在以下幾種情況下很有用:

  • 想要找到奇怪的內核行爲的原因,並且需要查看在任何區域(或特定區域)上發生的詳細情況。
  • 遇到了奇怪的延遲,但很難找到它的起源。
  • 希望快速找到特定函數的執行路徑
  • 你只是想窺探一個正在運行的內核,並想看看那裏發生了什麼。
# tracer: function_graph
#
# CPU  DURATION                  FUNCTION CALLS
# |     |   |                     |   |   |   |

 0)               |  sys_open() {
 0)               |    do_sys_open() {
 0)               |      getname() {
 0)               |        kmem_cache_alloc() {
 0)   1.382 us    |          __might_sleep();
 0)   2.478 us    |        }
 0)               |        strncpy_from_user() {
 0)               |          might_fault() {
 0)   1.389 us    |            __might_sleep();
 0)   2.553 us    |          }
 0)   3.807 us    |        }
 0)   7.876 us    |      }
 0)               |      alloc_fd() {
 0)   0.668 us    |        _spin_lock();
 0)   0.570 us    |        expand_files();
 0)   0.586 us    |        _spin_unlock();

有幾列可以動態 啓用或禁用。 可以根據需要將幾個選項組合。

  • 默認啓用顯示執行該功能的cpu編號。 有時最好只跟蹤一個cpu(請參閱tracing_cpu_mask文件),否則可能會在跟蹤切換cpu時看到無序函數調用。

    • hide:echo nofuncgraph-cpu> trace_options
    • show:echo funcgraph-cpu> trace_options
  • 在函數的結束括號行上顯示函數的執行時間,或者在葉子的情況下顯示在當前函數的同一行上。 它是默認啓用的。

    • hide:echo nofuncgraph-duration> trace_options
    • show:echo funcgraph-duration> trace_options
  • 函數執行花費的時間超過一定量,則會顯示延遲標記

    • hide:echo nofuncgraph-overhead> trace_options
    • show:echo funcgraph-overhead> trace_options

取決於:funcgraph-duration,如:

  3) # 1837.709 us |          } /* __switch_to */
  3)               |          finish_task_switch() {
  3)   0.313 us    |            _raw_spin_unlock_irq();
  3)   3.177 us    |          }
  3) # 1889.063 us |        } /* __schedule */
  3) ! 140.417 us  |      } /* __schedule */
  3) # 2034.948 us |    } /* schedule */
  3) * 33998.59 us |  } /* schedule_preempt_disabled */

  [...]

  1)   0.260 us    |              msecs_to_jiffies();
  1)   0.313 us    |              __rcu_read_unlock();
  1) + 61.770 us   |            }
  1) + 64.479 us   |          }
  1)   0.313 us    |          rcu_bh_qs();
  1)   0.313 us    |          __local_bh_enable();
  1) ! 217.240 us  |        }
  1)   0.365 us    |        idle_cpu();
  1)               |        rcu_irq_exit() {
  1)   0.417 us    |          rcu_eqs_enter_common.isra.47();
  1)   3.125 us    |        }
  1) ! 227.812 us  |      }
  1) ! 457.395 us  |    }
  1) @ 119760.2 us |  }

  [...]

  2)               |    handle_IPI() {
  1)   6.979 us    |                  }
  2)   0.417 us    |      scheduler_ipi();
  1)   9.791 us    |                }
  1) + 12.917 us   |              }
  2)   3.490 us    |    }
  1) + 15.729 us   |            }
  1) + 18.542 us   |          }
  2) $ 3594274 us  |  }

+ 表示該函數執行時間超過10us。

! 表示該函數執行時間超過100us。

#表示該函數執行時間超過1000us。

*** **表示該函數執行時間超過10ms。

@表示該函數執行時間超過100ms。

$表示該函數執行時間超過1s。

  • task / pid字段顯示執行該功能的命令行和PID。 它是默認禁用的。

hide:echo nofuncgraph-proc> trace_options
show:echo funcgraph-proc> trace_options
例如:

  # tracer: function_graph
  #
  # CPU  TASK/PID        DURATION                  FUNCTION CALLS
  # |    |    |           |   |                     |   |   |   |
  0)    sh-4802     |               |                  d_free() {
  0)    sh-4802     |               |                    call_rcu() {
  0)    sh-4802     |               |                      __call_rcu() {
  0)    sh-4802     |   0.616 us    |                        rcu_process_gp_end();
  0)    sh-4802     |   2.899 us    |                      }
  0)    sh-4802     |   4.040 us    |                    }
  0)    sh-4802     |   5.151 us    |                  }
  0)    sh-4802     | + 49.370 us   |                }
  • 顯示系統自啓動以來的絕對時間戳。 每次進入/退出函數時都會給出此時間的快照。
    • hide:echo nofuncgraph-abstime> trace_options
    • show:echo funcgraph-abstime> trace_options
  #
  #      TIME       CPU  DURATION                  FUNCTION CALLS
  #       |         |     |   |                     |   |   |   |
  360.774522 |   1)   0.541 us    |                     }
  360.774522 |   1)   4.663 us    |                   }
  360.774523 |   1)   0.541 us    |                   __wake_up_bit();
  360.774524 |   1)   6.796 us    |                 }
  360.774524 |   1)   7.952 us    |               }
  360.774525 |   1)   9.063 us    |             }
  360.774525 |   1)   0.615 us    |             journal_mark_dirty();
  360.774527 |   1)   0.578 us    |             __brelse();
  360.774528 |   1)               |             reiserfs_prepare_for_journal() {
  360.774528 |   1)               |               unlock_buffer() {
  360.774529 |   1)               |                 wake_up_bit() {
  360.774529 |   1)               |                   bit_waitqueue() {
  360.774530 |   1)   0.594 us    |                     __phys_addr();

如果該函數的起始不在跟蹤緩衝區中,則函數名稱始終顯示在函數的右括號之後。

  • 可以啓用右括號後的函數名稱顯示,從而更輕鬆地使用grep搜索函數執行時間。 它是默認禁用的。
    • hide:echo nofuncgraph-tail > trace_options
    • show:echo funcgraph-tail > trace_options
 Example with nofuncgraph-tail (default):
  0)               |      putname() {
  0)               |        kmem_cache_free() {
  0)   0.518 us    |          __phys_addr();
  0)   1.757 us    |        }
  0)   2.861 us    |      }

  Example with funcgraph-tail:
  0)               |      putname() {
  0)               |        kmem_cache_free() {
  0)   0.518 us    |          __phys_addr();
  0)   1.757 us    |        } /* kmem_cache_free() */
  0)   2.861 us    |      } /* putname() */

可以使用trace_printk()對特定函數進行一些註釋。例如,如果要在__might_sleep()函數中放置註釋,則只需要包含<linux/ftrace.h>並在__might_sleep()中調用trace_printk()

trace_printk("I'm a comment!\n")

5.11 動態trace

如果內核配置了CONFIG_DYNAMIC_FTRACE,則在禁用函數跟蹤時,系統將幾乎不會有任何開銷。它的工作方式是mcount函數調用(放置在每個內核函數的開頭,由gcc中的-pg產生),開始指向一個簡單的返回。 (啓用FTRACE將在編譯內核時包含-pg。)

在編譯時,每個C文件對象都通過recordmcount程序運行(位於scripts目錄中)。該程序將解析C對象中的ELF頭,以查找代碼段.text中調用mcount的所有位置。從gcc 4.6版本開始,爲x86添加了-mfentry,它調用了“__fentry__”而不是“mcount”。在創建堆棧幀之前調用。

請注意,並非所有跟蹤部分。可以通過notrace阻止它們,查看available_filter_functions文件以查看可以跟蹤的功能。

創建了一個名爲“__mcount_loc”的部分,其中包含對代碼段.text中所有mcount/fentry調用點的引用。 recordmcount程序將此部分重新鏈接回原始對象。內核的最後鏈接階段將所有這些引用添加到單個表中。

在啓動時,在初始化SMP之前,動態ftrace代碼掃描此表並將所有位置更新爲nops。它還會記錄添加到available_filter_functions列表中的位置。模塊在加載時和執行之前進行處理。卸載模塊時,它還會從ftrace功能列表中刪除其功能。這在模塊卸載代碼中是自動的,模塊作者不需要擔心它。

啓用跟蹤時,修改功能跟蹤點的過程取決於體系結構。舊方法是使用kstop_machine來防止執行代碼的CPU被修改(這可能導致CPU執行不需要的操作,特別是如果修改的代碼跨越緩存(或頁面)邊界),並且將nops修補回調用。但這一次,他們不會調用mcount(這只是一個函數存根)。他們現在調用ftrace基礎。

修改函數跟蹤點的新方法是在要修改的位置放置斷點,同步所有CPU,修改斷點未覆蓋的其餘指令。再次同步所有CPU,然後將帶有完成版本的斷點刪除到ftrace調用站點。

有些arch甚至不需要同步,並且只需將新代碼打到舊代碼之上,而其他CPU同時執行它們沒有任何問題。記錄被跟蹤函數的一個特殊副作用是我們現在可以有選擇地選擇我們希望跟蹤的函數以及我們希望mcount調用保留爲nops的函數。

使用兩個文件,一個用於啓用對指定函數跟蹤,另一個用於對指定函數禁用跟蹤。 他們是: set_ftrace_filterset_ftrace_notrace 可以添加到這些文件的可用函數列表列在:

available_filter_functions

root@work:/sys/kernel/debug/tracing#cat available_filter_functions | more
run_init_process
try_to_run_init_process
do_one_initcall
match_dev_by_uuid
name_to_dev_t
rootfs_mount
rootfs_mount
calibration_delay_done
calibrate_delay
do_audit_syscall_entry
exit_to_usermode_loop
syscall_slow_exit_work
syscall_trace_enter_phase1
syscall_trace_enter_phase2
syscall_trace_enter
do_syscall_32_irqs_off
do_fast_syscall_32
vgetcpu_cpu_init
map_vdso
vgetcpu_cpu_notifier
arch_setup_additional_pages
compat_arch_setup_additional_pages
update_vsyscall_tz
.......

如果只對sys_nanosleep hrtimer_interrupt兩個函數感興趣:

root@work:/sys/kernel/debug/tracing#echo sys_nanosleep hrtimer_interrupt > set_ftrace_filter
root@work:/sys/kernel/debug/tracing#echo function > current_tracer
root@work:/sys/kernel/debug/tracing#echo 1 > tracing_on
root@work:/sys/kernel/debug/tracing#usleep 1  #跟蹤過程操作
root@work:/sys/kernel/debug/tracing#echo 0 > tracing_on
root@work:/sys/kernel/debug/tracing#cat trace

要查看正在跟蹤哪些函數:

root@work:/sys/kernel/debug/tracing#cat set_ftrace_filter

過濾器還允許通配符匹配。
<match>* - 將匹配以<match>開頭的函數
*<match> - 將匹配以<match>結尾的函數
*<match>* - 將匹配其中包含<match>的函數
<match1>*<match2> - 將匹配以<match1>開頭並以<match2>結束的函

注意:最好使用引號括起通配符,否則shell可能會將參數擴展爲本地目錄中的文件名。

root@work:/sys/kernel/debug/tracing#echo 'hrtimer_*' > set_ftrace_filter

重置過濾器使用>,要追加則使用>>,和普通bash操作一樣。

5.11.1 使用函數圖動態trace

如果只想跟蹤一個函數及其所有子函數,則只需將其名稱echo到set_graph_function

root@work:/sys/kernel/debug/tracing#echo __do_fault > set_graph_function

將生成以下__do_fault()的“擴展”跟蹤:

 0)               |  __do_fault() {
 0)               |    filemap_fault() {
 0)               |      find_lock_page() {
 0)   0.804 us    |        find_get_page();
 0)               |        __might_sleep() {
 0)   1.329 us    |        }
 0)   3.904 us    |      }
 0)   4.979 us    |    }
 0)   0.653 us    |    _spin_lock();
 0)   0.578 us    |    page_add_file_rmap();
 0)   0.525 us    |    native_set_pte_at();
 0)   0.585 us    |    _spin_unlock();
 0)               |    unlock_page() {
 0)   0.541 us    |      page_waitqueue();
 0)   0.639 us    |      __wake_up_bit();
 0)   2.786 us    |    }
 0) + 14.237 us   |  }
 0)               |  __do_fault() {
 0)               |    filemap_fault() {
 0)               |      find_lock_page() {
 0)   0.698 us    |        find_get_page();
 0)               |        __might_sleep() {
 0)   1.412 us    |        }
 0)   3.950 us    |      }
 0)   5.098 us    |    }
 0)   0.631 us    |    _spin_lock();
 0)   0.571 us    |    page_add_file_rmap();
 0)   0.526 us    |    native_set_pte_at();
 0)   0.586 us    |    _spin_unlock();
 0)               |    unlock_page() {
 0)   0.533 us    |      page_waitqueue();
 0)   0.638 us    |      __wake_up_bit();
 0)   2.793 us    |    }
 0) + 14.012 us   |  }
5.11.1 ftrace_enable

/proc/sysctl/ftrace_enable是函數跟蹤器的一個開關。 默認情況下啓用它(在內核中啓用函數跟蹤時)。 如果禁用,則禁用所有函數跟蹤。 這不僅用於ftrace的函數跟蹤器,還有其他用途(perf,kprobes,堆棧跟蹤、profiling等)。這可以通過以下方式禁用和啓用:

root@work:/sys/kernel/debug/tracing#sysctl kernel.ftrace_enabled = 0
root@work:/sys/kernel/debug/tracing#sysctl kernel.ftrace_enabled = 1

root@work:/sys/kernel/debug/tracing#echo 0> / proc / sys / kernel / ftrace_enabled
root@work:/sys/kernel/debug/tracing#echo 1> / proc / sys / kernel / ftrace_enabled
5.11.1 Filter commands

set_ftrace_filter接口支持一些命令,命令具有以下格式:
::
支持以下command:

  • mod
    此命令啓用每個模塊的函數過濾。該參數定義了模塊。例如,如果只想跟蹤ext3模塊中的write*函數,運行:
echo 'write*:mod:ext3' > set_ftrace_filter

通過>>追加到過濾器文件來添加更多不同模塊的函數。通過添加來刪除特定的模塊函數:

echo '!writeback*:mod:ext3' >> set_ftrace_filter

Mod命令支持模塊通配。禁用除特定模塊以外的所有函數的跟蹤:

echo '!*:mod:!ext3' >> set_ftrace_filter

禁用所有模塊的跟蹤,但仍跟蹤內核:

echo '!*:mod:*' >> set_ftrace_filter

僅爲內核啓用過濾器:

echo '*write*:mod:!*' >> set_ftrace_filter

爲通配模塊啓用過濾器:

echo '*write*:mod:*snd*' >> set_ftrace_filter
  • traceon/traceoff
    當命中指定的函數時,這些命令會打開和關閉跟蹤。該參數確定跟蹤系統打開和關閉的次數。如果未指定,則沒有限制。 例如,要在前5次遇到__schedule_bug時禁用跟蹤:
echo '__schedule_bug:traceoff:5' > set_ftrace_filter

要在命中__schedule_bug時始終禁用跟蹤:

echo '__schedule_bug:traceoff' > set_ftrace_filter

無論它們是否追加到set_ftrace_filter,這些命令都是累積的。 要刪除命令,請在前面加上它。 並刪除參數:

echo '!__schedule_bug:traceoff:0' > set_ftrace_filter

上面刪除了具有計數器的__schedule_bug的traceoff命令。刪除沒有設定計數的命令:

echo '!__schedule_bug:traceoff' > set_ftrace_filter
  • snapshot
    將在命中函數時觸發快照。
echo 'native_flush_tlb_others:snapshot' > set_ftrace_filter

僅快照一次:

echo 'native_flush_tlb_others:snapshot:1' > set_ftrace_filter

要刪除上述命令:

echo '!native_flush_tlb_others:snapshot' > set_ftrace_filter
echo '!native_flush_tlb_others:snapshot:0' > set_ftrace_filter
  • enable_event/disable_event
    這些命令可以啓用或禁用跟蹤事件。 注意,因爲函數跟蹤回調非常敏感,所以當註冊這些命令時,跟蹤點被激活,但在“軟”模式下被禁用。也就是說,將調用跟蹤點,但不會跟蹤。只要有觸發它的命令,事件跟蹤點就會保持此模式。
echo 'try_to_wake_up:enable_event:sched:sched_switch:2' >  set_ftrace_filter

格式爲:

<function>:enable_event:<system>:<event>[:count]
<function>:disable_event:<system>:<event>[:count]

要刪除事件命令:

echo '!try_to_wake_up:enable_event:sched:sched_switch:0' >  set_ftrace_filter
echo '!schedule:disable_event:sched:sched_switch' >  set_ftrace_filter
  • dump
    當命中該函數時,它會將ftrace環形緩衝區的內容轉儲到控制檯。如果您需要調試某些內容,並希望在某個函數被命中時轉儲跟蹤,這將非常有用。 也許它是一個在tripple故障發生之前被調用的函數,並且不允許你獲得常規轉儲。

  • cpudump
    當命中該函數時,它會將當前CPU的ftrace環形緩衝區的內容轉儲到控制檯。 與“dump”不同,它僅爲CPU已執行這個函數才觸發打印出環形緩衝區的內容。

5.11.1 trace_pipe

trace_pipe輸出與跟蹤文件相同的內容,但對跟蹤的影響不同。 每次從trace_pipe讀取都會被消耗掉。 意味着再次讀寫會不同, 跟蹤是實時的。

root@work:/sys/kernel/debug/tracing#echo function > current_tracer
root@work:/sys/kernel/debug/tracing#cat trace_pipe > /tmp/trace.out &     #後臺運行
root@work:/sys/kernel/debug/tracing#echo 1 > tracing_on
root@work:/sys/kernel/debug/tracing#usleep 1
root@work:/sys/kernel/debug/tracing#echo 0 > tracing_on
root@work:/sys/kernel/debug/tracing#cat trace         #無輸出

注意,讀取trace_pipe文件將阻塞,直到添加更多輸入。

5.11.1 trace entries

在診斷內核中的問題時,擁有太多或不足夠的數據都會很麻煩。 文件buffer_size_kb用於修改內部跟蹤緩衝區的大小。 列出的數字是每個CPU可以記錄的條目數。 要知道總大小,將可能的CPU數乘以條目數。如果分配太多,可能會導致觸發Out-Of-Memory。
per_cpu緩衝區可以單獨更改:

root@work:/sys/kernel/debug/tracing#echo 10000 > per_cpu/cpu0/buffer_size_kb
root@work:/sys/kernel/debug/tracing#echo 100 > per_cpu/cpu1/buffer_size_kb

當per_cpu緩衝區不相同時,頂層的buffer_size_kb將只顯示一個X.

root@work:/sys/kernel/debug/tracing#cat buffer_size_kb
X.

這是buffer_total_size_kb有用的地方:

root@work:/sys/kernel/debug/tracing#cat buffer_total_size_kb 12916

寫入頂級buffer_size_kb會將所有緩衝區重置爲相同。

5.11.1 Snapshot

CONFIG_TRACER_SNAPSHOT使所有非延遲跟蹤器都可以使用通用快照功能。 (記錄最大延遲的延遲跟蹤器,例如“irqsoff”或“wakeup”無法使用此功能,因爲這些功能已在內部使用快照機制。)

快照在特定時間點保留當前跟蹤緩衝區而不停止跟蹤。 Ftrace使用備用緩衝區交換當前緩衝區,並在新的緩衝區中繼續跟蹤。“跟蹤”中的以下tracefs文件與此功能相關:

snapshot:

這用於拍攝快照並讀取快照的輸出。 echo 1到此文件中以分配備用緩衝區並拍攝快照(交換),然後以與“跟蹤”相同的格式從該文件中讀取快照(如上文“文件系統”一節所述)。 兩者都讀取快照和跟蹤可並行執行。 分配備用緩衝區時,echoing 0釋放它,並且回顯其他(正)值清除快照內容。
更多細節顯示在下表中。

status\input 0 1 else
not allocated (do nothing) alloc+swap (do nothing)
allocated free swap clear

以下是使用快照功能的示例。

root@work:/sys/kernel/debug/tracing#echo 1 > events/sched/enable
root@work:/sys/kernel/debug/tracing#echo 1 > snapshot
root@work:/sys/kernel/debug/tracing#cat snapshot
# tracer: nop
#
# entries-in-buffer/entries-written: 71/71   #P:8
#
#                              _-----=> irqs-off
#                             / _----=> need-resched
#                            | / _---=> hardirq/softirq
#                            || / _--=> preempt-depth
#                            ||| /     delay
#           TASK-PID   CPU#  ||||    TIMESTAMP  FUNCTION
#              | |       |   ||||       |         |
          <idle>-0     [005] d...  2440.603828: sched_switch: prev_comm=swapper/5 prev_pid=0 prev_prio=120 prev_state=R ==> next_comm=snapshot-test-2 next_pid=2242 next_prio=120
           sleep-2242  [005] d...  2440.603846: sched_switch: prev_comm=snapshot-test-2 prev_pid=2242 prev_prio=120 prev_state=R ==> next_comm=kworker/5:1 next_pid=60 next_prio=120
[...]
          <idle>-0     [002] d...  2440.707230: sched_switch: prev_comm=swapper/2 prev_pid=0 prev_prio=120 prev_state=R ==> next_comm=snapshot-test-2 next_pid=2229 next_prio=120
          
          
root@work:/sys/kernel/debug/tracing#cat trace
# tracer: nop
#
# entries-in-buffer/entries-written: 77/77   #P:8
#
#                              _-----=> irqs-off
#                             / _----=> need-resched
#                            | / _---=> hardirq/softirq
#                            || / _--=> preempt-depth
#                            ||| /     delay
#           TASK-PID   CPU#  ||||    TIMESTAMP  FUNCTION
#              | |       |   ||||       |         |
          <idle>-0     [007] d...  2440.707395: sched_switch: prev_comm=swapper/7 prev_pid=0 prev_prio=120 prev_state=R ==> next_comm=snapshot-test-2 next_pid=2243 next_prio=120
 snapshot-test-2-2229  [002] d...  2440.707438: sched_switch: prev_comm=snapshot-test-2 prev_pid=2229 prev_prio=120 prev_state=S ==> next_comm=swapper/2 next_pid=0 next_prio=120
[...]

如果在當前跟蹤器是latency跟蹤器之一時嘗試使用此快照功能,則會得到以下結果。

root@work:/sys/kernel/debug/tracing#echo wakeup > current_tracer
root@work:/sys/kernel/debug/tracing#echo 1 > snapshot
	bash: echo: write error: Device or resource busy
root@work:/sys/kernel/debug/tracing#cat snapshot
	cat: snapshot: Device or resource busy
5.11.1 Instances

在tracefs中,跟蹤目錄是一個名爲“instances”的目錄。該目錄可以使用mkdir在其中創建新目錄,並使用rmdir刪除目錄。 在此目錄中使用mkdir創建的目錄在創建後已包含文件和其他目錄。

root@work:/sys/kernel/debug/tracing# mkdir instances/foo
root@work:/sys/kernel/debug/tracing# ls instances/foo
buffer_size_kb  buffer_total_size_kb  events  free_buffer  per_cpu
set_event  snapshot  trace  trace_clock  trace_marker  trace_options
trace_pipe  tracing_on

可以看到,新目錄看起來與traceing目錄本身類似。 它非常相似,只是緩衝區和事件或創建的任何其他實例與主目錄無關。

新目錄中的文件就像traceing目錄中具有相同名稱的文件一樣,除了使用的緩衝區是一個單獨的新緩衝區。 這些文件會影響該緩衝區,但不影響主緩衝區,但trace_options除外。 目前,trace_options影響所有實例,頂級緩衝區影響相同,但在將來的版本中可能會更改。 也就是說,選項可能變得特定於它們所駐留的實例。

請注意,沒有 function tracer文件,current_tracer和available_tracers也沒有。 這是因爲緩衝區當前只能爲它們啓用事件。

mkdir instances/foo
mkdir instances/bar
mkdir instances/zoot
echo 100000 > buffer_size_kb
echo 1000 > instances/foo/buffer_size_kb
echo 5000 > instances/bar/per_cpu/cpu1/buffer_size_kb
echo function > current_trace
echo 1 > instances/foo/events/sched/sched_wakeup/enable
echo 1 > instances/foo/events/sched/sched_wakeup_new/enable
echo 1 > instances/foo/events/sched/sched_switch/enable
echo 1 > instances/bar/events/irq/enable
echo 1 > instances/zoot/events/syscalls/enable
root@work:/sys/kernel/debug/tracing#cat trace_pipe
CPU:2 [LOST 11745 EVENTS]
            bash-2044  [002] .... 10594.481032: _raw_spin_lock_irqsave <-get_page_from_freelist
            bash-2044  [002] d... 10594.481032: add_preempt_count <-_raw_spin_lock_irqsave
            bash-2044  [002] d..1 10594.481032: __rmqueue <-get_page_from_freelist
            bash-2044  [002] d..1 10594.481033: _raw_spin_unlock <-get_page_from_freelist
            bash-2044  [002] d..1 10594.481033: sub_preempt_count <-_raw_spin_unlock
            bash-2044  [002] d... 10594.481033: get_pageblock_flags_group <-get_pageblock_migratetype
            bash-2044  [002] d... 10594.481034: __mod_zone_page_state <-get_page_from_freelist
            bash-2044  [002] d... 10594.481034: zone_statistics <-get_page_from_freelist
            bash-2044  [002] d... 10594.481034: __inc_zone_state <-zone_statistics
            bash-2044  [002] d... 10594.481034: __inc_zone_state <-zone_statistics
            bash-2044  [002] .... 10594.481035: arch_dup_task_struct <-copy_process
[...]
root@work:/sys/kernel/debug/tracing#cat instances/foo/trace_pipe
            bash-1998  [000] d..4   136.676759: sched_wakeup: comm=kworker/0:1 pid=59 prio=120 success=1 target_cpu=000
            bash-1998  [000] dN.4   136.676760: sched_wakeup: comm=bash pid=1998 prio=120 success=1 target_cpu=000
          <idle>-0     [003] d.h3   136.676906: sched_wakeup: comm=rcu_preempt pid=9 prio=120 success=1 target_cpu=003
          <idle>-0     [003] d..3   136.676909: sched_switch: prev_comm=swapper/3 prev_pid=0 prev_prio=120 prev_state=R ==> next_comm=rcu_preempt next_pid=9 next_prio=120
     rcu_preempt-9     [003] d..3   136.676916: sched_switch: prev_comm=rcu_preempt prev_pid=9 prev_prio=120 prev_state=S ==> next_comm=swapper/3 next_pid=0 next_prio=120
            bash-1998  [000] d..4   136.677014: sched_wakeup: comm=kworker/0:1 pid=59 prio=120 success=1 target_cpu=000
            bash-1998  [000] dN.4   136.677016: sched_wakeup: comm=bash pid=1998 prio=120 success=1 target_cpu=000
            bash-1998  [000] d..3   136.677018: sched_switch: prev_comm=bash prev_pid=1998 prev_prio=120 prev_state=R+ ==> next_comm=kworker/0:1 next_pid=59 next_prio=120
     kworker/0:1-59    [000] d..4   136.677022: sched_wakeup: comm=sshd pid=1995 prio=120 success=1 target_cpu=001
     kworker/0:1-59    [000] d..3   136.677025: sched_switch: prev_comm=kworker/0:1 prev_pid=59 prev_prio=120 prev_state=S ==> next_comm=bash next_pid=1998 next_prio=120
[...]

root@work:/sys/kernel/debug/tracing#cat instances/bar/trace_pipe
     migration/1-14    [001] d.h3   138.732674: softirq_raise: vec=3 [action=NET_RX]
          <idle>-0     [001] dNh3   138.732725: softirq_raise: vec=3 [action=NET_RX]
            bash-1998  [000] d.h1   138.733101: softirq_raise: vec=1 [action=TIMER]
            bash-1998  [000] d.h1   138.733102: softirq_raise: vec=9 [action=RCU]
            bash-1998  [000] ..s2   138.733105: softirq_entry: vec=1 [action=TIMER]
            bash-1998  [000] ..s2   138.733106: softirq_exit: vec=1 [action=TIMER]
            bash-1998  [000] ..s2   138.733106: softirq_entry: vec=9 [action=RCU]
            bash-1998  [000] ..s2   138.733109: softirq_exit: vec=9 [action=RCU]
            sshd-1995  [001] d.h1   138.733278: irq_handler_entry: irq=21 name=uhci_hcd:usb4
            sshd-1995  [001] d.h1   138.733280: irq_handler_exit: irq=21 ret=unhandled
            sshd-1995  [001] d.h1   138.733281: irq_handler_entry: irq=21 name=eth0
            sshd-1995  [001] d.h1   138.733283: irq_handler_exit: irq=21 ret=handled
[...]
root@work:/sys/kernel/debug/tracing#cat instances/zoot/trace
# tracer: nop
#
# entries-in-buffer/entries-written: 18996/18996   #P:4
#
#                              _-----=> irqs-off
#                             / _----=> need-resched
#                            | / _---=> hardirq/softirq
#                            || / _--=> preempt-depth
#                            ||| /     delay
#           TASK-PID   CPU#  ||||    TIMESTAMP  FUNCTION
#              | |       |   ||||       |         |
            bash-1998  [000] d...   140.733501: sys_write -> 0x2
            bash-1998  [000] d...   140.733504: sys_dup2(oldfd: a, newfd: 1)
            bash-1998  [000] d...   140.733506: sys_dup2 -> 0x1
            bash-1998  [000] d...   140.733508: sys_fcntl(fd: a, cmd: 1, arg: 0)
            bash-1998  [000] d...   140.733509: sys_fcntl -> 0x1
            bash-1998  [000] d...   140.733510: sys_close(fd: a)
            bash-1998  [000] d...   140.733510: sys_close -> 0x0
            bash-1998  [000] d...   140.733514: sys_rt_sigprocmask(how: 0, nset: 0, oset: 6e2768, sigsetsize: 8)
            bash-1998  [000] d...   140.733515: sys_rt_sigprocmask -> 0x0
            bash-1998  [000] d...   140.733516: sys_rt_sigaction(sig: 2, act: 7fff718846f0, oact: 7fff71884650, sigsetsize: 8)
            bash-1998  [000] d...   140.733516: sys_rt_sigaction -> 0x0

您可以看到最頂層跟蹤緩衝區的跟蹤僅顯示函數跟蹤。 foo實例顯示喚醒和任務切換。

要刪除實例,只需刪除它們的目錄:

root@work:/sys/kernel/debug/tracing#rmdir instances/foo
root@work:/sys/kernel/debug/tracing#rmdir instances/bar
root@work:/sys/kernel/debug/tracing#rmdir instances/zoot

請注意,如果進程在其中一個實例目錄中打開了跟蹤文件,則rmdir將失敗並顯示EBUSY。

5.11.1 Stack trace

由於內核具有固定大小的堆棧,因此不要將其浪費在函數中。內核開發人員必須熟悉他們在堆棧上分配的內容。如果它們添加太多,系統可能存在堆棧溢出的危險,並且會發生損壞,通常會導致系統崩潰。

有一些工具可以檢查這一點,通常中斷定期檢查使用情況。但是,如果您可以在每個函數調用中執行檢查,這將非常有用。由於ftrace提供了一個函數跟蹤器,因此可以方便地在每次函數調用時檢查堆棧大小。這是通過堆棧跟蹤器啓用的。

CONFIG_STACK_TRACER啓用ftrace堆棧跟蹤功能。
要啓用它,請在/proc/sys/kernel/stack_tracer_enabled中寫入'1'。

root@work:/sys/kernel/debug/tracing#echo 1 > /proc/sys/kernel/stack_tracer_enabled

您還可以通過在內核命令行中啓用它來在啓動期間跟蹤內核的堆棧大小,方法是在內核命令行參數中添加“stacktrace”。

運行幾分鐘後,輸出如下:

root@work:/sys/kernel/debug/tracing#cat stack_max_size
2928
root@work:/sys/kernel/debug/tracing#cat stack_trace
        Depth    Size   Location    (18 entries)
        -----    ----   --------
  0)     2928     224   update_sd_lb_stats+0xbc/0x4ac
  1)     2704     160   find_busiest_group+0x31/0x1f1
  2)     2544     256   load_balance+0xd9/0x662
  3)     2288      80   idle_balance+0xbb/0x130
  4)     2208     128   __schedule+0x26e/0x5b9
  5)     2080      16   schedule+0x64/0x66
  6)     2064     128   schedule_timeout+0x34/0xe0
  7)     1936     112   wait_for_common+0x97/0xf1
  8)     1824      16   wait_for_completion+0x1d/0x1f
  9)     1808     128   flush_work+0xfe/0x119
 10)     1680      16   tty_flush_to_ldisc+0x1e/0x20
 11)     1664      48   input_available_p+0x1d/0x5c
 12)     1616      48   n_tty_poll+0x6d/0x134
 13)     1568      64   tty_poll+0x64/0x7f
 14)     1504     880   do_select+0x31e/0x511
 15)      624     400   core_sys_select+0x177/0x216
 16)      224      96   sys_select+0x91/0xb9
 17)      128     128   system_call_fastpath+0x16/0x1b

注意,如果gcc正在使用-mfentry,則在設置堆棧幀之前會跟蹤函數。 這意味着當使用-mfentry時,堆棧跟蹤器不會測試葉級函數。

目前,-mfentry僅在x86上由gcc 4.6.0及更高版本使用。


二、ftrace實現機制

1. 待補充....

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