接之前文章 《ftrace tracers 之 function、function_graph 使用笔记(一)》
先从简单的代码流程跟踪使用学起,看下具体如何使用 function
和 function_graph
。
功能简介:
1、function
跟踪器可以跟踪内核函数的执行情况,可以通过 set_ftrace_filter
显式指定要跟踪的函数。可以用于跟踪内核某个函数被调用的频次,在被调用的情况。
2、function_graph
与function
跟踪器类似,只是function
跟踪器在函数的入口处探测函数,而function_graph
跟踪器在函数的入口和出口处跟踪,然后使得其提供了绘制类似于 C 代码源的函数调用图的能力。可以通过 set_graph_function
显式指定要生成调用流程图的函数。因此可以用于跟踪内核某个函数内部调用的子函数流程及耗时。
整体架构
基于 ftrace 的内核函数调用跟踪整体架构:
内核配置:
一般用作产品正式发布的版本,默认tracer
应该是 nop
,如果想要使用 其他 tracer
需要打开对应的内核配置宏才行。
比如,今天我们使用到的 function
、function_graph
,需使能如下对应配置内核宏:
CONFIG_HAVE_FUNCTION_TRACER=y
CONFIG_HAVE_FUNCTION_GRAPH_TRACER=y
CONFIG_FUNCTION_TRACER=y
CONFIG_FUNCTION_GRAPH_TRACER=y
如果配置ok,在系统中会看到类似如下信息:
root:/sys/kernel/tracing # cat available_tracers
function_graph function nop ...
接下来就可以正常使用 function
和 function_graph
了。
使用说明
1、准备工作
我们主要是为了单个事件或单个函数跟踪,所以排除其他多余的信息干扰。如果是做具体问题分析,则就要根据实际需要进行设置了。
root:/sys/kernel/tracing # echo 0 > tracing_on
root:/sys/kernel/tracing # echo > set_event
root:/sys/kernel/tracing # echo > trace
2、以 sched_wakeup
这个跟踪事件/跟踪点,跟踪函数为例:
root:/sys/kernel/tracing # cat events/sched/sched_wakeup/format
name: sched_wakeup
ID: 87
format:
field:unsigned short common_type; offset:0; size:2; signed:0;
field:unsigned char common_flags; offset:2; size:1; signed:0;
field:unsigned char common_preempt_count; offset:3; size:1; signed:0;
field:int common_pid; offset:4; size:4; signed:1;
field:char comm[16]; offset:8; size:16; signed:0;
field:pid_t pid; offset:24; size:4; signed:1;
field:int prio; offset:28; size:4; signed:1;
field:int success; offset:32; size:4; signed:1;
field:int target_cpu; offset:36; size:4; signed:1;
print fmt: "comm=%s pid=%d prio=%d target_cpu=%03d", REC->comm, REC->pid, REC->prio, REC->target_cpu
说明:
1、以上 格式的 sched_wakeup 打印信息,由该跟踪点对应的处理函数 sched_wakeup_handler
处理生成。
2、针对 sched_wakeup 进行跟踪的函数为:__traceiter_sched_wakeup
。
2.1、通过function
跟踪看下进行跟踪 sched_wakeup 事件的函数 __traceiter_sched_wakeup
被调用情况。
root:/sys/kernel/tracing # echo function > current_tracer
root:/sys/kernel/tracing # echo 1 > options/func_stack_trace
root:/sys/kernel/tracing # echo __traceiter_sched_wakeup > set_ftrace_filter
root:/sys/kernel/tracing # cat set_ftrace_filter
__traceiter_sched_wakeup
ftrace 设置过滤函数过程需要一点时间,稍等片刻。然后:
root:/sys/kernel/tracing # echo 1 > tracing_on
因为是休眠/唤醒相关的函数,所以需要人为制造一次待机/唤醒动作,然后才能看到跟踪日志,如:
root:/sys/kernel/tracing # cat trace
# tracer: function
#
# entries-in-buffer/entries-written: 1035/1035 #P:8
#
# _-----=> irqs-off
# / _----=> need-resched
# | / _---=> hardirq/softirq
# || / _--=> preempt-depth
# ||| / delay
# TASK-PID CPU# |||| TIMESTAMP FUNCTION
# | | | |||| | |
kworker/0:0-14171 [000] .... 78999.634206: lpi_pinctrl_runtime_resume: lpi_pinctrl_runtime_resume: enter
kworker/0:0-14171 [000] .... 78999.634220: lpi_pinctrl_runtime_resume: lpi_pinctrl_runtime_resume: exit
...
kworker/0:0-14171 [000] .... 78999.634759: swrm_runtime_suspend: swrm_runtime_suspend: pm_runtime: suspend done state: 3
...
kworker/0:0-14171 [000] .... 78999.761684: lpi_pinctrl_runtime_suspend: lpi_pinctrl_runtime_suspend: enter
kworker/0:0-14171 [000] .... 78999.763192: lpi_pinctrl_runtime_suspend: lpi_pinctrl_runtime_suspend: exit
被调用的地方和过程有很多,以上只提取了一部分信息,可以看到 一次对称的 resume / suspend 过程, 在此过程,__traceiter_sched_wakeup
有被 pinctrl 调用的情况。
2.2、通过function_graph
跟踪 看下进行跟踪 sched_wakeup 事件的函数 __traceiter_sched_wakeup
调用关系情况。
通过function
跟踪看下进行跟踪sched_wakeup 的函数 __traceiter_sched_wakeup
被调用情况。
root:/sys/kernel/tracing # echo > set_ftrace_filter
root:/sys/kernel/tracing # echo function_graph > current_tracer
root:/sys/kernel/tracing # echo 1 > options/funcgraph-tail
root:/sys/kernel/tracing # echo __traceiter_sched_wakeup > set_graph_function
root:/sys/kernel/tracing # cat set_graph_function
__traceiter_sched_wakeup
注意,使用 echo > set_ftrace_filter
清除已指定的函数是必须要的操作,否则无法进行 function_graph
绘制。使用echo 1 > options/funcgraph-tail
可以增加函数尾部注释。
ftrace 设置过滤函数过程需要一点时间,稍等片刻。同样,使能 tracing_on 之后,需要人为制造一次休眠唤醒,然后 cat 抓取日志:
root:/sys/kernel/tracing # echo 1 > tracing_on
root:/sys/kernel/tracing # cat trace
# tracer: function_graph
#
# CPU DURATION FUNCTION CALLS
# | | | | | | |
------------------------------------------
1) kworker-14181 => kworker-14620
------------------------------------------
1) | /* lpi_pinctrl_runtime_suspend: enter */
------------------------------------------
1) kworker-14181 => kworker-14620
------------------------------------------
1) | /* lpass_hw_vote_unprepare: core vote clk_id 10a */
1) | /* digital_cdc_rsc_mgr_hw_vote_disable */
1) | /* lpi_pinctrl_runtime_suspend: exit */
以上只是部分信息节选,可能函数 __traceiter_sched_wakeup 本身就是 ftrace 进行跟踪 sched_wakeup 这个 event 的,函数本身并没有复杂的调用流程关系,所以我们看到的 function关系、 function_graph关系也都比较简单。
以上相当于使用 ftrace 跟踪了一个 ftrace 的跟踪事件的函数,^_^
。
下面以一个常规内核函数为例再看下。
3、以常规内核函数 pm_wakeup_ws_event
为例:
3.1、使用function
跟踪 pm_wakeup_ws_event
。
root:/sys/kernel/tracing # echo 0 > tracing_on
root:/sys/kernel/tracing # echo > set_event
root:/sys/kernel/tracing # echo > trace
root:/sys/kernel/tracing # echo > set_graph_function
root:/sys/kernel/tracing # echo function > current_tracer
root:/sys/kernel/tracing # echo 1 > options/func_stack_trace
root:/sys/kernel/tracing # echo pm_wakeup_ws_event > set_ftrace_filter
root:/sys/kernel/tracing # cat set_ftrace_filter
pm_wakeup_ws_event
root:/sys/kernel/tracing # echo 1 > tracing_on
ftrace 设置过滤函数过程需要一点时间,稍等片刻。然后:
root:/sys/kernel/tracing # cat trace
# tracer: function
#
# entries-in-buffer/entries-written: 0/0 #P:8
#
# _-----=> irqs-off
# / _----=> need-resched
# | / _---=> hardirq/softirq
# || / _--=> preempt-depth
# ||| / delay
# TASK-PID CPU# |||| TIMESTAMP FUNCTION
# | | | |||| | |
...
kworker/1:2-16429 [001] .... 127214.479918: lpi_pinctrl_runtime_suspend: lpi_pinctrl_runtime_suspend: enter
...
kworker/1:2-16429 [001] .... 127214.483106: lpi_pinctrl_runtime_suspend: lpi_pinctrl_runtime_suspend: exit
...
3.2、使用function_graph
跟踪 pm_wakeup_ws_event
。
root:/sys/kernel/tracing # echo 0 > tracing_on
root:/sys/kernel/tracing # echo > set_event
root:/sys/kernel/tracing # echo > trace
root:/sys/kernel/tracing # echo > set_ftrace_filter
root:/sys/kernel/tracing # echo function_graph > current_tracer
root:/sys/kernel/tracing # echo 1 > options/funcgraph-tail
root:/sys/kernel/tracing # echo pm_wakeup_ws_event> set_graph_function
root:/sys/kernel/tracing # cat set_graph_function
pm_wakeup_ws_event
root:/sys/kernel/tracing # echo 1 > tracing_on
root:/sys/kernel/tracing # cat trace
ftrace 设置过滤函数过程需要一点时间,稍等片刻。然后:
root:/sys/kernel/tracing # cat trace
root:/sys/kernel/tracing # cat trace| head
# tracer: function_graph
#
# CPU DURATION FUNCTION CALLS
# | | | | | | |
------------------------------------------
1) kworker-14620 => HwBinde-4845
------------------------------------------
1) | pm_wakeup_ws_event() {
1) 1.928 us | _raw_spin_lock_irqsave();
1) | wakeup_source_activate() {
1) | ktime_get() {
1) 1.198 us | arch_counter_read();
1) 3.594 us | } /* ktime_get */
1) 6.615 us | } /* wakeup_source_activate */
1) | s2idle_wake() {
1) 1.146 us | _raw_spin_lock_irqsave();
1) 1.145 us | _raw_spin_unlock_irqrestore();
1) 5.469 us | } /* s2idle_wake */
1) | wakeup_source_deactivate() {
1) | ktime_get() {
1) 1.197 us | arch_counter_read();
1) 3.333 us | } /* ktime_get */
1) 1.146 us | del_timer();
1) 7.865 us | } /* wakeup_source_deactivate */
1) 1.146 us | _raw_spin_unlock_irqrestore();
1) + 38.334 us | } /* pm_wakeup_ws_event */
1) | pm_wakeup_ws_event() {
1) 1.562 us | _raw_spin_lock_irqsave();
1) | wakeup_source_activate() {
1) | ktime_get() {
1) 1.198 us | arch_counter_read();
1) 3.386 us | } /* ktime_get */
1) 5.625 us | } /* wakeup_source_activate */
1) | s2idle_wake() {
1) 1.146 us | _raw_spin_lock_irqsave();
1) 1.146 us | _raw_spin_unlock_irqrestore();
1) 5.365 us | } /* s2idle_wake */
1) | wakeup_source_deactivate() {
1) | ktime_get() {
1) 1.146 us | arch_counter_read();
1) 3.281 us | } /* ktime_get */
1) 1.198 us | del_timer();
1) 7.761 us | } /* wakeup_source_deactivate */
1) 1.875 us | _raw_spin_unlock_irqrestore();
1) + 30.156 us | } /* pm_wakeup_ws_event */
------------------------------------------
root:/sys/kernel/tracing # ps -A| grep 14620
root 14620 2 0 0 worker_thread 0 I [kworker/1:2-events]
3.3 、函数 pm_wakeup_ws_event 原型。
/** drivers/base/power/wakeup.c
* pm_wakeup_ws_event - Notify the PM core of a wakeup event.
* @ws: Wakeup source object associated with the event source.与事件源关联的唤醒源对象。
* @msec: Anticipated event processing time (in milliseconds).预期的事件处理时间(以毫秒为单位)。
* @hard: If set, abort suspends in progress and wake up from suspend-to-idle.
*
* Notify the PM core of a wakeup event whose source is @ws that will take approximately @msec milliseconds to be processed by the kernel. If @ws is not active, activate it. If @msec is nonzero, set up the @ws' timer to execute pm_wakeup_timer_fn() in future.
* 通知 PM 内核有一个唤醒事件,它的源是 @ws,内核处理大约需要 @msec 毫秒。 如果@ws 未激活,请激活它。 如果@msec 不为零,则设置@ws 的计时器以在将来执行 pm_wakeup_timer_fn()。
*
* It is safe to call this function from interrupt context. 在中断上下文调用这个函数是安全的。
*/
void pm_wakeup_ws_event(struct wakeup_source *ws, unsigned int msec, bool hard)
{
unsigned long flags;
unsigned long expires;
if (!ws)
return;
spin_lock_irqsave(&ws->lock, flags);
wakeup_source_report_event(ws, hard);
if (!msec) {
wakeup_source_deactivate(ws);
goto unlock;
}
expires = jiffies + msecs_to_jiffies(msec);
if (!expires)
expires = 1;
if (!ws->timer_expires || time_after(expires, ws->timer_expires)) {
mod_timer(&ws->timer, expires);
ws->timer_expires = expires;
}
unlock:
spin_unlock_irqrestore(&ws->lock, flags);
}
EXPORT_SYMBOL_GPL(pm_wakeup_ws_event);
ref:
https://baijiahao.baidu.com/s?id=1727506047428430246&wfr=spider&for=pc
https://support.huaweicloud.com/moredocuments-kunpengdevps/kunpengsys_06_0100_0.html