1. ftrace简介

1,361 阅读23分钟

1. 参考资料

pwl999 的Linux Ftrace (目录)

pwl999 Linux ftrace ftrace的使用

kernel.org doc ftrace

2. ftrace介绍

ftrace 是一个内部跟踪器,旨在帮助系统的开发人员和设计人员找到内核内部发生的事情。它可用于调试或分析发生在用户空间之外的延迟和性能问题。

尽管 ftrace 通常被认为是函数跟踪器,但它实际上是一个由多种跟踪实用程序组成的框架。有延迟跟踪来检查在禁用和启用中断之间发生了什么,以及抢占和从任务被唤醒到任务被实际调度的时间。

ftrace 最常见的用途之一是事件跟踪。 在内核跟踪和分析方面,Linux 内核有三个主要的机制:

  • 跟踪点tracepoint:一种基于静态测试代码的工作机制
  • 探针kprobe:一种动态跟踪机制,用于在任意时刻中断内核代码的运行,调用它自己的处理程序,在完成需要的操作之后再返回
  • perf_events —— 一个访问 PMU(性能监视单元Performance Monitoring Unit)的接口

image.png

ftrace直观理解有两层含义:一是提供函数钩子的基础设施,二是基于tracefs文件系统的trace框架。常用的功能如下:

  • function tracer:在函数头挂钩子函数
  • function graph tracer:可以带时间戳函数执行流打印
  • trace event:函数执行时调用静态的钩子函数
  • kprobe:一般是挂在函数入口点,用于获取参数
  • kretprobe:通常是函数出口点,执行钩子函数

3. tracer的使用

ftrace_enable是 function tracer的大开关。默认是enable,如果被disable,所有相关的trace都会被disable,包括 (perf, kprobes, stack tracing, profiling, etc)。

echo 0 > /proc/sys/kernel/ftrace_enabled 
echo 1 > /proc/sys/kernel/ftrace_enabled

trace目录在以下路径:/sys/kernel/debug/tracing

用列表来遍历“tracing/”文件夹下每一个文件的作用和描述:

