大家好!我是大聪明-PLUS!
跟踪是查找软件中的错误的一种有用技术,而ftrace是 Linux 内核内置的跟踪框架。
但在开始讨论 ftrace 之前,让我们先定义一下什么是跟踪。
什么是追踪?
跟踪是日志记录的一种特殊用途,用于记录有关程序执行流程的信息。
您知道什么时候在代码中添加打印消息来调试吗?您正在使用自己的"跟踪系统"跟踪应用程序。添加到代码中的调试消息是静态跟踪点,您的"跟踪系统"可能会将调试消息发送到程序的标准输出或日志文件。
嗯,这确实有效。但扩展性不好。每次你想跟踪代码的某个部分,你都必须添加新的调试消息并重新构建代码。而且你无法控制消息的去向。如果你想保存它们以便稍后分析怎么办?如果你想过滤消息怎么办?而且代码会变得一团糟,充斥着大量的打印语句(你必须非常小心,不要提交任何带有调试消息的代码,对吧?)。
事实上,大多数时候我们不需要在代码中添加调试消息,因为已经有跟踪工具和框架可以为我们做到这一点。
这就是 ftrace 的用武之地......
什么是 ftrace?
Ftrace是Linux内核的一个跟踪框架。它于2008年被添加到内核中,此后得到了长足的发展。
Ftrace 是函数跟踪器 的缩写,主要用于观察和记录内核函数的执行流程。它由 Steven Rostedt 创建,源自Ingo Molnar 的另外两个工具: latency tracer和 Steven 的 logdev实用程序。
使用 ftrace,您可以真正了解内核的运行情况。您可以跟踪函数调用,从而深入了解内核的工作原理。您可以找出运行用户空间应用程序时调用了哪些内核函数。您可以分析函数,测量执行时间,并找出瓶颈和性能问题。您可以识别内核空间中的挂起。您可以测量运行实时任务所需的时间,并找出延迟问题。您可以测量内核空间中的堆栈使用情况,并找出可能的堆栈溢出。您可以做很多事情来监控和查找内核中的错误!
黑魔法?或许有点儿像
但它是怎么运作的呢?
ftrace 如何工作?
跟踪主要有两种类型:静态跟踪和动态跟踪。
静态追踪是通过在源代码中添加静态探针来实现的。它们的处理负载较低,但追踪的代码有限,且是在构建时定义的。
动态追踪是通过在代码中注入动态探针来实现的,允许在运行时定义需要追踪的代码。它有一定的处理负载,但需要追踪的源代码范围要大得多。
Ftrace 结合使用静态探测(函数跟踪、事件跟踪等)和动态探测(kprobes、uprobes 等)。
为了跟踪函数调用,ftrace 将使用 GCC 的-pg选项构建内核。
当使用-pg构建内核时,GCC 会将机器指令添加到每个非内联函数的序言中,这些指令会将执行重定向到执行实际跟踪的 ftrace 的跳马和跟踪器插件。
下面的反汇编代码是gpiod_set_value 函数的开头部分。第 10 行对*__gnu_mcount_nc* 的调用是由 GCC 添加的,ftrace 将使用它来跟踪该函数(这与gprof用于分析用户空间应用程序的技术相同)。
|----------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| ` 1 2 3 4 5 6 7 8 9 10 11 12 `
| `$ arm-linux-gdb -q vmlinux Reading symbols from vmlinux...done. (gdb) disassemble /m gpiod_set_value Dump of assembler code for function gpiod_set_value: 3133 { 0xc03a83ac : mov r12, sp 0xc03a83b0 : push {r4, r5, r11, r12, lr, pc} 0xc03a83b4 : sub r11, r12, #4 0xc03a83b8 : push {lr} ; (str lr, [sp, #-4]!) 0xc03a83bc : bl 0xc001b420 0xc03a83c0 : mov r5, r1 0xc03a83c8 : mov r4, r0`
|
Ftrace 还能够跟踪内核中的事件。
事件是开发人员添加的静态跟踪点,用于监视内核子系统,如调度程序、电源管理、中断、网络、gpio 等。只需在 Linux 内核源代码中搜索以*trace_*开头的函数,您就会发现几个使用跟踪事件的地方:
`$ grep -R trace_ drivers/gpio/*
drivers/gpio/gpiolib.c: trace_gpio_direction(desc_to_gpio(desc), 1, status);
drivers/gpio/gpiolib.c: trace_gpio_value(desc_to_gpio(desc), 0, val);
drivers/gpio/gpiolib.c: trace_gpio_direction(desc_to_gpio(desc), 0, ret);
drivers/gpio/gpiolib.c: trace_gpio_value(desc_to_gpio(desc), 1, value);
drivers/gpio/gpiolib.c: trace_gpio_value(desc_to_gpio(desc), 1, value);
drivers/gpio/gpiolib.c: trace_gpio_direction(desc_to_gpio(desc), value, err);
drivers/gpio/gpiolib.c: trace_gpio_direction(desc_to_gpio(desc), !value, err);
drivers/gpio/gpiolib.c: trace_gpio_value(desc_to_gpio(desc), 0, value);
drivers/gpio/gpiolib.c: trace_gpio_value(desc_to_gpio(desc), 0, value);`
那么如何与 ftrace 交互呢?
当启用跟踪时,所有收集到的跟踪数据将由 ftrace 存储在内存中的环形缓冲区中。
有一个名为 tracefs 的虚拟文件系统(通常挂载在*/sys/kernel/tracing*中)用于配置 ftrace 并收集跟踪数据。所有操作都通过此目录中的简单文件操作完成。
这就是我喜欢 ftrace 的原因之一。由于跟踪系统的接口是文件系统,你可以使用echo 和cat等简单工具来跟踪 Linux 内核!
那么如何使用它呢?
如何使用 ftrace?
首先,我们应该在 Linux 内核中启用 ftrace(CONFIG_FTRACE)以及我们计划使用的所有跟踪器和选项。这是内核版本 4.18 的 menuconfig 屏幕,其中启用了大多数 ftrace 选项:
启用 ftrace 后,我们可以挂载 tracefs 虚拟文件系统:
`# mount -t tracefs tracefs /sys/kernel/tracing`
这是 ftrace 提供的文件系统接口:
`# ls /sys/kernel/tracing/
README set_ftrace_filter
available_events set_ftrace_notrace
available_filter_functions set_ftrace_pid
available_tracers set_graph_function
buffer_size_kb set_graph_notrace
buffer_total_size_kb snapshot
current_tracer stack_max_size
dyn_ftrace_total_info stack_trace
enabled_functions stack_trace_filter
events timestamp_mode
free_buffer trace
function_profile_enabled trace_clock
hwlat_detector trace_marker
instances trace_marker_raw
max_graph_depth trace_options
options trace_pipe
per_cpu trace_stat
printk_formats tracing_cpumask
saved_cmdlines tracing_max_latency
saved_cmdlines_size tracing_on
saved_tgids tracing_thresh
set_event uprobe_events
set_event_pid uprobe_profile`
我们可以打印可用跟踪器的列表:
`# cat available_tracers
hwlat blk function_graph wakeup_dl wakeup_rt
wakeup irqsoff function nop`
有函数跟踪器(function 、function_graph )、延迟跟踪器(wakeup_dl 、wakeup_rt 、irqsoff 、wakeup 、hwlat )、I/O 跟踪器(blk)等等!
要启用跟踪器,我们只需将其名称写入current_tracer:
`# echo function > current_tracer`
我们可以使用trace 或trace_pipe文件读取跟踪缓冲区:
`# cat trace
# tracer: function
#
# _-----=> irqs-off
# / _----=> need-resched
# | / _---=> hardirq/softirq
# || / _--=> preempt-depth
# ||| / delay
# TASK-PID CPU# |||| TIMESTAMP FUNCTION
# | | | |||| | |
-0 [001] d... 23.695208: _raw_spin_lock_irqsave`