ftrace 是内建于 Linux 内核的跟踪工具,从 2.6.27 开始加入主流内核。使用 ftrace 可以调试或者分析内核中发生的事情。ftrace 提供了不同的跟踪器,以用于不同的场合,比如跟踪内核函数调用、对上下文切换进行跟踪、查看中断被关闭的时长、跟踪内核态中的延迟以及性能问题等。系统开发人员可以使用 ftrace 对内核进行跟踪调试,以找到内核中出现的问题的根源,方便对其进行修复。另外,对内核感兴趣的读者还可以通过 ftrace 来观察内核中发生的活动,了解内核的工作机制。
Ftrace 的设计目标简单,本质上是一种静态代码插装技术,不需要支持某种编程接口让用户自定义 trace 行为。静态代码插装技术更加可靠,不会因为用户的不当使用而导致内核崩溃。 ftrace 代码量很小,稳定可靠。
ftrace的目录在内核的/kernel/trace/,编译配置可以查看目录内的Makefile,然后在内核的/arch/arm64/configs/下配置,一般eng已经添加配置,无需增加。
……
obj-$(CONFIG_TRACING) += trace_phintk.o
obj-$(CONFIG_CONTEXT_SWITCH_TRACER) += trace_sched_switch.o
obj-$(COMFIG_FUNCTION_TRACER) += tPace_functions.o
obj—$(COMFIG_IRQSOFF_TRACER) += trace_ihqsoff.o
obj—$(COMFIG_PREEMPT_TRACER) += tPace_qusoff.o
obj—$(CONFIG_SCHEDLTRACER) += tPace_Sched_wakeup.o
obj—$(COMFIG_CPU_FREQ_SWITCH_PROFILER) += trace_cpu_fheq_switch.o
obj—$(COHFIG_NOP_TRACER) += tPace_nop.o
obj—$(CONFIG_STACK_TRACER) += tPace_stack.o
obj—$(CONFIG_MMIOTRACE) += tPace_mmiotPace.o
obj-$(CONFIG_FUNCTION_GRAPH_TRACER) += trace_functions_gbaph.o
obj-$(CONFIG_TRACE_BRANCH_PROFILING) += tPace_bPanch.o
obj-$(COHFIG_BLK_DEV_IO_TRACE) += blktPace.o
……
ftrace 通过 debugfs 向用户态提供访问接口。配置内核时激活 debugfs 后会创建目录 /sys/kernel/debug ,debugfs 文件系统就是挂载到该目录。
adb shell进去用下面命令挂载debugfs
mount -t debugfs nodev /sys/kernel/debug
激活内核对 ftrace 的支持后会在 debugfs 下创建一个 tracing 目录 /sys/kernel/debug/tracing 。该目录下包含了 ftrace 的控制和输出文件,如图 7 所示。根据编译内核时针对 ftrace 的设定不同,该目录下实际显示的文件和目录与这里也会不同。
/sys/kernel/debug/trace 目录下文件和目录比较多,有些是各种跟踪器共享使用的,有些是特定于某个跟踪器使用的。在操作这些数据文件时,通常使用 echo 命令来修改其值,也可以在程序中通过文件读写相关的函数来操作这些文件的值。下面只对部分文件进行描述,读者可以参考内核源码包中 Documentation/trace 目录下的文档以及 kernel/trace 下的源文件以了解其余文件的用途。
ftrace 当前包含多个跟踪器,用于跟踪不同类型的信息,比如进程调度、中断关闭等。可以查看文件 available_tracers 获取内核当前支持的跟踪器列表,这些可以在编译内核时配置。同时可以查看available_event获得支持的跟踪event列表,这个支持的比较多,配置相对简单,只需向set_event写相应值,在向tracing_on写1就可以
使用传统的 ftrace 需要如下几个步骤:
ftrace操作流程
查看 available_tracers 或available_event文件,获取当前内核支持的跟踪器列表
将所选择的跟踪器的名字写入文件 current_tracer,也可以将pid写到set_ftrace_pid用来追踪指定进程,或将event写到set_event中
将要跟踪的函数写入文件 set_ftrace_filter ,将不希望跟踪的函数写入文件 set_ftrace_notrace
激活 ftrace 跟踪,即将 1 写入文件 tracing_on
查看文件 trace 、trace_pipe、latency_trace获取跟踪信息,对内核的运行进行分析调试
[root@linux tracing]# pwd
/sys/kernel/debug/tracing
[root@linux tracing]# cat available_tracers
[root@linux tracing]# cat available_event
……
#get list
……
[root@linux tracing]# echo 0 > tracing_on
[root@linux tracing]# echo function(or some other) > current_tracer
#or
[root@linux tracing]# echo binder(or some other) > set_event
[root@linux tracing]# echo 1 > tracing_on
# 让内核运行一段时间,这样 ftrace 可以收集一些跟踪信息,之后再停止跟踪
[root@linux tracing]# echo 0 > tracing_on
[root@linux tracing]# cat trace | head -10
#tracer: nop
#
# entries-in-buffer/entries-written: 545132/122936680 #P:8
#
# _-----=> irqs-off
# / _----=> need-resched
# | / _---=> hardirq/softirq
# || / _--=> preempt-depth
# ||| / delay
# TASK-PID CPU# |||| TIMESTAMP FUNCTION
# | | | |||| | |
qualcomm.qti.q-2678 [003] ...1 3924.202445: binder_transaction_node_to_ref: transaction=6818641 node=18811 src_ptr=0x0000007f7cdb2e00 ==> dest_ref=18812 dest_desc=349
qualcomm.qti.q-2678 [003] ...2 3924.202451: binder_transaction_ref_to_node: transaction=6818641 node=19084 src_ref=19085 src_desc=19 ==> dest_ptr=0x0000007f696a1460
qualcomm.qti.q-2678 [003] ...1 3924.202472: binder_write_done: ret=0
qualcomm.qti.q-2678 [003] ...1 3924.202474: binder_wait_for_work: proc_work=0 transaction_stack=1 thread_todo=1
qualcomm.qti.q-2678 [003] ...1 3924.202476: binder_return: cmd=0x7206 BR_TRANSACTION_COMPLETE
qualcomm.qti.q-2678 [003] ...1 3924.202482: binder_read_done: ret=0
qualcomm.qti.q-2678 [003] ...1 3924.202483: binder_ioctl_done: ret=0
function_graph 跟踪器则可以提供类似 C 代码的函数调用关系信息。通过写文件 set_graph_function 可以显示指定要生成调用关系的函数,缺省会对所有可跟踪的内核函数生成函数调用关系图。
[root@linux tracing]# pwd
/sys/kernel/debug/tracing
[root@linux tracing]# echo 0 > tracing_on
[root@linux tracing]# echo function_graph > current_tracer
[root@linux tracing]# echo __do_fault > set_graph_function
[root@linux tracing]# echo 1 > tracing_on
# 让内核运行一段时间,这样 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
sched_switch 跟踪器可以对进程的调度切换以及之间的唤醒操作进行跟踪
[root@linux tracing]# pwd
/sys/kernel/debug/tracing
[root@linux tracing]# echo 0 > tracing_on
[root@linux tracing]# echo sched_switch > current_tracer
[root@linux tracing]# echo 1 > tracing_on
# 让内核运行一段时间,这样 ftrace 可以收集一些跟踪信息,之后再停止跟踪
[root@linux tracing]# echo 0 > tracing_on
[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-terminal
events/0-9 [000] 26208.817088: 9:120:S ==> [000] 1377:120:R gnome-terminal
当关闭中断时,CPU 会延迟对设备的状态变化做出反应,有时候这样做会对系统性能造成比较大的影响。irqsoff 跟踪器可以对中断被关闭的状况进行跟踪,有助于发现导致较大延迟的代码;当出现最大延迟时,跟踪器会记录导致延迟的跟踪信息,文件 tracing_max_latency 则记录中断被关闭的最大延时
[root@linux tracing]# pwd
/sys/kernel/debug/tracing
[root@linux tracing]# echo 0 > tracing_on
[root@linux tracing]# echo sched_switch > current_tracer
[root@linux tracing]# echo 1 > tracing_on
# 让内核运行一段时间,这样 ftrace 可以收集一些跟踪信息,之后再停止跟踪
[root@linux tracing]# echo 0 > tracing_on
[root@linux tracing]# cat trace | head -10
# 让内核运行一段时间,这样 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
参考:
使用 ftrace 调试 Linux 内核,第 1 部分