file说明
current_tracer显示当前使用的tracer,默认是nop。
available_tracers列出当前已经注册的tracer
tracing_on0: disable ring buffer,1: enable ring buffer,这个仅仅只是禁止数据写入ring buffer,但是各种桩函数还是被调用,trace动作仍然发生,开销依然存在。
trace该文件把ringbuffer数据输出成用户可读的格式。注意:在这个文件open时,ring buffer是临时关闭的。读操作并不会清除掉ring buffer中的数据,可以重复读;
trace_pipe文件内容和“trace”文件是一样的,区别在于:1、并行读,读操作不会disable写操作;2、只支持读一次,该读操作会清除掉ring buffer中的数据,再次去读没有内容了;
trace_options一些通用的trace相关的option
options“options”是一个文件夹,包含了所有注册的tracer可配置的option
tracing_max_latency某些tracer用来记录最大latency。例如,最大中断disable时间。最大时间被记录到本文件,对应的trace被记录到“trace”文件。新的trace最有大于现在的max latency胡,才会被记录。
tracing_thresh有一些latency tracers判断latency大于本文件的标值才会被记录。只有值大于0才会被激活。
buffer_size_kb设置ring buffer在每个cpu上的大小。默认在每个cpu上大小都是相等的,如果显示“X”,查看“per_cpu/cpu0/buffer_size_kb”具体每个cpu的大小。
buffer_total_size_kbring buffer总的的大小
free_buffer本文件用来控制ring buffer的free,当free时ring buffer被resize到它的最小尺寸。使用方法:当进程不需要使用ring buffer了,他open“free_buffer”并且close文件,ring buffer会被resize到最小尺寸。
tracing_cpumask独立控制在每个cpu上是否开trace功能。
set_ftrace_filterfunction tracer的filter。dynamic ftrace是通过动态修改函数代码来插入桩函数的,这让对没有被trace的函数的影响降到了最低。需要使用dynamic ftrace跟踪哪些函数:echo 函数名 > set_ftrace_filter
set_ftrace_notracefunction tracer的filter。和“set_ftrace_filter”的作用相反,设置哪些函数不要被trace。如果同一函数在“set_ftrace_filter”和“set_ftrace_notrace”中同时被设置,效果等同于没有
set_ftrace_pidfunction tracer的filter。function tracer只追踪这个文件描述的PID。
set_event_pidtrace event的filter。只有本文件描述的PID的进程,才会记录其trace event。
set_graph_functionfunction graph tracer的filter。function graph tracer仅仅trace在本文件中描述的函数。
set_graph_notracefunction graph tracer的filter。和“set_graph_function”功能相反,function graph tracer不trace在本文件中描述的函数
available_filter_functions有效的可以被设置为filter的函数名。
dyn_ftrace_total_info该文件是调试目的,有多少个函数的桩函数被dynamic ftrace转换成nop可以被追踪。
enabled_functions该文件用来调试ftrace的,它可以显示所有attach了回调函数的函数。
function_profile_enabled保留被调用的函数数量的直方图, 直方图内容可以显示在以下文件中:trace_stats/function
trace_stats保存不同trace统计数据的目录
kprobe_events通过kprobe动态的创建trace event。而通过TRACE_EVENT()宏定义的都是静态的trace event
kprobe_profilekprobe trace_event的统计
max_graph_depthfunction graph tracer显示函数调用关系的层级
printk_formats使用trace_printk()打印数据的格式化字符串。使用“trace”文件读取ringbuffer中trace_printk()打印的数据时,需要知道解析格式。这个文件保存了所有的trace_printk()的打印格式。
saved_cmdlinesftrace建立起了一个cache,用来记录进程“pid”和“comms”之间的映射关系,在输出时能根据pid快速查找到进程的comms。如果进程的comms没有缓冲,使用空白填充 “<…>” 。
saved_cmdlines_sizesaved_cmdlines这块cache的大小
saved_tgids如果“record-tgid”选项被使能,PID对应的TGID映射也会被记录。
snapshot显示“snapshot”缓存中的内存,类似“trace”文件。snapshot对应一块独立的ring buffer,用来快照ring buffer中的内容。
stack_max_sizestack tracer的最大的堆栈尺寸
stack_tracestack tracer的最大的堆栈的具体的回调情况
stack_trace_filterstack tracer的filter。指示哪些函数可以被stack tracer跟踪。
trace_clockring buffer记录时间戳所使用的时钟源
trace_marker该文件运行用户态直接写内容到ring buffer,通常用来同步用户态和内核态的事件
trace_marker_raw和“trace_marker”类似,但是写入的是二进制格式。
uprobe_events通过uprobe动态的创建trace event。而通过TRACE_EVENT()宏定义的都是静态的trace event
uprobe_profileUprobe的统计功能
instances这是一个创建多个ring buffer的方法,可以让不同的events使用不同的ring buffer
eventstrace event的文件夹,包含所有通过TRACE_EVENT()宏定义的静态event 和 通过kprobe、uprobe定义的动态event
set_event使能trace event。echo xxxevent > set_event // enbale对应的xxxevent
available_events列出所有有效的event。相当于“events”文件夹中所有event名字的集合
timestamp_mode配置ring buffer中的时间戳为delta mode还是absolute 模式
hwlat_detectorHardware Latency Detector文件夹
per_cpuper cpu的文件夹
per_cpu/cpu0/buffer_size_kb单个cpu上的buffer size数据
per_cpu/cpu0/trace单个cpu上的trace信息
per_cpu/cpu0/trace_pipe单个cpu上的trace_pipe信息
per_cpu/cpu0/trace_pipe_raw单个cpu上的二进制trace_pipe信息
per_cpu/cpu0/snapshot单个cpu上的snapshot
per_cpu/cpu0/snapshot_raw单个cpu上的raw snapshot,返回二进制信息
per_cpu/cpu0/stats显示每个cpu的ring buffer统计

