[Android][kernel][Linux]使用ftrace抓取IO相关信息

[Android][kernel][Linux]使用ftrace抓取IO相关信息

ftrace介绍

官方介绍可以在kernel/Documentation/trace/ftrace.txt中查看,如下是截取片段后的翻译结果:

ftrace (function trace) 是旨在帮助开发者探究内核内部运行逻辑的一个追踪器。可用于调试或分析内核里面的高延迟性能问题;

虽然ftrace从命名来看是针对函数的追踪器,但是实际上它已经成为了一个整合了多种追踪器的工具框架。其不仅可以显示中断开关的耗时,也可以追踪到任务从唤醒到加入执行队列之间由于资源抢占导致的时间差;

ftrace中一个最常用的使用方式是事件追踪(event tracing)。其可以通过debugfs文件系统启用内核中数百个静态事件埋点,来跟踪内核中某一特定模块的运行情况;

ftrace常用概念

tracer

tracer - 顾名思义,追踪器,不同追踪器各司其职,用在不同的场景。以Android kernel 3.18为例,目前调试的手机有如下三种追踪器可用:

xxx:/ # cat /sys/kernel/debug/tracing/available_tracers                                                                                                                                                           
function_graph function nop

根据官方介绍,如下是翻译后的解释:

  1. function - 记录kernel内部所有函数调用

  2. function_graph - 与function类似,但是function仅仅记录函数入口,而function_graph会记录入口与出口,从而可以生成一份类似C语言源代码格式的图表;

  3. nop - 'trace nothing’跟踪器,顾名思义,当current_tracer节点为nop时,不会启用任何追踪器;

要切换追踪器很简单,只需要将如上可用的追踪器名字写入current_tracer节点即可:

echo function_graph > /sys/kernel/debug/tracing/current_tracer

event

event - 事件,用于过滤需要记录的事件,只有设置在set_event节点中的事件才会被抓取;默认该节点内容为空;

xxx:/ # cat /sys/kernel/debug/tracing/set_event                                                                                                                                                                   
xxx:/ # 

至于可用的event标签,则可以通过/sys/kernel/debug/tracing/available_events节点查看,由于太多,此处就不一一列举;

另外还有一个方式可以查看支持的event标签,那就是/sys/kernel/debug/tracing/events/目录下的子目录:

xxx:/ # ls /sys/kernel/debug/tracing/events/                                                                                                                                                                      
almk       cma                 enable    filelock     i2c   jbd2            mmc           msm_vidc            power           rcu        rpm_smd     skb     thermal wil6210   
android_fs compaction          exception filemap      iommu kgsl            module        napi                printk          regmap     sched       sock    timer   workqueue 
asoc       cpufreq_interactive ext3      ftrace       ipa   kmem            msm_bus       net                 process_reclaim regulator  scm         spi     udp     writeback 
binder     devfreq             ext4      gpio         ipi   lowmemorykiller msm_cam       oom                 random          rmnet_data scsi        swiotlb ufs     xhci-hcd  
block      dwc3                f2fs      header_event irq   mdss            msm_core      pagemap             ras             rndis_ipa  sde_rotator sync    v4l2    
cfg80211   emulation           fence     header_page  jbd   migrate         msm_low_power perf_trace_counters raw_syscalls    rpm        signal      task    vmscan

因此,启用某个event也有两种方式:

  1. 使用echo 1 > /sys/kernel/debug/tracing/events/<$event>/enable来启用某类标签;
  2. 使用echo <$event> > /sys/kernel/debug/tracing/set_event来启用某类标签;

两者是等效的,并且可以在另一个方式所示的节点中通过cat来确认状态;

xxx:/ # cat /sys/kernel/debug/tracing/events/android_fs/enable                                                                                                                                                
0
xxx:/ # echo android_fs >  /sys/kernel/debug/tracing/set_event                                                                                                                                                    
xxx:/ # cat /sys/kernel/debug/tracing/events/android_fs/enable                                                                                                                                                    
1

