使用 ftrace 调试 Linux 内核,第 2 部分
ftrace 操作概述
使用 ftrace 提供的跟踪器来调试或者分析内核时需要如下操作:
- 切换到目录 /sys/kernel/debug/tracing/ 下
- 查看 available_tracers 文件,获取当前内核支持的跟踪器列表
- 关闭 ftrace 跟踪,即将 0 写入文件 tracing_enabled
- 激活 ftrace_enabled ,否则 function 跟踪器的行为类似于 nop;另外,激活该选项还可以让一些跟踪器比如 irqsoff 获取更丰富的信息。建议使用 ftrace 时将其激活。要激活 ftrace_enabled ,可以通过 proc 文件系统接口来设置:
echo 1 > /proc/sys/kernel/ftrace_enabled
- 将所选择的跟踪器的名字写入文件 current_tracer
- 将要跟踪的函数写入文件 set_ftrace_filter ,将不希望跟踪的函数写入文件 set_ftrace_notrace。通常直接操作文件 set_ftrace_filter 就可以了
- 激活 ftrace 跟踪,即将 1 写入文件 tracing_enabled。还要确保文件 tracing_on 的值也为 1,该文件可以控制跟踪的暂停
- 如果是对应用程序进行分析的话,启动应用程序的执行,ftrace 会跟踪应用程序运行期间内核的运作情况
- 通过将 0 写入文件 tracing_on 来暂停跟踪信息的记录,此时跟踪器还在跟踪内核的运行,只是不再向文件 trace 中写入跟踪信息;或者将 0 写入文件 tracing_enabled 来关闭跟踪
- 查看文件 trace 获取跟踪信息,对内核的运行进行分析调试
接下来将对跟踪器的使用以及跟踪信息的格式通过实例加以讲解。
fucntion 跟踪器
function 跟踪器可以跟踪内核函数的调用情况,可用于调试或者分析 bug ,还可用于了解和观察 Linux 内核的执行过程。清单 1 给出了使用 function 跟踪器的示例。
清单 1. function 跟踪器使用示例
[root@linux tracing]# pwd
/sys/kernel/debug/tracing
[root@linux tracing]# echo 0 > tracing_enabled
[root@linux tracing]# echo 1 > /proc/sys/kernel/ftrace_enabled
[root@linux tracing]# echo function > current_tracer
[root@linux tracing]# echo 1 > tracing_on
[root@linux tracing]# echo 1 > tracing_enabled # 让内核运行一段时间,这样 ftrace 可以收集一些跟踪信息,之后再停止跟踪 [root@linux tracing]# echo 0 > tracing_enabled
[root@linux tracing]# cat trace | head -10
# tracer: function
#
# TASK-PID CPU# TIMESTAMP FUNCTION
# | | | | |
<idle>-0 [000] 20654.426521: _raw_spin_lock <-scheduler_tick
<idle>-0 [000] 20654.426522: task_tick_idle <-scheduler_tick
<idle>-0 [000] 20654.426522: cpumask_weight <-scheduler_tick
<idle>-0 [000] 20654.426523: cpumask_weight <-scheduler_tick
<idle>-0 [000] 20654.426523: run_posix_cpu_timers <-update_process_times
<idle>-0 [000] 20654.426524: hrtimer_forward <-tick_sched_timer
trace 文件给出的信息格式很清晰。首先,字段“tracer:”给出了当前所使用的跟踪器的名字,这里为 function 跟踪器。然后是跟踪信息记录的格式,TASK 字段对应任务的名字,PID 字段则给出了任务的进程 ID,字段 CPU# 表示运行被跟踪函数的 CPU 号,这里可以看到 idle 进程运行在 0 号 CPU 上,其进程 ID 是 0 ;字段 TIMESTAMP 是时间戳,其格式为“<secs>.<usecs>”
,表示执行该函数时对应的时间戳;FUNCTION 一列则给出了被跟踪的函数,函数的调用者通过符号 “<-” 标明,这样可以观察到函数的调用关系。
function_graph 跟踪器
在 function 跟踪器给出的信息中,可以通过 FUNCTION 列中的符号 “<-” 来查看函数调用关系,但是由于中间会混合不同函数的调用,导致看起来非常混乱,十分不方便。function_graph 跟踪器则可以提供类似 C 代码的函数调用关系信息。通过写文件 set_graph_function 可以显示指定要生成调用关系的函数,缺省会对所有可跟踪的内核函数生成函数调用关系图。清单 2 给出了使用 function_grapch 跟踪器的示例,示例中将内核函数 __do_fault 作为观察对象,该函数在内核运作过程中会被频繁调用。
清单 2. function_graph 跟踪器使用示例
[root@linux tracing]# pwd
/sys/kernel/debug/tracing
[root@linux tracing]# echo 0 > tracing_enabled
[root@linux tracing]# echo 1 > /proc/sys/kernel/ftrace_enabled
[root@linux tracing]# echo function_graph > current_tracer
[root@linux tracing]# echo __do_fault > set_graph_function
[root@linux tracing]# echo 1 > tracing_on
[root@linux tracing]# echo 1 > tracing_enabled # 让内核运行一段时间,这样 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
在文件 trace 的输出信息中,首先给出的也是当前跟踪器的名字,这里是 function_graph 。接下来是详细的跟踪信息,可以看到,函数的调用关系以类似 C 代码的形式组织。
CPU 字段给出了执行函数的 CPU 号,本例中都为 1 号 CPU。DURATION 字段给出了函数执行的时间长度,以 us 为单位。FUNCTION CALLS 则给出了调用的函数,并显示了调用流程。注意,对于不调用其它函数的函数,其对应行以“;”结尾,而且对应的 DURATION 字段给出其运行时长;对于调用其它函数的函数,则在其“}”对应行给出了运行时长,该时间是一个累加值,包括了其内部调用的函数的执行时长。DURATION 字段给出的时长并不是精确的,它还包含了执行 ftrace 自身的代码所耗费的时间,所以示例中将内部函数时长累加得到的结果会与对应的外围调用函数的执行时长并不一致;不过通过该字段还是可以大致了解函数在时间上的运行开销的。清单 2 中最后通过 echo 命令重置了文件 set_graph_function 。
sched_switch 跟踪器
sched_switch 跟踪器可以对进程的调度切换以及之间的唤醒操作进行跟踪,如清单 3 所示。
清单 3. sched_switch 跟踪器使用示例
[root@linux tracing]# pwd
/sys/kernel/debug/tracing
[root@linux tracing]# echo 0 > tracing_enabled
[root@linux tracing]# echo 1 > /proc/sys/kernel/ftrace_enabled
[root@linux tracing]# echo sched_switch > current_tracer
[root@linux tracing]# echo 1 > tracing_on
[root@linux tracing]# echo 1 > tracing_enabled # 让内核运行一段时间,这样 ftrace 可以收集一些跟踪信息,之后再停止跟踪 [root@linux tracing]# echo 0 > tracing_enabled
[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
在 sched_swich 跟踪器获取的跟踪信息中记录了进程间的唤醒操作和调度切换信息,可以通过符号‘ + ’和‘ ==> ’区分;唤醒操作记录给出了当前进程唤醒运行的进程,进程调度切换记录中显示了接替当前进程运行的后续进程。
描述进程状态的格式为“Task-PID:Priority:Task-State”。以示例跟踪信息中的第一条跟踪记录为例,可以看到进程 bash 的 PID 为 1408 ,其对应的内核态优先级为 120 ,当前状态为 S(可中断睡眠状态),当前 bash 并没有唤醒其它进程;从第 3 条记录可以看到,进程 bash 将进程 events/0 唤醒,而在第 4 条记录中发生了进程调度,进程 bash 切换到进程 events/0 执行。
在 Linux 内核中,进程的状态在内核头文件 include/linux/sched.h 中定义,包括可运行状态 TASK_RUNNING(对应跟踪信息中的符号‘ R ’)、可中断阻塞状态 TASK_INTERRUPTIBLE(对应跟踪信息中的符号‘ S ’)等。同时该头文件也定义了用户态进程所使用的优先级的范围,最小值为 MAX_USER_RT_PRIO(值为 100 ),最大值为 MAX_PRIO - 1(对应值为 139 ),缺省为 DEFAULT_PRIO(值为 120 );在本例中,进程优先级都是缺省值 120 。
irqsoff 跟踪器
当关闭中断时,CPU 会延迟对设备的状态变化做出反应,有时候这样做会对系统性能造成比较大的影响。irqsoff 跟踪器可以对中断被关闭的状况进行跟踪,有助于发现导致较大延迟的代码;当出现最大延迟时,跟踪器会记录导致延迟的跟踪信息,文件 tracing_max_latency 则记录中断被关闭的最大延时。
清单 4. irqsoff 跟踪器使用示例
[root@linux tracing]# pwd
/sys/kernel/debug/tracing
[root@linux tracing]# echo 0 > tracing_enabled
[root@linux tracing]# echo 1 > /proc/sys/kernel/ftrace_enabled
[root@linux tracing]# echo irqsoff > current_tracer
[root@linux tracing]# echo 1 > tracing_on
[root@linux tracing]# echo 1 > tracing_enabled # 让内核运行一段时间,这样 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
从清单 4 中的输出信息中,可以看到当前 irqsoff 延迟跟踪器的版本信息。接下来是最大延迟时间,以 us 为单位,本例中为 34380 us ,查看文件 tracing_max_latency 也可以获取该值。从“task:”字段可以知道延迟发生时正在运行的进程为 idle(其 pid 为 0 )。中断的关闭操作是在函数 reschedule_interrupt 中进行的,由“=> started at:”标识,函数 restore_all_ontrace 重新激活了中断,由“=> ended at:”标识;中断关闭的最大延迟发生在函数 trace_hardirqs_on_thunk 中,这个可以从最大延迟时间所在的记录项看到,也可以从延迟记录信息中最后的“=>”标识所对应的记录行知道这一点。
在输出信息中,irqs-off、need_resched 等字段对应于进程结构 struct task_struct 的字段或者状态标志,可以从头文件 arch/<platform>/include/asm/thread_info.h 中查看进程支持的状态标志,include/linux/sched.h 则给出了结构 struct task_struct 的定义。其中,irqs-off 字段显示是否中断被禁止,为‘ d ’表示中断被禁止;need_resched 字段显示为‘ N ’表示设置了进程状态标志 TIF_NEED_RESCHED。hardirq/softirq 字段表示当前是否发生硬件中断 / 软中断;preempt-depth 表示是否禁止进程抢占,例如在持有自旋锁的情况下进程是不能被抢占的,本例中进程 idle 是可以被其它进程抢占的。结构 struct task_struct 中的 lock_depth 字段是与大内核锁相关的,而最近的内核开发工作中有一部分是与移除大内核锁相关的,这里对该字段不再加以描述。
另外,还有用于跟踪禁止进程抢占的跟踪器 preemptoff 和跟踪中断 / 抢占禁止的跟踪器 preemptirqsoff,它们的使用方式与输出信息格式与 irqsoff 跟踪器类似,这里不再赘述。
跟踪指定模块中的函数
前面提过,通过文件 set_ftrace_filter 可以指定要跟踪的函数,缺省目标为所有可跟踪的内核函数;可以将感兴趣的函数通过 echo 写入该文件。为了方便使用,set_ftrace_filter 文件还支持简单格式的通配符。
- begin*选择所有名字以 begin 字串开头的函数
- *middle*选择所有名字中包含 middle 字串的函数
- *end选择所有名字以 end 字串结尾的函数
需要注意的是,这三种形式不能组合使用,比如“begin*middle*end”实际的效果与“begin”相同。另外,使用通配符表达式时,需要用单引号将其括起来,如果使用双引号,shell 可能会对字符‘ * ’进行扩展,这样最终跟踪的对象可能与目标函数不一样。
通过该文件还可以指定属于特定模块的函数,这要用到 mod 指令。指定模块的格式为:
echo ':mod:[module_name]' > set_ftrace_filter
下面给出了一个指定跟踪模块 ipv6 中的函数的例子。可以看到,指定跟踪模块 ipv6 中的函数会将文件 set_ftrace_filter 的内容设置为只包含该模块中的函数。
清单 5. 指定跟踪 ipv6 模块中的函数
[root@linux tracing]# pwd
/sys/kernel/debug/tracing
[root@linux tracing]# echo ':mod:ipv6' > set_ftrace_filter
[root@linux tracing]# cat set_ftrace_filter | head -5
ipv6_opt_accepted
inet6_net_exit
ipv6_gro_complete
inet6_create
ipv6_addr_copy
小结
本系列文章对 ftrace 的配置和使用进行了介绍。本文是其中的第二部分,介绍了 ftrace 所提供的跟踪器的使用,重点对不同格式的跟踪信息进行了描述,并对如何跟踪指定模块中的函数进行了介绍。当前 Linux 内核中 ftrace 支持多个跟踪器,由于篇幅限制,这里不能对其全部加以描述。读者可参照参考资料中给出的信息对 ftrace 跟踪器进行更深入的了解。下一篇文章将对如何在代码中通过 ftrace 提供的工具函数与 ftrace 交互进行描述。
使用 ftrace 调试 Linux 内核,第 2 部分的更多相关文章
- 使用 ftrace 调试 Linux 内核【转】
转自:http://blog.csdn.net/adaptiver/article/details/7930646 使用 ftrace 调试 Linux 内核,第 1 部分 http://blog.c ...
- 使用 ftrace 调试 Linux 内核,第 1 部分【转】
转自:http://www.ibm.com/developerworks/cn/linux/l-cn-ftrace1/index.html ftrace 是 Linux 内核中提供的一种调试工具.使用 ...
- 使用 ftrace 调试 Linux 内核,第1部分
ftrace 是 Linux 内核中提供的一种调试工具.使用 ftrace 可以对内核中发生的事情进行跟踪,这在调试 bug 或者分析内核时非常有用.本系列文章对 ftrace 进行了介绍,分为三部分 ...
- 使用 ftrace 调试 Linux 内核,第 3 部分
内核头文件 include/linux/kernel.h 中描述了 ftrace 提供的工具函数的原型,这些函数包括 trace_printk.tracing_on/tracing_off 等.本文通 ...
- 使用QEMU调试Linux内核代码
http://blog.chinaunix.net/uid-20729583-id-1884617.html http://www.linuxidc.com/Linux/2014-08/105510. ...
- 【转】TI-Davinci开发系列之六CCS5.2调试Linux内核
上转博文<TI-Davinci开发系列之五CCS5.2使用gdbserver远程调试应用程序> 使用CCS5.2远程调试内核时,只需导入Linux内核源码,而不需要编译内核,也就不会用到交 ...
- Gdb远程调试Linux内核遇到的Bug
知识共享许可协议本作品采用知识共享署名 4.0 国际许可协议进行许可.转载保留声明头部与原文链接https://luzeshu.com/blog/gdb-bug 本博客同步在http://www.cn ...
- VELT-0.1.5开发:使用kgdb调试Linux内核【转】
转自:http://demo.netfoucs.com/lights_joy/article/details/44106589 VELT的全称是Visual EmbedLinuxTools,它是一个与 ...
- 跟踪调试Linux内核的启动过程
跟踪调试Linux内核的启动过程---使用gdb 符钰婧 原创作品转载请注明出处 <Linux内核分析>MOOC课程http://mooc.study.163.com/course/UST ...
随机推荐
- Luogu P4609 [FJOI2016]建筑师&&CF 960G Bandit Blues
考虑转化题意,我们发现其实就是找一个长度为\(n\)的全排列,使得这个排列有\(A\)个前缀最大值,\(B\)个后缀最大值,求方案数 我们考虑把最大值拎出来单独考虑,同时定义一些数的顺序排列为单调块( ...
- idea 发布和本地测试问题
1.maven本地打包成jar 提示[错误: 找不到或无法加载主类]修改 配置maven ---->Runner---->VM Optins [-DarchetypeCatalog=loc ...
- k8s 创建资源的两种方式【转】
命令 vs 配置文件 Kubernetes 支持两种方式创建资源: 1. 用 kubectl 命令直接创建,比如: kubectl run nginx-deployment --image=nginx ...
- C++判断两个double类型双精度浮点数是否同号
看到的一种整数的方法 != y < ) 由此, double x,y; == fabs( ) { } 目前想到的比较合适判断方法. 此外这里还有一种强制转换类型求符号位的方法. /** * Ge ...
- rf统计条数
js模式 直接引用关键字模式
- [BZOJ3631]:[JLOI2014]松鼠的新家(LCA+树上差分)
题目传送门 题目描述: 松鼠的新家是一棵树,前几天刚刚装修了新家,新家有n个房间,并且有n-1根树枝连接,每个房间都可以相互到达,且俩个房间之间的路线都是唯一的.天哪,他居然真的住在“树”上.松鼠想邀 ...
- Ueditor1.4.3上传视频IE下无法播放的问题
一:百度编辑器插入视频后,自动生成一段代码: <video class="edui-upload-video vjs-default-skin video-js" contr ...
- linux文本界面../和./的区别
linux文本界面../和./的区别 ../代表的是上一个目录 ./代表的当前目录
- Ubuntu 15.04 Qt5 链接 mysql数据库
序 最近在Ubuntu15.04下做一个Linux-服务器-客户端通信项目,用到MySQL数据库.开始的时候,在数据库链接时遇到障碍,查找资料解决. 特此记录,分享于此. 环境配置 系统:Ubuntu ...
- Oracle常用查询语句
"ORACLE数据字典视图的种类分别为:USER,ALL 和 DBA. USER_*:有关用户所拥有的对象信息,即用户自己创建的对象信息 ALL_*:有关用户可以访问的对象的信息,即用户自己 ...