ftrace目前支持的tracer有以下这些:

tracer说明
function追踪所有的内核函数
function_graph和“function tracer”比较类似,但它除了探测函数的入口还探测函数的出口。它可以画出一个图形化的函数调用,类似于c源代码风格。
blk块设备tracer
hwlat用来侦测硬件产生的延迟。
irqsoff追踪并记录最大关闭中断时间。
preemptoff追踪并记录最大关闭抢占时间。
preemptirqsoff追踪并记录关闭中断 and/or 关闭抢占的最大时间。
wakeup追踪所有任务从获得调度到被唤醒的最大延迟时间。
wakeup_rt追踪RT与dl类型的任务从获得调度到被唤醒的最大延迟时间。
wakeup_dl追踪Deadline类型的任务从获得调度到被唤醒的最大延迟时间。
mmiotrace追踪硬件IO
branch追踪likely/unlikely的分支预测情况
nop空的tracer

“trace_options”文件(或者“option”文件夹中的文件)用来控制trace文件的输出格式,或者由tracer自动配置。

tracing# cat trace_options 
print-parent
nosym-offset
nosym-addr
noverbose
noraw
nohex
nobin
noblock
trace_printk
annotate
nouserstacktrace
nosym-userobj
noprintk-msg-only
context-info
nolatency-format
record-cmd
norecord-tgid
overwrite
nodisable_on_free
irq-info
markers
noevent-fork
function-trace
nofunction-fork
nodisplay-graph
nostacktrace
notest_nop_accept
notest_nop_refuse

Disable某个选项,使用“no”前缀:

echo noprint-parent > trace_options

Enable某个选项,去掉“no”前缀:

echo sym-offset > trace_options
Option说明
print-parent是否打印父函数
sym-offset是否打印符号的offset
sym-addr是否打印符号地址
verbose显示详细信息,这涉及到启用 latency-format 选项时的跟踪文件。
raw显示裸数据。如果用户应用知道裸数据的解析方法,可以在用户态解析,优于在内核解析。
hex使用hex格式解析裸数据。
bin使用bin格式解析裸数据。
block如果设置,读trace_pipe文件不会在轮询时阻塞。
trace_printk是否禁止trace_printk写入数据到ring buffer。
annotate有时当 CPU 缓冲区已满且一个 CPU 缓冲区最近发生了大量事件(因此时间范围较短),而另一个 CPU 可能只有少量事件,这使得它包含了较早的事件,可能会导致混淆。当报告跟踪时,它会先显示最旧的事件,可能会看起来只有一个 CPU 运行(具有最旧事件的那个)。当设置了 annotate 选项时,它会增加提示,显示新 CPU 缓冲区的开始:
-0 [001] dNs4 21169.031481: wake_up_idle_cpu <-add_timer_on
-0 [001] dNs4 21169.031482: _raw_spin_unlock_irqrestore <-add_timer_on
-0 [001] .Ns4 21169.031484: sub_preempt_count <-_raw_spin_unlock_irqrestore
##### CPU 2 buffer started ####
-0 [002] .N.1 21169.031484: rcu_idle_exit <-cpu_idle
-0 [001] .Ns3 21169.031484: _raw_spin_unlock <-clocksource_watchdog
-0 [001] .Ns3 21169.031485: sub_preempt_count <-_raw_spin_unlock
userstacktrace记录用户空间的堆栈回调
sym-userobj记录用户空间的堆栈回调,并且解析出对应符号。这在ASLR使能时非常有用,因为ASLR是随机加载库地址,事后根据堆栈可能恢复不出原来的调用关系。
printk-msg-onlytrace_printk()仅仅打印格式不打印参数
context-info是否打印trace event的头部通用信息:comm, PID, timestamp, CPU, and other useful data.
latency-format是否使用“Latency trace format”打印,解释事件延迟的发生
record-cmd是否在sched_switch时间点来创建pid到comm的转换cache。
record-tgid是否创建pid到tgid的cache
overwrite控制ringbuffer满后的行为是,overwrite旧数据,还是discard新数据。
disable_on_free当使用“set_free_buffer”释放ringbuffer空间时,是否停止tracing。
irq-info是否打印相关flag信息:interrupt, preempt count, need resched
markers是否允许用户态直接通过trace_marker接口往ringbuffer中写数据
event-fork对于trace_event,如果进程在pid filter列表中,fork创建的子进程是否自动加入到filter列表中。
function-trace在latency tracers是否可以同时使能function tracing。
function-fork对于function trace,如果进程在pid filter列表中,fork创建的子进程是否自动加入到filter列表中。
display-graph“function graph tracing”风格的显示
stacktraceevent中是否记录堆栈回调

