Ftrace 调试linux内核
Ftrace 调试linux内核
简介
ftrace 是内建于 Linux 内核的跟踪工具,从 2.6.27 开始加入主流内核。使用 ftrace 可以调试或者分析内核中发生的事情。ftrace 提供了不同的跟踪器,以用于不同的场合,比如跟踪内核函数调用、对上下文切换进行跟踪、查看中断被关闭的时长、跟踪内核态中的延迟以及性能问题等。系统开发人员可以使用 ftrace 对内核进行跟踪调试,以找到内核中出现的问题的根源,方便对其进行修复。另外,对内核感兴趣的读者还可以通过 ftrace 来观察内核中发生的活动,了解内核的工作机制。
Ftrace 的设计目标简单,本质上是一种静态代码插装技术,不需要支持某种编程接口让用户自定义 trace 行为。静态代码插装技术更加可靠,不会因为用户的不当使用而导致内核崩溃。 ftrace 代码量很小,稳定可靠。
内核编译支持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
……
通过debugfs访问ftrace
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 的设定不同,该目录下实际显示的文件和目录与这里也会不同。
trace目录结构
/sys/kernel/debug/trace 目录下文件和目录比较多,有些是各种跟踪器共享使用的,有些是特定于某个跟踪器使用的。在操作这些数据文件时,通常使用 echo 命令来修改其值,也可以在程序中通过文件读写相关的函数来操作这些文件的值。下面只对部分文件进行描述,读者可以参考内核源码包中 Documentation/trace 目录下的文档以及 kernel/trace 下的源文件以了解其余文件的用途。
- README文件提供了一个简短的使用说明,展示了 ftrace 的操作命令序列。可以通过 cat 命令查看该文件以了解概要的操作流程。
- current_tracer用于设置或显示当前使用的跟踪器;使用 echo 将跟踪器名字写入该文件可以切换到不同的跟踪器。系统启动后,其缺省值为 nop ,即不做任何跟踪操作。在执行完一段跟踪任务后,可以通过向该文件写入 nop 来重置跟踪器。
- available_tracers记录了当前编译进内核的跟踪器的列表,可以通过 cat 查看其内容;写 current_tracer 文件时用到的跟踪器名字必须在该文件列出的跟踪器名字列表中。
- available_event记录了当前编译进内核的可以跟踪event的列表,可以通过 cat 查看其内容;写 set_event文件时用到的跟踪器名字必须在该文件列出的跟踪器名字列表中。
- trace文件提供了查看获取到的跟踪信息的接口。可以通过 cat 等命令查看该文件以查看跟踪到的内核活动记录,也可以将其内容保存为记录文件以备后续查看。
- tracing_enabled用于控制 current_tracer 中的跟踪器是否可以跟踪内核函数的调用情况。写入 0 会关闭跟踪活动,写入 1 则**跟踪功能;其缺省值为 1 。在以前的ftrace中存在,新版本内核已经去除只保留tracing_on,老版本打开trace需要同时向tracing_enabled、tracing_on 写1.
- set_graph_function设置要清晰显示调用关系的函数,显示的信息结构类似于 C 语言代码,这样在分析内核运作流程时会更加直观一些。在使用 function_graph 跟踪器时使用;缺省为对所有函数都生成调用关系序列,可以通过写该文件来指定需要特别关注的函数。
- buffer_size_kb用于设置单个 CPU 所使用的跟踪缓存的大小。跟踪器会将跟踪到的信息写入缓存,每个 CPU 的跟踪缓存是一样大的。跟踪缓存实现为环形缓冲区的形式,如果跟踪到的信息太多,则旧的信息会被新的跟踪信息覆盖掉。注意,要更改该文件的值需要先将 current_tracer 设置为 nop 才可以。
- tracing_on用于控制跟踪的暂停。有时候在观察到某些事件时想暂时关闭跟踪,可以将 0 写入该文件以停止跟踪,这样跟踪缓冲区中比较新的部分是与所关注的事件相关的;写入 1 可以继续跟踪。
- available_filter_functions记录了当前可以跟踪的内核函数。对于不在该文件中列出的函数,无法跟踪其活动。
- set_ftrace_filter和 set_ftrace_notrace在编译内核时配置了动态 ftrace (选中 CONFIG_DYNAMIC_FTRACE 选项)后使用。前者用于显示指定要跟踪的函数,后者则作用相反,用于指定不跟踪的函数。如果一个函数名同时出现在这两个文件中,则这个函数的执行状况不会被跟踪。这些文件还支持简单形式的含有通配符的表达式,这样可以用一个表达式一次指定多个目标函数;具体使用在后续文章中会有描述。注意,要写入这两个文件的函数名必须可以在文件 available_filter_functions 中看到。缺省为可以跟踪所有内核函数,文件 set_ftrace_notrace 的值则为空。
- trace_pipe和trace相同,只不过是实时显示当前数据,但运行时像管道一样,可以在每次事件发生时读出追踪信息,但是读出的内容不能再次读出。
- tracing_cpumask 以十六进制的位掩码指定要作为追踪对象的处理器,例如,指定0XB时仅在处理器0,1,3上进行追踪。
- set_ftrace_pid 指定作为追踪对象的进程的PID号。
- latency_trace 和trace相同,不过组织方式略不同,主要方便用户分析系统有关延迟问题
ftrace跟踪器
ftrace 当前包含多个跟踪器,用于跟踪不同类型的信息,比如进程调度、中断关闭等。可以查看文件 available_tracers 获取内核当前支持的跟踪器列表,这些可以在编译内核时配置。同时可以查看available_event获得支持的跟踪event列表,这个支持的比较多,配置相对简单,只需向set_event写相应值,在向tracing_on写1就可以
- nop跟踪器不会跟踪任何内核活动,将 nop 写入 current_tracer 文件可以删除之前所使用的跟踪器,并清空之前收集到的跟踪信息,即刷新 trace 文件。
- function跟踪器可以跟踪内核函数的执行情况;可以通过文件 set_ftrace_filter 显示指定要跟踪的函数。
- function_graph跟踪器可以显示类似 C 源码的函数调用关系图,这样查看起来比较直观一些;可以通过文件 set_grapch_function 显示指定要生成调用流程图的函数。
- sched_switch跟踪器可以对内核中的进程调度活动进行跟踪。
- irqsoff跟踪器和 preemptoff跟踪器分别跟踪关闭中断的代码和禁止进程抢占的代码,并记录关闭的最大时长,preemptirqsoff跟踪器则可以看做它们的组合。、
- blk 块I / O追踪器。
Tracer使用
使用传统的 ftrace 需要如下几个步骤:
- 选择一种 tracer
- 使能 ftrace
- 执行需要 trace 的应用程序,比如需要跟踪 ls,就执行 ls
- 关闭 ftrace
- 查看 trace 文件
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获取跟踪信息,对内核的运行进行分析调试
[[email protected] tracing]# pwd
/sys/kernel/debug/tracing
[[email protected] tracing]# cat available_tracers
[[email protected] tracing]# cat available_event
……
#get list
……
[[email protected] tracing]# echo 0 > tracing_on
[[email protected] tracing]# echo function(or some other) > current_tracer
#or
[[email protected] tracing]# echo binder(or some other) > set_event
[[email protected] tracing]# echo 1 > tracing_on
# 让内核运行一段时间,这样 ftrace 可以收集一些跟踪信息,之后再停止跟踪
[[email protected] tracing]# echo 0 > tracing_on
[[email protected] 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
function_graph 跟踪器则可以提供类似 C 代码的函数调用关系信息。通过写文件 set_graph_function 可以显示指定要生成调用关系的函数,缺省会对所有可跟踪的内核函数生成函数调用关系图。
[[email protected] tracing]# pwd
/sys/kernel/debug/tracing
[[email protected] tracing]# echo 0 > tracing_on
[[email protected] tracing]# echo function_graph > current_tracer
[[email protected] tracing]# echo __do_fault > set_graph_function
[[email protected] tracing]# echo 1 > tracing_on
# 让内核运行一段时间,这样 ftrace 可以收集一些跟踪信息,之后再停止跟踪
[[email protected] tracing]# echo 0 > tracing_enabled
[[email protected] 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 | }
[[email protected] tracing]# echo > set_graph_function
sched_switch
sched_switch 跟踪器可以对进程的调度切换以及之间的唤醒操作进行跟踪
[[email protected] tracing]# pwd
/sys/kernel/debug/tracing
[[email protected] tracing]# echo 0 > tracing_on
[[email protected] tracing]# echo sched_switch > current_tracer
[[email protected] tracing]# echo 1 > tracing_on
# 让内核运行一段时间,这样 ftrace 可以收集一些跟踪信息,之后再停止跟踪
[[email protected] tracing]# echo 0 > tracing_on
[[email protected] 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
irqsoff
当关闭中断时,CPU 会延迟对设备的状态变化做出反应,有时候这样做会对系统性能造成比较大的影响。irqsoff 跟踪器可以对中断被关闭的状况进行跟踪,有助于发现导致较大延迟的代码;当出现最大延迟时,跟踪器会记录导致延迟的跟踪信息,文件 tracing_max_latency 则记录中断被关闭的最大延时
[[email protected] tracing]# pwd
/sys/kernel/debug/tracing
[[email protected] tracing]# echo 0 > tracing_on
[[email protected] tracing]# echo sched_switch > current_tracer
[[email protected] tracing]# echo 1 > tracing_on
# 让内核运行一段时间,这样 ftrace 可以收集一些跟踪信息,之后再停止跟踪
[[email protected] tracing]# echo 0 > tracing_on
[[email protected] tracing]# cat trace | head -10
# 让内核运行一段时间,这样 ftrace 可以收集一些跟踪信息,之后再停止跟踪
[[email protected] tracing]# echo 0 > tracing_enabled
[[email protected] 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
[[email protected] tracing]# cat tracing_max_latency
34380
参考:
上一篇: go接入etcd客户端使用
下一篇: Linux内核的ftrace调试接口