前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >使用 Ftrace 分析函数性能

使用 Ftrace 分析函数性能

作者头像
刘盼
发布2022-06-07 10:16:32
1.2K0
发布2022-06-07 10:16:32
举报

作者简介:伟林,中年码农,从事过电信、手机、安全、芯片等行业,目前依旧从事Linux方向开发工作,个人爱好Linux相关知识分享。

0.背景

ftrace的功能非常强大,可以在系统的各个关键点上采集数据用以追踪系统的运行情况。既支持预设的静态插桩点(trace event),也支持每个函数的动态插桩(function tracer)。还可以利用动态插桩来测量函数的执行时间(function graph tracer)。关于ftrace的详细操作和原理分析可以参考Linux ftrace一文。

本文的主要目的主要是利用ftrace来做新增代码的性能分析和优化,应用的主要场景如下:

我们在现有的代码中增加了一批新函数A_*()。

功能完成后,我们希望知道两个问题:

  • Question 1、在运行过程中,新增的函数A_*()造成了多少的性能损失?
  • Question 2、如果需要优化,怎样找出某个耗时比较大的A_xxx()函数具体开销在哪里?

1.trace-cmd工具的安装

我们可以手工操作/sys/kernel/debug/tracing路径下的大量的配置文件接口,来使用ftrace的强大功能。但是这些接口对普通用户来说太多太复杂了,我们可以使用对ftrace功能进行二次封装的一些命令来操作。

trace-cmd就是ftrace封装命令其中的一种。该软件包由两部分组成:

  • 1、trace-cmd。提供了数据抓取和数据分析的功能。
  • 2、kernelshark。可以用图形化的方式来详细分析数据,也可以做数据抓取。

首先我们需要安装trace-cmd工具,ubuntu下可以通过以下两种方式安装:

  • 1、编译源码,安装最新版本:
1、安装依赖:sudo apt-get install build-essential git cmake libjson-c-dev -ysudo apt-get install freeglut3-dev libxmu-dev libxi-dev -ysudo apt-get install qtbase5-dev -y
2、下载源码:git clone https://git.kernel.org/pub/scm/utils/trace-cmd/trace-cmd.git/
3、编译安装:cd trace-cmdmake guisudo make install_gui
  • 2、从软件源安装,版本较旧:
sudo apt install trace-cmd kernelshark

2.粗粒度分析

假设我们新增了一批函数名为vfs_*(),性能分析时我们可以先总体追踪一下这些函数的耗时,以及耗时在总体时间中的占比。即Question 1。

2.1 使用trace-cmd record -l func命令抓取数据

trace-cmd从per_cpu buffer中抓取原始数据/sys/kernel/debug/tracing/per_cpu/cpu0/trace_pipe_raw,所以它的开销小并且支持长时间抓取。

sudo trace-cmd record -p function_graph -l vfs_* -F cp -r ~/perf perf.bak

命令的详细参数含义如下:

  • -p function_graph :指定当前tracer为function_graph,只有function_graph才能测量函数执行时间
  • -l vfs_* :函数过滤,指定function_graph追踪哪些函数。function_graph有两种过滤条件可以配置: -l func。实际对应set_ftrace_filter,这种方式插桩的开销较小,只会追踪顶层func的执行时间且支持*等通配符的设置。

 -g func。实际对应set_graph_function,这种方式插桩的开销较大,但能追踪func以及func所有子函数的的执行时间,不支持*等通配符的设置。

  • -F cp -r ~/perf perf.bak :进程过滤,指定对cp -r ~/perf perf.bak这个进程进行追踪。也可以使用-P pid来指定进程。还可以不指定进程默认对全局进程追踪,例如sleep 10追踪10s。

因为-l func和-g func的特点,所以我们在粗粒度分析时使用-l func,在细粒度分析时使用-g func。这也是本文的一个精髓。

2.2 使用trace-cmd report --profile命令分析数据

所有的原始trace数据已经默认存储到trace.dat文件中了。

1、使用trace-cmd report命令可以把trace.dat解析成文本格式:

$ sudo trace-cmd report| more...
# 注释 #    进程名-pid  CPU    时间戳(s)      函数入口/出口  耗时等级  函数耗时      函数名              cp-3484  [006] 20010.128398: funcgraph_entry:      + 52.946 us  |  vfs_open();              cp-3484  [006] 20010.128466: funcgraph_entry:      ! 212.370 us |  vfs_read();              cp-3484  [006] 20010.128723: funcgraph_entry:        1.832 us   |  vfs_read();              cp-3484  [006] 20010.128725: funcgraph_entry:        0.250 us   |  vfs_read();              cp-3484  [006] 20010.128729: funcgraph_entry:        0.673 us   |  vfs_open();              cp-3484  [006] 20010.128730: funcgraph_entry:        0.688 us   |  vfs_read();              cp-3484  [006] 20010.128731: funcgraph_entry:        0.434 us   |  vfs_read();              cp-3484  [006] 20010.129213: funcgraph_entry:        1.386 us   |  vfs_open();              cp-3484  [006] 20010.129215: funcgraph_entry:                   |  vfs_statx_fd() {

文本主要格式的含义如上中文注释所示,我们读出开始的时间戳和结束的时间戳,就能计算出操作的总体时间。

2、trace-cmd report --profile命令可以对我们追踪的函数执行时间进行统计:

$ sudo trace-cmd report --profile...
task: cp-3484   # 进程名和PID#注释#         函数名       次数   总时长(ns)      平均时长(ns)   最大时长(ns)(时间戳s)         最小时长(ns)(时间戳s)  Event: func: vfs_read() (6017) Total: 565900849 Avg: 94050 Max: 1827017(ts:20010.738236) Min:203(ts:20010.130418)  Event: func: vfs_write() (3520) Total: 319047851 Avg: 90638 Max: 591045(ts:20010.398434) Min:3437(ts:20011.032217)  Event: func: vfs_statx() (3865) Total: 49642741 Avg: 12844 Max: 1411479(ts:20010.626101) Min:924(ts:20010.718592)  Event: func: vfs_mkdir() (690) Total: 9175927 Avg: 13298 Max: 63201(ts:20010.305020) Min:8868(ts:20010.939694)  Event: func: vfs_getattr() (9529) Total: 5968390 Avg: 626 Max: 31850(ts:20010.459588) Min:260(ts:20010.718595)  Event: func: vfs_statx_fd() (5666) Total: 5513205 Avg: 973 Max: 32808(ts:20010.539845) Min:434(ts:20010.508351)  Event: func: vfs_open() (5668) Total: 4346891 Avg: 766 Max: 52724(ts:20010.128451) Min:236(ts:20011.152689)  Event: func: vfs_getattr_nosec() (9529) Total: 2162038 Avg: 226 Max: 18470(ts:20010.182797) Min:91(ts:20010.718877)

--profile对数据进行了统计和排序。它按照进程为单位,对每个进程的监控函数的调用时间进行了统计,有调用次数、总时长、平均时长、最大/小时长,并且默认按照总时长进行了排序。

这样就很方便的找到哪个函数耗时最多,占比有多少。比如上例中耗时最多的是vfs_read()。

计算出所有函数的总体耗时,单位为ns:

// `$6`指定了第6列`总时长`sudo trace-cmd report --profile | grep "Event: func:" | awk '{print $6}' | awk '{sum+=$1}END{print sum}'

也可以按照其他维度对数据进行排序:

// 按照`平均时长`进行排序,`k8`指定了第8列`平均时长`sudo trace-cmd report --profile | grep "Event: func:" | sort -k8 -n -r      

3.细粒度分析

上一节中我们使用粗粒度分析的方法找出了耗时最长的函数为vfs_read(),需要进一步分析vfs_read()的耗时究竟消耗在哪个子函数上。即Question 2。

3.1 使用trace-cmd record -g func命令抓取数据

上一节已经阐述了,抓取函数内部所有子函数的执行时间,需要使用-g func选项。trace数据已经默认存储到trace.dat文件中。

 sudo trace-cmd record -p function_graph -g vfs_read -F cp -r ~/perf perf.bak

3.2 使用trace-cmd report --profile命令分析数据

1、使用trace-cmd report命令可以把trace.dat解析成文本格式:

$ sudo trace-cmd report| more...
              cp-3663  [006] 27162.447945: funcgraph_entry:                   |  vfs_read() {              cp-3663  [006] 27162.447948: funcgraph_entry:                   |    smp_irq_work_interrupt() {              cp-3663  [006] 27162.447948: funcgraph_entry:                   |      irq_enter() {              cp-3663  [006] 27162.447948: funcgraph_entry:        0.122 us   |        rcu_irq_enter();              cp-3663  [006] 27162.447949: funcgraph_exit:         0.396 us   |      }              cp-3663  [006] 27162.447949: funcgraph_entry:                   |      __wake_up() {              cp-3663  [006] 27162.447950: funcgraph_entry:                   |        __wake_up_common_lock() {              cp-3663  [006] 27162.447950: funcgraph_entry:        0.093 us   |          _raw_spin_lock_irqsave();              cp-3663  [006] 27162.447950: funcgraph_entry:        0.100 us   |          __wake_up_common();              cp-3663  [006] 27162.447950: funcgraph_entry:        0.098 us   |          _raw_spin_unlock_irqrestore();              cp-3663  [006] 27162.447950: funcgraph_exit:         0.666 us   |        }              cp-3663  [006] 27162.447950: funcgraph_exit:         0.877 us   |      }

可以看到,抓出了函数的层次调用关系,以及在函数结束时打印出了函数执行时间。

2、trace-cmd report --profile命令对所有子函数进行统计:

$ sudo trace-cmd report --profile | more...
task: cp-3663  Event: func: vfs_read() (6011) Total: 1573004753 Avg: 261687 Max: 49374656(ts:27164.368077) Min:3394(ts:27162.503426)  Event: func: __vfs_read() (6011) Total: 1530911551 Avg: 254685 Max: 49372111(ts:27164.368077) Min:1928(ts:27162.451416)  Event: func: new_sync_read() (6009) Total: 1527333966 Avg: 254174 Max: 49371813(ts:27164.368076) Min:1891(ts:27162.503426)  Event: func: ext4_file_read_iter() (6009) Total: 1523645073 Avg: 253560 Max: 49371449(ts:27164.368076) Min:1703(ts:27162.503426)  Event: func: generic_file_read_iter() (6009) Total: 1519828915 Avg: 252925 Max: 49371191(ts:27164.368076) Min:1547(ts:27162.503426)  Event: func: ondemand_readahead() (3073) Total: 755070718 Avg: 245711 Max: 2425224(ts:27164.891754) Min:282(ts:27162.906376)  Event: func: __do_page_cache_readahead() (3073) Total: 752847053 Avg: 244987 Max: 2424918(ts:27164.891753) Min:91(ts:27162.906375)  Event: func: page_cache_sync_readahead() (2484) Total: 406033723 Avg: 163459 Max: 1685271(ts:27162.480326) Min:477(ts:27162.906376)  Event: func: ext4_readpages() (2966) Total: 365560885 Avg: 123250 Max: 1564052(ts:27162.480212) Min:19321(ts:27162.503496)
...

--profile对数据进行了统计和排序。它按照进程为单位,对每个进程的监控函数vfs_read()及其子函数的调用时间进行了统计,有调用次数、总时长、平均时长、最大/小时长,并且默认按照总时长进行了排序。

需要注意的是,上述的统计并没有呈现出函数的调用关系,所以它们的时长可能是相互包含的。另外因为任务切换的发生,数据中还记录了一些非追踪函数vfs_read()子函数以外的函数。但是以上的统计数据,对于排查重点函数还是非常有帮助的。

3.3 使用kernelshark图形化分析数据

trace-cmd report --profile主要是使用统计的方式来找出热点。如果要看vfs_read()一个具体的调用过程,除了使用上一节的trace-cmd report命令,还可以使用kernelshark图形化的形式来查看。

下图是使用kernelshark打开trace.dat文件,并且逐个分析vfs_read()子函数调用的示意图:

关于kernelshark的详细使用可以参考kernelshark guid。

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2022-06-07,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 人人都是极客 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 0.背景
  • 1.trace-cmd工具的安装
  • 2.粗粒度分析
  • 3.细粒度分析
相关产品与服务
命令行工具
腾讯云命令行工具 TCCLI 是管理腾讯云资源的统一工具。使用腾讯云命令行工具,您可以快速调用腾讯云 API 来管理您的腾讯云资源。此外,您还可以基于腾讯云的命令行工具来做自动化和脚本处理,以更多样的方式进行组合和重用。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档