function_graph tracer特有的选项:

option说明
funcgraph-overrun当设置时,在跟踪每个函数之后显示graph stack的“溢出”。溢出是指调用的堆栈深度大于为每个任务保留的堆栈深度。每个任务在调用图中有一个固定的函数数组进行跟踪。如果调用的深度超过了该值,则不会跟踪该函数。溢出是因为超过该数组而错过的函数数量。
funcgraph-cpu显示所在CPU
funcgraph-overhead显示“delay”程度
funcgraph-proc每一行显示进程comm
funcgraph-duration每个函数返回处显示函数执行时间
funcgraph-abstime每一行显示绝对时间戳
funcgraph-irqs如果disable,将不会跟踪中断中的函数
funcgraph-tail在函数结束时显示函数名
sleep-time函数的执行时间包括进程被调度出去的sleep时间,启用后,将计算任务被调度出的时间作为函数调用的一部分。 没启用的话,每次schd out的时间进行累计,再减掉。
graph-time在使用函数图跟踪器运行函数分析器时,要包括调用嵌套函数的时间。当未设置此选项时,函数报告的时间仅包括函数本身执行的时间,而不包括它调用的函数的时间。
3.1 function tracer使用:查看"vfs_open"函数调用信息
cd /sys/kernel/debug/tracing/
# 先禁用数据写入到 Ring 缓冲区
echo 0 > tracing_on
# 设置当前使用的tracer
echo function > current_tracer
cat current_tracer
function
# 过滤要跟踪的函数
echo vfs_open > set_ftrace_filter
cat set_ftrace_filter
vfs_open
# 创建文件用于测试
mkdir -p /home/idle/study/ftrace/function_tracer
touch /home/idle/study/ftrace/function_tracer/test_vfs_open
echo test_vfs_open > /home/idle/study/ftrace/function_tracer/test_vfs_open
# 使能数据写入到 Ring 缓冲区
echo 1 > tracing_on
# 触发
cat /home/idle/study/ftrace/function_tracer/test_vfs_open
test_vfs_open
# 获取trace结果
cat trace

image.png

  • 首先在头部打印当前tracer的名字,这个例子里tracer是“function”;
  • 然后打印”buffer中有多少个event/总共写了多少个entry”,两者的差值是丢失的entry(17 - 17 = 0 events lost)。
  • 每行的信息解析格式:进程名 “cat”, 进程PID “4033”, 运行的cpu “001”, 被trace的函数 “vfs_open” 父函数 “do_open”。时间戳TIMESTAMP表示的是函数enter的时刻。
