欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Ftrace 调试linux内核

程序员文章站 2022-07-13 22:13:51
...

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 的设定不同,该目录下实际显示的文件和目录与这里也会不同。
Ftrace 调试linux内核

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_filterset_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

参考:

使用 ftrace 调试 Linux 内核,第 1 部分