使用 ftrace 调试 Linux 内核,第 2 部分
ftrace 操作概述
使用 ftrace 提供的跟蹤器來調(diào)試或者分析內(nèi)核時需要如下操作:
- 切換到目錄 /sys/kernel/debug/tracing/ 下
- 查看 available_tracers 文件,獲取當前內(nèi)核支持的跟蹤器列表
- 關(guān)閉 ftrace 跟蹤,即將 0 寫入文件 tracing_enabled
- 激活 ftrace_enabled ,否則 function 跟蹤器的行為類似于 nop;另外,激活該選項還可以讓一些跟蹤器比如 irqsoff 獲取更豐富的信息。建議使用 ftrace 時將其激活。要激活 ftrace_enabled ,可以通過 proc 文件系統(tǒng)接口來設置:
- 將所選擇的跟蹤器的名字寫入文件 current_tracer
- 將要跟蹤的函數(shù)寫入文件 set_ftrace_filter ,將不希望跟蹤的函數(shù)寫入文件 set_ftrace_notrace。通常直接操作文件 set_ftrace_filter 就可以了
- 激活 ftrace 跟蹤,即將 1 寫入文件 tracing_enabled。還要確保文件 tracing_on 的值也為 1,該文件可以控制跟蹤的暫停
- 如果是對應用程序進行分析的話,啟動應用程序的執(zhí)行,ftrace 會跟蹤應用程序運行期間內(nèi)核的運作情況?
- 通過將 0 寫入文件 tracing_on 來暫停跟蹤信息的記錄,此時跟蹤器還在跟蹤內(nèi)核的運行,只是不再向文件 trace 中寫入跟蹤信息;或者將 0 寫入文件 tracing_enabled 來關(guān)閉跟蹤
- 查看文件 trace 獲取跟蹤信息,對內(nèi)核的運行進行分析調(diào)試
接下來將對跟蹤器的使用以及跟蹤信息的格式通過實例加以講解。
回頁首
fucntion 跟蹤器
function 跟蹤器可以跟蹤內(nèi)核函數(shù)的調(diào)用情況,可用于調(diào)試或者分析 bug ,還可用于了解和觀察 Linux 內(nèi)核的執(zhí)行過程。清單 1 給出了使用 function 跟蹤器的示例。
清單 1. function 跟蹤器使用示例
[root@linux tracing]# pwd /sys/kernel/debug/tracing [root@linux tracing]# echo 0 > tracing_enabled [root@linux tracing]# echo 1 > /proc/sys/kernel/ftrace_enabled [root@linux tracing]# echo function > current_tracer [root@linux tracing]# echo 1 > tracing_on [root@linux tracing]# echo 1 > tracing_enabled # 讓內(nèi)核運行一段時間,這樣 ftrace 可以收集一些跟蹤信息,之后再停止跟蹤[root@linux tracing]# echo 0 > tracing_enabled [root@linux tracing]# cat trace | head -10 # tracer: function # # TASK-PID CPU# TIMESTAMP FUNCTION # | | | | | <idle>-0 [000] 20654.426521: _raw_spin_lock <-scheduler_tick <idle>-0 [000] 20654.426522: task_tick_idle <-scheduler_tick <idle>-0 [000] 20654.426522: cpumask_weight <-scheduler_tick <idle>-0 [000] 20654.426523: cpumask_weight <-scheduler_tick <idle>-0 [000] 20654.426523: run_posix_cpu_timers <-update_process_times <idle>-0 [000] 20654.426524: hrtimer_forward <-tick_sched_timertrace 文件給出的信息格式很清晰。首先,字段“tracer:”給出了當前所使用的跟蹤器的名字,這里為 function 跟蹤器。然后是跟蹤信息記錄的格式,TASK 字段對應任務的名字,PID 字段則給出了任務的進程 ID,字段 CPU# 表示運行被跟蹤函數(shù)的 CPU 號,這里可以看到 idle 進程運行在 0 號 CPU 上,其進程 ID 是 0 ;字段 TIMESTAMP 是時間戳,其格式為“<secs>.<usecs>”,表示執(zhí)行該函數(shù)時對應的時間戳;FUNCTION 一列則給出了被跟蹤的函數(shù),函數(shù)的調(diào)用者通過符號 “<-” 標明,這樣可以觀察到函數(shù)的調(diào)用關(guān)系。
回頁首
function_graph 跟蹤器
在 function 跟蹤器給出的信息中,可以通過 FUNCTION 列中的符號 “<-” 來查看函數(shù)調(diào)用關(guān)系,但是由于中間會混合不同函數(shù)的調(diào)用,導致看起來非常混亂,十分不方便。function_graph 跟蹤器則可以提供類似 C 代碼的函數(shù)調(diào)用關(guān)系信息。通過寫文件 set_graph_function 可以顯示指定要生成調(diào)用關(guān)系的函數(shù),缺省會對所有可跟蹤的內(nèi)核函數(shù)生成函數(shù)調(diào)用關(guān)系圖。清單 2 給出了使用 function_grapch 跟蹤器的示例,示例中將內(nèi)核函數(shù) __do_fault 作為觀察對象,該函數(shù)在內(nèi)核運作過程中會被頻繁調(diào)用。
清單 2. function_graph 跟蹤器使用示例
[root@linux tracing]# pwd /sys/kernel/debug/tracing [root@linux tracing]# echo 0 > tracing_enabled [root@linux tracing]# echo 1 > /proc/sys/kernel/ftrace_enabled [root@linux tracing]# echo function_graph > current_tracer [root@linux tracing]# echo __do_fault > set_graph_function [root@linux tracing]# echo 1 > tracing_on [root@linux tracing]# echo 1 > tracing_enabled # 讓內(nèi)核運行一段時間,這樣 ftrace 可以收集一些跟蹤信息,之后再停止跟蹤[root@linux tracing]# echo 0 > tracing_enabled [root@linux tracing]# cat trace | head -20 # tracer: function_graph # # CPU DURATION FUNCTION CALLS # | | | | | | | 1) 9.936 us | } 1) 0.714 us | put_prev_task_fair(); 1) | pick_next_task_fair() { 1) | set_next_entity() { 1) 0.647 us | update_stats_wait_end(); 1) 0.699 us | __dequeue_entity(); 1) 3.322 us | } 1) 0.865 us | hrtick_start_fair(); 1) 6.313 us | } 1) | __switch_to_xtra() { 1) 1.601 us | memcpy(); 1) 3.938 us | } [root@linux tracing]# echo > set_graph_function在文件 trace 的輸出信息中,首先給出的也是當前跟蹤器的名字,這里是 function_graph 。接下來是詳細的跟蹤信息,可以看到,函數(shù)的調(diào)用關(guān)系以類似 C 代碼的形式組織。
CPU 字段給出了執(zhí)行函數(shù)的 CPU 號,本例中都為 1 號 CPU。DURATION 字段給出了函數(shù)執(zhí)行的時間長度,以 us 為單位。FUNCTION CALLS 則給出了調(diào)用的函數(shù),并顯示了調(diào)用流程。注意,對于不調(diào)用其它函數(shù)的函數(shù),其對應行以“;”結(jié)尾,而且對應的 DURATION 字段給出其運行時長;對于調(diào)用其它函數(shù)的函數(shù),則在其“}”對應行給出了運行時長,該時間是一個累加值,包括了其內(nèi)部調(diào)用的函數(shù)的執(zhí)行時長。DURATION 字段給出的時長并不是精確的,它還包含了執(zhí)行 ftrace 自身的代碼所耗費的時間,所以示例中將內(nèi)部函數(shù)時長累加得到的結(jié)果會與對應的外圍調(diào)用函數(shù)的執(zhí)行時長并不一致;不過通過該字段還是可以大致了解函數(shù)在時間上的運行開銷的。清單 2 中最后通過 echo 命令重置了文件 set_graph_function 。
回頁首
sched_switch 跟蹤器
sched_switch 跟蹤器可以對進程的調(diào)度切換以及之間的喚醒操作進行跟蹤,如清單 3 所示。
清單 3. sched_switch 跟蹤器使用示例
[root@linux tracing]# pwd /sys/kernel/debug/tracing [root@linux tracing]# echo 0 > tracing_enabled [root@linux tracing]# echo 1 > /proc/sys/kernel/ftrace_enabled [root@linux tracing]# echo sched_switch > current_tracer [root@linux tracing]# echo 1 > tracing_on [root@linux tracing]# echo 1 > tracing_enabled # 讓內(nèi)核運行一段時間,這樣 ftrace 可以收集一些跟蹤信息,之后再停止跟蹤[root@linux tracing]# echo 0 > tracing_enabled [root@linux tracing]# cat trace | head -10 # tracer: sched_switch # # TASK-PID CPU# TIMESTAMP FUNCTION # | | | | | bash-1408 [000] 26208.816058: 1408:120:S + [000] 1408:120:S bash bash-1408 [000] 26208.816070: 1408:120:S + [000] 1408:120:S bash bash-1408 [000] 26208.816921: 1408:120:R + [000] 9:120:R events/0 bash-1408 [000] 26208.816939: 1408:120:R ==> [000] 9:120:R events/0 events/0-9 [000] 26208.817081: 9:120:R + [000] 1377:120:R gnome-terminalevents/0-9 [000] 26208.817088: 9:120:S ==> [000] 1377:120:R gnome-terminal在 sched_swich 跟蹤器獲取的跟蹤信息中記錄了進程間的喚醒操作和調(diào)度切換信息,可以通過符號‘ + ’和‘ ==> ’區(qū)分;喚醒操作記錄給出了當前進程喚醒運行的進程,進程調(diào)度切換記錄中顯示了接替當前進程運行的后續(xù)進程。
描述進程狀態(tài)的格式為“Task-PID:Priority:Task-State”。以示例跟蹤信息中的第一條跟蹤記錄為例,可以看到進程 bash 的 PID 為 1408 ,其對應的內(nèi)核態(tài)優(yōu)先級為 120 ,當前狀態(tài)為 S(可中斷睡眠狀態(tài)),當前 bash 并沒有喚醒其它進程;從第 3 條記錄可以看到,進程 bash 將進程 events/0 喚醒,而在第 4 條記錄中發(fā)生了進程調(diào)度,進程 bash 切換到進程 events/0 執(zhí)行。
在 Linux 內(nèi)核中,進程的狀態(tài)在內(nèi)核頭文件 include/linux/sched.h 中定義,包括可運行狀態(tài) TASK_RUNNING(對應跟蹤信息中的符號‘ R ’)、可中斷阻塞狀態(tài) TASK_INTERRUPTIBLE(對應跟蹤信息中的符號‘ S ’)等。同時該頭文件也定義了用戶態(tài)進程所使用的優(yōu)先級的范圍,最小值為 MAX_USER_RT_PRIO(值為 100 ),最大值為 MAX_PRIO - 1(對應值為 139 ),缺省為 DEFAULT_PRIO(值為 120 );在本例中,進程優(yōu)先級都是缺省值 120 。
回頁首
irqsoff 跟蹤器
當關(guān)閉中斷時,CPU 會延遲對設備的狀態(tài)變化做出反應,有時候這樣做會對系統(tǒng)性能造成比較大的影響。irqsoff 跟蹤器可以對中斷被關(guān)閉的狀況進行跟蹤,有助于發(fā)現(xiàn)導致較大延遲的代碼;當出現(xiàn)最大延遲時,跟蹤器會記錄導致延遲的跟蹤信息,文件 tracing_max_latency 則記錄中斷被關(guān)閉的最大延時。
清單 4. irqsoff 跟蹤器使用示例
[root@linux tracing]# pwd /sys/kernel/debug/tracing [root@linux tracing]# echo 0 > tracing_enabled [root@linux tracing]# echo 1 > /proc/sys/kernel/ftrace_enabled [root@linux tracing]# echo irqsoff > current_tracer [root@linux tracing]# echo 1 > tracing_on [root@linux tracing]# echo 1 > tracing_enabled # 讓內(nèi)核運行一段時間,這樣 ftrace 可以收集一些跟蹤信息,之后再停止跟蹤[root@linux tracing]# echo 0 > tracing_enabled [root@linux tracing]# cat trace | head -35 # tracer: irqsoff # # irqsoff latency trace v1.1.5 on 2.6.33.1 # -------------------------------------------------------------------- # latency: 34380 us, #6/6, CPU#1 | (M:desktop VP:0, KP:0, SP:0 HP:0 #P:2) # ----------------- # | task: -0 (uid:0 nice:0 policy:0 rt_prio:0) # ----------------- # => started at: reschedule_interrupt # => ended at: restore_all_notrace # # # _------=> CPU# # / _-----=> irqs-off # | / _----=> need-resched # || / _---=> hardirq/softirq # ||| / _--=> preempt-depth # |||| /_--=> lock-depth # |||||/ delay # cmd pid |||||| time | caller # \ / |||||| \ | / <idle>-0 1dN... 4285us!: trace_hardirqs_off_thunk <-reschedule_interrupt <idle>-0 1dN... 34373us+: smp_reschedule_interrupt <-reschedule_interrupt <idle>-0 1dN... 34375us+: native_apic_mem_write <-smp_reschedule_interrupt <idle>-0 1dN... 34380us+: trace_hardirqs_on_thunk <-restore_all_notrace <idle>-0 1dN... 34384us : trace_hardirqs_on_caller <-restore_all_notrace <idle>-0 1dN... 34384us : <stack trace> => trace_hardirqs_on_thunk [root@linux tracing]# cat tracing_max_latency 34380從清單 4 中的輸出信息中,可以看到當前 irqsoff 延遲跟蹤器的版本信息。接下來是最大延遲時間,以 us 為單位,本例中為 34380 us ,查看文件 tracing_max_latency 也可以獲取該值。從“task:”字段可以知道延遲發(fā)生時正在運行的進程為 idle(其 pid 為 0 )。中斷的關(guān)閉操作是在函數(shù) reschedule_interrupt 中進行的,由“=> started at:”標識,函數(shù) restore_all_ontrace 重新激活了中斷,由“=> ended at:”標識;中斷關(guān)閉的最大延遲發(fā)生在函數(shù) trace_hardirqs_on_thunk 中,這個可以從最大延遲時間所在的記錄項看到,也可以從延遲記錄信息中最后的“=>”標識所對應的記錄行知道這一點。
在輸出信息中,irqs-off、need_resched 等字段對應于進程結(jié)構(gòu) struct task_struct 的字段或者狀態(tài)標志,可以從頭文件 arch/<platform>/include/asm/thread_info.h 中查看進程支持的狀態(tài)標志,include/linux/sched.h 則給出了結(jié)構(gòu) struct task_struct 的定義。其中,irqs-off 字段顯示是否中斷被禁止,為‘ d ’表示中斷被禁止;need_resched 字段顯示為‘ N ’表示設置了進程狀態(tài)標志 TIF_NEED_RESCHED。hardirq/softirq 字段表示當前是否發(fā)生硬件中斷 / 軟中斷;preempt-depth 表示是否禁止進程搶占,例如在持有自旋鎖的情況下進程是不能被搶占的,本例中進程 idle 是可以被其它進程搶占的。結(jié)構(gòu) struct task_struct 中的 lock_depth 字段是與大內(nèi)核鎖相關(guān)的,而最近的內(nèi)核開發(fā)工作中有一部分是與移除大內(nèi)核鎖相關(guān)的,這里對該字段不再加以描述。
另外,還有用于跟蹤禁止進程搶占的跟蹤器 preemptoff 和跟蹤中斷 / 搶占禁止的跟蹤器 preemptirqsoff,它們的使用方式與輸出信息格式與 irqsoff 跟蹤器類似,這里不再贅述。
回頁首
跟蹤指定模塊中的函數(shù)
前面提過,通過文件 set_ftrace_filter 可以指定要跟蹤的函數(shù),缺省目標為所有可跟蹤的內(nèi)核函數(shù);可以將感興趣的函數(shù)通過 echo 寫入該文件。為了方便使用,set_ftrace_filter 文件還支持簡單格式的通配符。
- begin*選擇所有名字以 begin 字串開頭的函數(shù)
- *middle*選擇所有名字中包含 middle 字串的函數(shù)
- *end選擇所有名字以 end 字串結(jié)尾的函數(shù)
需要注意的是,這三種形式不能組合使用,比如“begin*middle*end”實際的效果與“begin”相同。另外,使用通配符表達式時,需要用單引號將其括起來,如果使用雙引號,shell 可能會對字符‘ * ’進行擴展,這樣最終跟蹤的對象可能與目標函數(shù)不一樣。
通過該文件還可以指定屬于特定模塊的函數(shù),這要用到 mod 指令。指定模塊的格式為:
echo ':mod:[module_name]' > set_ftrace_filter下面給出了一個指定跟蹤模塊 ipv6 中的函數(shù)的例子。可以看到,指定跟蹤模塊 ipv6 中的函數(shù)會將文件 set_ftrace_filter 的內(nèi)容設置為只包含該模塊中的函數(shù)。
清單 5. 指定跟蹤 ipv6 模塊中的函數(shù)
[root@linux tracing]# pwd /sys/kernel/debug/tracing [root@linux tracing]# echo ':mod:ipv6' > set_ftrace_filter [root@linux tracing]# cat set_ftrace_filter | head -5 ipv6_opt_accepted inet6_net_exit ipv6_gro_complete inet6_create ipv6_addr_copy回頁首
小結(jié)
本系列文章對 ftrace 的配置和使用進行了介紹。本文是其中的第二部分,介紹了 ftrace 所提供的跟蹤器的使用,重點對不同格式的跟蹤信息進行了描述,并對如何跟蹤指定模塊中的函數(shù)進行了介紹。當前 Linux 內(nèi)核中 ftrace 支持多個跟蹤器,由于篇幅限制,這里不能對其全部加以描述。讀者可參照參考資料中給出的信息對 ftrace 跟蹤器進行更深入的了解。下一篇文章將對如何在代碼中通過 ftrace 提供的工具函數(shù)與 ftrace 交互進行描述。
轉(zhuǎn)載于:https://www.cnblogs.com/jefree/p/4439010.html
總結(jié)
以上是生活随笔為你收集整理的使用 ftrace 调试 Linux 内核,第 2 部分的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: vs2012 发布网站时出现 sgen.
- 下一篇: linux 其他常用命令