field说明
irqs-off中断是否disable:‘d’:中断disable; ‘.’:中断没有disable; ‘X’:架构不支持读取该信息
need-resched需要调度标志:‘N’ both TIF_NEED_RESCHED and PREEMPT_NEED_RESCHED is set, ‘n’ only TIF_NEED_RESCHED is set, ‘p’ only PREEMPT_NEED_RESCHED is set, ‘.’ otherwise.
hardirq/softirq当前所处中断环境:‘Z’ - NMI occurred inside a hardirq, ‘z’ - NMI is running ,‘H’ - hard irq occurred inside a softirq, ‘h’ - hard irq is running, ‘s’ - soft irq is running, ‘.’ - normal context.
preempt-depth禁止抢占的深度,即禁止抢占的计数值。
3.2 function graph tracer使用:函数"vfs_open"的执行时间
cd /sys/kernel/debug/tracing/
# 先禁用数据写入到 Ring 缓冲区
echo 0 > tracing_on
# 清空trace文件
echo 0 > trace
# 设置当前使用的tracer
echo function_graph > current_tracer
cat current_tracer
function_graph
# 过滤要跟踪的函数
echo vfs_open > set_ftrace_filter
cat set_ftrace_filter
vfs_open
# 启用进程TASK/PID打印
echo 1 > options/funcgraph-proc 
# 创建文件用于测试
mkdir -p /home/idle/study/ftrace/function_graph_tracer
touch /home/idle/study/ftrace/function_graph_tracer/test_vfs_open
echo test_vfs_open > /home/idle/study/ftrace/function_graph_tracer/test_vfs_open
# 使能数据写入到 Ring 缓冲区
echo 1 > tracing_on
# 触发
cat /home/idle/study/ftrace/function_graph_tracer/test_vfs_open
test_vfs_open
# 获取trace结果
cat trace

image.png

查看到vfs_open被执行的调试信息包括TASK/PID,函数的执行时间,接口名。原理上是通过在函数的入口点和出口点(kprobe/kretprobe)挂钩子,然后计算入口与出口的时间来实现的。

3.3 function graph tracer使用:函数"vfs_open"向下执行流
cd /sys/kernel/debug/tracing/
# 先禁用数据写入到 Ring 缓冲区
echo 0 > tracing_on
# 清空trace文件
echo 0 > trace
# 设置当前使用的tracer
echo function_graph > current_tracer
cat current_tracer
function_graph
# 不使用过滤!!!
echo  > set_ftrace_filter
cat set_ftrace_filter
#### all functions enabled ####
#  使用函数图表
echo vfs_open > set_graph_function 
cat set_graph_function 
vfs_open
# 打印进程TASK/PID
echo 1 > options/funcgraph-proc 
# 尾部注释(方便观察)
echo 1 > options/funcgraph-tail 
# 创建文件用于测试
mkdir -p /home/idle/study/ftrace/function_graph_tracer
touch /home/idle/study/ftrace/function_graph_tracer/test_vfs_open
echo test_vfs_open > /home/idle/study/ftrace/function_graph_tracer/test_vfs_open
# 使能数据写入到 Ring 缓冲区
echo 1 > tracing_on
# 触发
cat /home/idle/study/ftrace/function_graph_tracer/test_vfs_open
test_vfs_open
# 获取trace结果
cat trace

vfs_open被执行时,能看到向下的执行流程,其中涉及到的函数调用和相关信息会打印出来。

image.png

3.4 wakeup tracer:追踪普通进程从被唤醒到真正得到执行之间的延迟
# 在执行延迟测试时降低跟踪程序的开销,否则会打印函数调用关系
echo 0 > options/function-trace
# 设置tracer
echo wakeup > current_tracer
echo 1 > tracing_on
# 复位最大值
echo 0 > tracing_max_latency
chrt -f 5 sleep 1
echo 0 > tracing_on
cat trace

image.png

wakeup tracer追踪优先级最高的进程,我们看到nice 0的migration进程花了282us从被唤醒到真正执行。

4. Filter commands

ftrace支持通过一系列的command来设置set_ftrace_filter,配置filter。格式如下:

<function>:<command>:<parameter>

支持以下的command:

4.1 mod

该命令使能某个模块的过滤。parameter用来定义模块。例如,追踪ext3模块的write*函数:

echo ‘write*:mod:ext3’ > set_ftrace_filter

追加配置使用’>>’,移除特定模块的函数使用’!’前缀:

echo '!writeback*:mod:ext3' >> set_ftrace_filter

支持模块通配符。例如去掉所有的函数追踪除了某个模块:

echo '!*:mod:!ext3' >> set_ftrace_filter