filter

filter - 过滤器,用于过滤函数,避免抓取的trace无效信息过多;
可用的filter很多,可从节点/sys/kernel/debug/tracing/available_filter_functions中查看;

cat /sys/kernel/debug/tracing/available_filter_functions

默认不进行过滤,若需要过滤函数,则将对应的函数名写入/sys/kernel/debug/tracing/set_ftrace_filter,例如:

echo vfs_write > /sys/kernel/debug/tracing/set_ftrace_filter

抓取

ftrace一般抓取信息量很大,因此建议增大buffer以确保信息完整:

echo 32768 > /sys/kernel/debug/tracing/buffer_size_kb

抓取前后建议清理cache,以保证结果不受缓存影响:

echo 3 > /proc/sys/vm/drop_caches

如果不想保留之前抓取的trace,则可以将之前的记录清空:

echo "" >  /sys/kernel/debug/tracing/trace

准备就绪后,就可以复现问题并抓取了:

echo 1 > /sys/kernel/debug/tracing/tracing_on

抓取时尽量保证操作单一,操作不要太复杂,否则trace可阅读性会比较差;
复现完毕后,通过如下指令关闭ftrace录制:

echo 0 > /sys/kernel/debug/tracing/tracing_on

最后,将录制的trace文件拉出并使用文本编辑器打开阅读:

adb pull /sys/kernel/debug/tracing/trace ~/Desktop/trace.txt

trace文件一般比较大,建议使用比较省内存的文本编辑器打开(图形化工具推荐sublime text);

示例

比如,如果遇到了文件写入速度慢的问题,想确认一下到底是哪一阶段出现了问题,那么可以通过如下步骤抓取:

echo 'mmc android_fs block sched_switch sched_wakeup sched_blocked_reason *****'  > /sys/kernel/debug/tracing/set_event
echo 'function_graph' > /sys/kernel/debug/tracing/current_tracer
echo 32768 > /sys/kernel/debug/tracing/buffer_size_kb
echo vfs_write > /sys/kernel/debug/tracing/set_ftrace_filter
echo "" >  /sys/kernel/debug/tracing/trace
echo 3 > /proc/sys/vm/drop_caches
echo 1 > /sys/kernel/debug/tracing/tracing_on
dd if=/dev/zero of=/sdcard/test count=1
echo 0 > /sys/kernel/debug/tracing/tracing_on

注意:抓取会导致读写性能明显下降,因此时间的绝对值不能作为参考,只能通过对比相对大小来定位问题;

此处由于我只抓取了vfs层的写入,因此trace文件很小,并且很容易定位到对应位置:

 ------------------------------------------

 4)               |  vfs_write() {
 4)               |    vfs_write() {
 4)               |      /* android_fs_datawrite_start: entry_name /media/0/test, offset 0, bytes 512, cmdline dd, pid 3024, i_size 0, ino 8751 */
 4)               |      /* android_fs_datawrite_end: ino 8751, offset 0, bytes 512 */
 4) ! 124.479 us  |    }
 4) ! 138.073 us  |  }
 4)               |  vfs_write() {
 4)               |  /* sched_wakeup: comm=kworker/4:4 pid=2944 prio=120 success=1 target_cpu=004 */
 4)               |  /* sched_switch: prev_comm=dd prev_pid=3024 prev_prio=120 prev_state=R+ ==> next_comm=kworker/4:4 next_pid=2944 next_prio=120 */
 ------------------------------------------
 4)    dd-3024     =>  kworker-2944 
 ------------------------------------------

后记

最近由于工作需要,在研究IO方面的问题,因此需要深度研究IO的流程,用ftrace抓取函数调用可以做到事半功倍,比一句一句跟代码,或者一句一句打log要高效得多;
更多用法,还请自己多多发掘;

文笔有限,若有纰漏,还请多多指教;

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章