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_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获取跟踪信息,对内核的运行进行分析调试

[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

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

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

irqsoff

当关闭中断时,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 部分

你可能感兴趣的:(Linux,嵌入式,Android)