去掉所有的模块追踪,但是非模块的kernel部分继续追踪:

echo '!*:mod:*' >> set_ftrace_filter
4.2 traceon/traceoff

这个命令在指定的函数被调用时打开/关闭tracing。parameter定义函数命中多少次以后打开/关闭tracing,如果没有描述则不做限制。

例如,disable tracing当schedule bug命中前5次:

echo '__schedule_bug:traceoff:5' > set_ftrace_filter

这些命令是累积的,无论你是否使用追加方式配置到set_ftrace_filter。如果要移除某个命令,使用‘!’前缀并且parameter次数drop到0:

echo '!__schedule_bug:traceoff:0' > set_ftrace_filter
4.3 snapshot

配置CONFIG_TRACER_SNAPSHOT 将会使快照特性有效对于所有的non latency tracers。(Latency tracers仅仅记录最大延迟,例如“irqsoff” or “wakeup”,不能使用这个特性,因为它们已经在内部使用快照机制)。

快照在一个特定时间点保留当前trace buffer而不停止跟踪。ftrace交换当前缓存区和备用缓存区,在新的当前缓存区(之前的备用缓存区)中继续跟踪。

它用来拿到快照并且读出快照。echo 1到文件会分配一个备份的缓存区并且进行交换,然后从快照中读内容使用和trace文件同样的格式输出。读快照和系统tracing并行进行。echo 0到文件会释放备份缓存区,echo其他的正值数会清除快照的内容。

# echo 1 > events/sched/enable
# echo 1 > snapshot
# cat snapshot
# tracer: nop
#
# entries-in-buffer/entries-written: 71/71   #P:8
#
#                              _-----=> irqs-off
#                             / _----=> need-resched
#                            | / _---=> hardirq/softirq
#                            || / _--=> preempt-depth
#                            ||| /     delay
#           TASK-PID   CPU#  ||||    TIMESTAMP  FUNCTION
#              | |       |   ||||       |         |
          <idle>-0     [005] d...  2440.603828: sched_switch: prev_comm=swapper/5 prev_pid=0 prev_prio=120   prev_state=R ==> next_comm=snapshot-test-2 next_pid=2242 next_prio=120
           sleep-2242  [005] d...  2440.603846: sched_switch: prev_comm=snapshot-test-2 prev_pid=2242 prev_prio=120   prev_state=R ==> next_comm=kworker/5:1 next_pid=60 next_prio=120
[...]
        <idle>-0     [002] d...  2440.707230: sched_switch: prev_comm=swapper/2 prev_pid=0 prev_prio=120 prev_state=R ==> next_comm=snapshot-test-2 next_pid=2229 next_prio=120

# cat trace
# tracer: nop
#
# entries-in-buffer/entries-written: 77/77   #P:8
#
#                              _-----=> irqs-off
#                             / _----=> need-resched
#                            | / _---=> hardirq/softirq
#                            || / _--=> preempt-depth
#                            ||| /     delay
#           TASK-PID   CPU#  ||||    TIMESTAMP  FUNCTION
#              | |       |   ||||       |         |
          <idle>-0     [007] d...  2440.707395: sched_switch: prev_comm=swapper/7 prev_pid=0 prev_prio=120 prev_state=R ==> next_comm=snapshot-test-2 next_pid=2243 next_prio=120
 snapshot-test-2-2229  [002] d...  2440.707438: sched_switch: prev_comm=snapshot-test-2 prev_pid=2229 prev_prio=120 prev_state=S ==> next_comm=swapper/2 next_pid=0 next_prio=120
[...]

当函数命中时将会导致一个快照被触发。

echo 'native_flush_tlb_others:snapshot' > set_ftrace_filter

移除上述的配置:

echo '!native_flush_tlb_others:snapshot' > set_ftrace_filter

当你在latency tracers下尝试使用快照,将会得到以下结果:

# echo wakeup > current_tracer
# echo 1 > snapshot
bash: echo: write error: Device or resource busy
# cat snapshot
cat: snapshot: Device or resource busy
4.4 enable_event/disable_event

这些命令可以enable/disable trace event。因为function tracing回调都是非常敏感的,当这些命令注册,这些函数的tracepoint被active但是处于“soft disable”状态,它会被调用但是不会产生记录,这些tracepoint只是用来判断条件是否被触发。

格式如下:

<function>:enable_event:<system>:<event>[:count]
<function>:disable_event:<system>:<event>[:count]
echo 'try_to_wake_up:enable_event:sched:sched_switch:2' > set_ftrace_filter

移除配置:

echo '!try_to_wake_up:enable_event:sched:sched_switch:0' > set_ftrace_filter
4.5 Instances

在tracefs中有个“instances”文件夹,这个文件夹可以使用mkdir创建新的子文件夹、使用rmdir删除子文件夹。如果子文件夹被创建,它默认包含以下文件和文件夹:

# mkdir instances/foo
# ls instances/foo
buffer_size_kb  buffer_total_size_kb  events  free_buffer  per_cpu
set_event  snapshot  trace  trace_clock  trace_marker  trace_options
trace_pipe  tracing_on

新目录中的文件与原tracing目录中同名的文件的工作方式相同,只是所使用的缓冲区是一个单独的新缓冲区。这些文件会影响该缓冲区,但不会影响主缓冲区。

当前我们使用的内核版本linux5.0,已经有current_tracer和available_tracers,可以使用function tracer,但是不支持function_graph。

# cat available_tracers 
function_graph wakeup_dl wakeup_rt wakeup preemptirqsoff preemptoff irqsoff function nop
# mkdir instances/idle
# cat instances/idle/available_tracers 
wakeup_dl wakeup_rt wakeup preemptirqsoff preemptoff irqsoff function nop
# mkdir instances/foo
# echo 100000 > buffer_size_kb
# echo 1000 > instances/foo/buffer_size_kb
# echo function > current_trace
# echo 1 > instances/foo/events/sched/sched_wakeup/enable
# echo 1 > instances/foo/events/sched/sched_wakeup_new/enable
# echo 1 > instances/foo/events/sched/sched_switch/enable
# cat trace_pipe
CPU:2 [LOST 11745 EVENTS]
            bash-2044  [002] .... 10594.481032: _raw_spin_lock_irqsave <-get_page_from_freelist
            bash-2044  [002] d... 10594.481032: add_preempt_count <-_raw_spin_lock_irqsave
            bash-2044  [002] d..1 10594.481032: __rmqueue <-get_page_from_freelist
            bash-2044  [002] d..1 10594.481033: _raw_spin_unlock <-get_page_from_freelist
            bash-2044  [002] d..1 10594.481033: sub_preempt_count <-_raw_spin_unlock
            bash-2044  [002] d... 10594.481033: get_pageblock_flags_group <-get_pageblock_migratetype
            bash-2044  [002] d... 10594.481034: __mod_zone_page_state <-get_page_from_freelist
            bash-2044  [002] d... 10594.481034: zone_statistics <-get_page_from_freelist
            bash-2044  [002] d... 10594.481034: __inc_zone_state <-zone_statistics
            bash-2044  [002] d... 10594.481034: __inc_zone_state <-zone_statistics
            bash-2044  [002] .... 10594.481035: arch_dup_task_struct <-copy_process
[...]

# cat instances/foo/trace_pipe
            bash-1998  [000] d..4   136.676759: sched_wakeup: comm=kworker/0:1 pid=59 prio=120 success=1 target_cpu=000
            bash-1998  [000] dN.4   136.676760: sched_wakeup: comm=bash pid=1998 prio=120 success=1 target_cpu=000
          <idle>-0     [003] d.h3   136.676906: sched_wakeup: comm=rcu_preempt pid=9 prio=120 success=1 target_cpu=003
          <idle>-0     [003] d..3   136.676909: sched_switch: prev_comm=swapper/3 prev_pid=0 prev_prio=120 prev_state=R ==> next_comm=rcu_preempt next_pid=9 next_prio=120
     rcu_preempt-9     [003] d..3   136.676916: sched_switch: prev_comm=rcu_preempt prev_pid=9 prev_prio=120 prev_state=S ==> next_comm=swapper/3 next_pid=0 next_prio=120
            bash-1998  [000] d..4   136.677014: sched_wakeup: comm=kworker/0:1 pid=59 prio=120 success=1 target_cpu=000
            bash-1998  [000] dN.4   136.677016: sched_wakeup: comm=bash pid=1998 prio=120 success=1 target_cpu=000
            bash-1998  [000] d..3   136.677018: sched_switch: prev_comm=bash prev_pid=1998 prev_prio=120 prev_state=R+ ==> next_comm=kworker/0:1 next_pid=59 next_prio=120
     kworker/0:1-59    [000] d..4   136.677022: sched_wakeup: comm=sshd pid=1995 prio=120 success=1 target_cpu=001
     kworker/0:1-59    [000] d..3   136.677025: sched_switch: prev_comm=kworker/0:1 prev_pid=59 prev_prio=120 prev_state=S ==> next_comm=bash next_pid=1998 next_prio=120
[...]

可以看到,top trace buffer只显示function tracing,foo 实例显示wakeups和task switches。

移除实例,只需简单的删除文件夹:

# rmdir instances/foo
# rmdir instances/bar
# rmdir instances/zoot

如果实例中的文件正在open状态,rmdir将会返回EBUSY失败。

4.6 Stack trace

因为kernel拥有固定的堆栈大小,所以在函数中不浪费堆栈是非常重要的。内核开发者必须小心在堆栈上分配内存,如果分配过多,系统可能会有堆栈溢出的危险,并且会发生出错,通常导致系统panic。

有一些工具用来检查这个,但是通常是中断周期性的检查使用率。但是如果你能在每个函数调用中执行这个检查,那将变得非常有用。由于fTrace提供了function trace,因此在每个函数调用中检查堆栈大小是方便的。通过stack tracer来启用。

配置CONFIG_STACK_TRACER 将会包含ftrace stack tracing功能。写 ‘1’ 到 /proc/sys/kernel/stack_tracer_enabled,使能:

# echo 1 > /proc/sys/kernel/stack_tracer_enabled

也可以在kernel启动命令行中使能,在cmdline中增加“stacktrace” 参数。

使能跟踪一段时间后,可以查看最大栈占用情况,stack_max_size这里打印的是最长栈的size。而在stack_trace 中打印的是最长栈的每个函数占用栈大小的情况,注意这里也只会记录的最长的栈情况。

运行一段时间后,看看输出:

# cat stack_max_size
2928

# cat stack_trace
        Depth    Size   Location    (18 entries)
        -----    ----   --------
  0)     2928     224   update_sd_lb_stats+0xbc/0x4ac
  1)     2704     160   find_busiest_group+0x31/0x1f1
  2)     2544     256   load_balance+0xd9/0x662
  3)     2288      80   idle_balance+0xbb/0x130
  4)     2208     128   __schedule+0x26e/0x5b9
  5)     2080      16   schedule+0x64/0x66
  6)     2064     128   schedule_timeout+0x34/0xe0
  7)     1936     112   wait_for_common+0x97/0xf1
  8)     1824      16   wait_for_completion+0x1d/0x1f
  9)     1808     128   flush_work+0xfe/0x119
 10)     1680      16   tty_flush_to_ldisc+0x1e/0x20
 11)     1664      48   input_available_p+0x1d/0x5c
 12)     1616      48   n_tty_poll+0x6d/0x134
 13)     1568      64   tty_poll+0x64/0x7f
 14)     1504     880   do_select+0x31e/0x511
 15)      624     400   core_sys_select+0x177/0x216
 16)      224      96   sys_select+0x91/0xb9
 17)      128     128   system_call_fastpath+0x16/0x1b