在Linux下做性能分析2:ftrace

news2024/11/22 22:10:31

==介绍==

在我们进一步介绍更多模型分析技巧前,我们先要对基本工具有一些了解。这一篇先介绍ftrace的基本用法。

ftrace在内核的Documentation目录下已经有文档了,我这里不是要对那个文档进行翻译,而是要说明这个工具的设计理念和使用策略。细节的东西读者要自己去看手册。

ftrace通过一个循环队列跟踪内核的执行过程。这个循环队列在内存中,大小是固定的(可以动态设置),所以写入的速度可以很快,在没有ftrace的时候,我经常通过类似的方式人工跟踪系统的执行过程,以便定位调度引起的各种问题。调度的问题对执行时间非常敏感,所以进行跟踪需要尽量避免把IO,等待等各种额外的要素加进来。而直接写内存就成为影响最小的一种模式了,ftrace很好地满足了这个要求。所以基本上现在我已经不需要再写额外的人工跟踪代码来跟踪系统的执行序列了。

ftrace通过debugfs对外提供接口,所以不需要额外的工具进行支持。ftrace在内核中的配置选项是CONFIG_FTRACE,除了这个基本选项外,下面还有很多子特定可以单独选,用户可以自己去看对应的Kconfig文档,一般的发行版都会开启这个特性,所以大部分情况下你也不需要为了使用这个功能重新编译内核。debugfs在大部分发行版中都mount在/sys/kernel/debug目录下,而ftrace就在这个目录下的tracing目录中。如果系统没有mount这个文件系统,你也可以手工mount。作为虚拟文件系统,它和procfs一样,给定类型就可以mount,比如你可以这样:

mount -t debugfs none /home/kenneth-lee/debug

tracing目录中的内容有点乱:

available_events            current_tracer         function_profile_enabled  options         saved_cmdlines_size  set_ftrace_pid      stack_trace         trace_options        tracing_on
available_filter_functions  dyn_ftrace_total_info  instances                 per_cpu         set_event            set_graph_function  stack_trace_filter  trace_pipe           tracing_thresh
available_tracers           enabled_functions      kprobe_events             printk_formats  set_event_pid        set_graph_notrace   trace               trace_stat           uprobe_events
buffer_size_kb              events                 kprobe_profile            README          set_ftrace_filter    snapshot            trace_clock         tracing_cpumask      uprobe_profile
buffer_total_size_kb        free_buffer            max_graph_depth           saved_cmdlines  set_ftrace_notrace   stack_max_size      trace_marker        tracing_max_latency

里面虽然有一个README文件在解释,但这个文档更新得不快,很多时候和实际的内容对不上。但不要紧,只要我们抓住重点逻辑,就很容易理解了。ftrace的目录设置和sysfs类似,都是把目录当作对象,把里面的文件当作这个对象的属性。所以,虽然这个目录中的文件众多,我们只要先理解以下几个概念就很容易抓住重点了:

1. instance

一个用来跟踪的缓冲区(内存)称为一个instance,缓冲区的大小由文件buffer_size_kb和buffer_total_size_kb文件指定。有了缓冲区,你就可以启动行为跟踪,跟踪的结果会分CPU写到缓冲区中。缓冲区的数据可以通过trace和trace_pipe两个接口读出。前者通常用于事后读,后者是个pipe,可以让你动态读。为了不影响执行过程,我更推荐前一个接口。

trace等文件的输出是综合所有CPU的,如果你关心单个CPU可以进入per_cpu目录,里面有这些文件的分CPU版本。

我们不要看到trace文件中输出那么多文本,就觉得这个跟踪效率不高。那些文本都是事后(就是你读这个文件的时候)format出来的,跟踪的时候仅仅记录了必须的数据信息,所以不需要担心跟踪时的效率问题。

所以读者应该已经明白了/sys/kernel/debug/tracing这个目录本身就代表一个instance。如果你需要更多的instance,你可以进入到这个目录下面的instances目录中,创建一个任意名字的目录,那个目录中就也会有另一套buffer_size_kb啦,trace啦这些文件,那里就是另一个instance了。通过多instance,你可以隔离多个独立的跟踪任务。当然,这也很浪费内存。

和所有面向对象设计一样,instance通过操作对应属性来控制其行为,比如,向trace文件写一个空字符串可以清空对应的缓冲区:

echo > trace

又比如,向tracing_on文件写1启动跟踪,写0停止跟踪等。向set_ftrace_pid写pid可以限制只根据某个pid的事件等。

更复杂的控制在trace_options中,这个文件也是很好理解的,类似vim,你看看它的内容,要启动某个功能就echo某个字符串进去,要关闭它只要echo带“no”前缀的那个字符串进去就可以了。options目录可以提供更精细的控制。

2. 事件(event)

ftrace有两种主要跟踪机制可以往缓冲区中写数据,一种是函数,一种是事件。前者比较酷,很多教程都会先讲前者。但对我来说,后者才比较可靠实用,所以我先讲后者。

事件是固定插入到内核中的跟踪点,我们看Linux代码的时候,经常看到这种trace_开头的函数调用:

        if (likely(prev != next)) {
                rq->nr_switches++;
                rq->curr = next;
                ++*switch_count;

                trace_sched_switch(preempt, prev, next);
                rq = context_switch(rq, prev, next, cookie); /* unlocks the rq */
        } else {
                lockdep_unpin_lock(&rq->lock, cookie);
                raw_spin_unlock_irq(&rq->lock);
        }

这个地方就是一个事件,也就是打在程序中的一个桩,如果你使能这个桩,程序执行到这个地方就会把这个点(就是一个整数,而不是函数名),加上后面的三个参数(preempt, prev, next)都写到缓冲区中。到后面你要输出的时候,它会用一个匹配的解释函数来把内容解释出来,然后你在trace文件中看到的就是这样的:

启动事件跟踪的方法很简单:

1. 先查available_events中有哪些可以用的事件(查events目录也可以)。

2. 把那个事件的名称写进set_event,可以写多个,可以写sched:*这样的通配符

3. 通过trace_on文件启动跟踪。启动之前可以通过比如tracing_cpumask这样的文件限制跟踪的CPU,通过set_event_pid设置跟踪的pid,或者通过其他属性进行更深入的设定。

剩下的事情就是执行跟踪程序和分析跟踪结果了。

我通常把ftrace的设置和启动等命令和业务程序的启动写到一个脚本中,一次运行足够的时间然后直接取结果。然后就专心手工或者通过python脚本分析输出结果(ftrace的输出用awk很不好拆,还是python比较实际)

事件跟踪的另一个更强大的功能是可以设定跟踪条件,要做这种精细化的设置,你需要直接操作events目录下面的事件参数,比如仍是跟踪前面这个sched_switch,你可以先看看events/sched/sched_switch/format文件:

name: sched_switch
ID: 273
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 prev_comm[16];       offset:8;       size:16;        signed:1;
        field:pid_t prev_pid;   offset:24;      size:4; signed:1;
        field:int prev_prio;    offset:28;      size:4; signed:1;
        field:long prev_state;  offset:32;      size:8; signed:1;
        field:char next_comm[16];       offset:40;      size:16;        signed:1;
        field:pid_t next_pid;   offset:56;      size:4; signed:1;
        field:int next_prio;    offset:60;      size:4; signed:1;

你就可以看到这个事件可以支持的域,你只要向同级目录中的filter中写一条类C的表达式就可以对这个事件进行过滤。

比如,我注意到这个跟踪点支持next_comm这个域,我就可以这样写:

echo 'next_comm ~ "cs"' > events/sched/sched_switch/filter

这样我就可以仅跟踪调度器切换到cs这个线程的场景了。

对于性能分析,我用得最多的是这个线程switch事件(还有softirq的一组事件)。因为从考量通量的角度,主业务CPU要不idle,它要不在处理业务,要不在调度。一个“不折腾”的系统,主业务进程应该每次都用完自己的时间片,如果它总用不完,要不是它实时性要求很高(主业务这种情况很少),要不是线程调度设计有问题。我们常常看到的一种模型是,由于业务在线程上安排不合理,导致一个线程刚执行一步,马上要等下一个线程完成,那个线程又执行一步,又要回来等前一个线程完成,这样CPU的时间都在切换上,整个通量就很低了。

这种模型后面专门论述,但通过ftrace看调度过程,我们很容易用python分析这个调度过程,捕获所有“提前切换”的情况,并对高几率的提前切换进行分析,就可以针对性地解决问题了。

事件上还可以安装trigger,用来触发特定的动作,我很少用这个功能,读者可以自己看手册看看有没有什么实际的用途。 其中手册中提到一个特别酷的功能叫“hist”(输出柱状图),它可以通过这样的命令:

echo 'hist:key=call_site:val=bytes_req' > /sys/kernel/debug/tracing/events/kmem/kmalloc/trigger

实现下面这样的效果:

这相当于给你提供一个各个内存分配点的内存分配次数和数量的一个分布图。这很爽,不过老实说,有了trace文件,要产生这样的数据也是分分钟的事,所以我也不是很需要这个功能。

3. 预定义功能

事件跟踪需要根据我们对Kernel业务流有清晰的认识,我们才能合理设置事件。功能跟踪就会简单得多,功能跟踪可以直接使能某种跟踪功能,具体用什么事件,设置什么参数等,都默认设置好,这种预定义功能在available_tracers中列出,只要选择其中一个,把对应的名字写入current_tracer文件中就可以启动这个功能

我的机器上支持如下功能:

blk mmiotrace function_graph wakeup_dl wakeup_rt wakeup function nop

比如我要跟踪系统唤醒的时延,我们可以:

echo wakeup > current_tracer
echo 1 > tracing_on

wakeup跟踪的输出是这个样子的:

它可以跟踪在你跟踪的期间里,最高优先级的任务的调度最大时延。比如上面这个统计统计到的最大时延是52us(算不错了),下面给出的跟踪点是这个任务(irq/49-iwlwifi-1625)被唤醒后,执行了哪些动作,才轮到它执行了。通过分析这些动作,你就可以知道你可以优化哪些流程来提升整个系统的实时性了。

我很少使用这种成套跟踪,读者可以自己一个个试用一下,看看是否有趁手的工具,可以帮助你解决你的环境中面的的问题。

4. 函数跟踪

成套跟踪中有两个功能其实是相对独立的,就是function和function_graph。这个功能可以和事件一样单独使用(可惜的是不能同时使用,其实照理说这没有什么难度的)。

函数跟踪和事件跟踪一样,相当于在函数入口那里增加了一个trace_函数, 函数跟踪的效果类似这样:

加堆栈跟踪的话,可以变成这样:

函数跟踪也可以做类似事件工作一样的过滤功能,这个用户可以看手册,我用这个功能一般是用来跟踪和性能无关的执行过程,

== 用户态跟踪==

ftrace一个比较明显的缺点是没有用户态的跟踪点支持,作为补救,instance中提供了一个文件,trace_marker,写这个文件可以在跟踪中产生一条记录。类似这样:

你可以注意到,这其中 tracing_mark_write就是一个marker,我在我的程序做pthread_yield()的时候加了一个marker,这样我就可以跟踪当我yield出去的时候,系统是否发生了重新调度。

这个跟踪方法的缺点是需要额外的系统调用,没有内核跟踪那么高效,但聊胜于无,至少这个方法帮助我解决过不少问题。

==会话跟踪==

使用ftrace的另一个缺点是它会话跟踪能力比较差,比如你在网卡上收到一个包,这个包调度到了Socket的队列,然后再送到用户队列。虽然你可以在这些位置人工增加跟踪点,但你不知道这个包属于哪个会话,你也不知道这个包在会话上的时延是什么。这个问题没有非常好的解决方案,关键在于你的跟踪点必须能从这个包上提取出会话有关的信息,比如你能取出这个包的类型和端口号,你就可以从跟踪结果上匹配出整个会话的流程来。

现在网络层的预置事件比较少,我们可以首先考虑是否可以通过函数来跟踪,如果不行,就自己加跟踪点吧。

==其他功能==

ftrace还可以通过uprobe/kprobe设置跟踪点,我对这两个东西不是很信任,所以用得很少,原理也很容易猜出来,所以,读者有兴趣可以自己去看,这里就不深入介绍了。

==总结==

本文介绍了ftrace的基本功能和用法。ftrace主要用于跟踪时延和行为,它让我们可以很深入地了解系统的运行行为。是进行Linux性能调优必须掌握的基本工具

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/754846.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

H3C-Cloud Lab实验-链路聚合实验

实验拓扑图: 实验需求: 1、按照图示配置PC3和PC4的IP地址 2、在SW1和SW2的两条直连链路上配置链路聚合,实现链路冗余,并可以增加传输带宽 3、SW1和SW2之间的直连链路要配置为Trunk类型,允许所有vlan通过 4、中断SW…

基于JavaSwing+Mysql的仓库销售管理系统

点击以下链接获取源码: https://download.csdn.net/download/qq_64505944/88049275 JDK1.8 MySQL5.7 功能:管理员与员工两个角色登录,基础数据查找,仓库查找,增删改查仓库信息、商品等 源码数据库文件配置文件课程设…

java单元测试(调试)

文章目录 测试分类JUnit单元测试介绍引入本地JUnit.jar编写和运行Test单元测试方法设置执行JUnit用例时支持控制台输入10.6.6 定义test测试方法模板 测试分类 **黑盒测试:**不需要写代码,给输入值,看程序是否能够输出期望的值。 **白盒测试…

LangChain + ChatGLM2-6B 搭建个人专属知识库

之前教过大家利用 langchain ChatGLM-6B 实现个人专属知识库,非常简单易上手。最近,智谱 AI 研发团队又推出了 ChatGLM 系列的新模型 ChatGLM2-6B,是开源中英双语对话模型 ChatGLM-6B 的第二代版本,性能更强悍。 树先生之所以现…

GitUI汉化

1.下载汉化文件 下载地址 备用下载地址 https://files.cnblogs.com/files/chenghu/git-gui-zh-master.zip https://files.cnblogs.com/files/chenghu/git-gui-zh-master.zip 2.找到git安装路径 C:\Program Files\Git\mingw64\share\git-gui\lib 3.解压出1下载的文件 复制粘…

配置无线网卡AP模式为wifi热点

1、判断网卡是否支持AP 不管是自带无线网卡、还是外接的usb无线网卡,要先配置为AP热点模式。需要看检查是否支持AP模式。 例如,这里插入 rtl8811c 的双频usb无线网卡,iwconfig查看网卡信息 nvidianvidia-desktop:~$ iwconfig wlan0 un…

数据结构——各种常见算法的实现方法和思路

文章目录 常见的排序算法类型复杂度和稳定性 1.冒泡排序2.直接插入排序3.希尔排序4.简单选择排序方法1:双向遍历选择排序方法2:单向遍历选择排序 5.归并排序方法1:递归方法2:非递归 6.快速排序方法1:随机取keyi方法2&a…

GD32F303 DAM串口接收

1.设置串口 串口配置比较常规,我只应用的空闲中断。 2.DMA设置 我设置的DMA是串口接收到数据后保存到数组里,数组满了以后会自动从头开始,并且会进入一次DMA中断。

Jenkins+Robot 接口自动化测试

目录 前言: 设计目标 项目说明 目录结构 配置 jenkins 1.安装插件 2.配置项目 前言: JenkinsRobot是一种常见的接口自动化测试方案,可以实现自动化的接口测试和持续集成。Jenkins是一个流行的持续集成工具,而Robot Framew…

代码随想录day3 | 203.移除链表元素 707.设计链表 206.反转链表

文章目录 一、移除链表元素的思想两种方法 二、203.移除链表元素三、707.设计链表四、206.反转链表 一、移除链表元素的思想 直接让前一个节点指向后一个节点即可 两种方法 第一种:直接删除 第二种:头删的时候,直接headhead->next 其实…

JAVA数据结构、集合操作及常用API_C++开发转JAVA

文章目录 零、引言一、JAVA数据结构基础1.0 数据类型概述1.1 基本数据类型 零、引言一、JAVA数据结构基础1.0 数据类型概述1.1 基本数据类型1.2 包装类1.3 基本类型和包装类型的区别1.4 包装类型的缓存机制1.5 equals() 和 1.6 自动装箱拆箱1.7 浮点数精度丢失1.8 数值、字符转…

windows下配置pytorch + yolov8+vscode,并自定义数据进行训练、摄像头实时预测

最近由于工程需要,研究学习了一下windows下如何配置pytorch和yolov8,并自己搜集数据进行训练和预测,预测使用usb摄像头进行实时预测。在此记录一下全过程 一、软件安装和配置 1. vscode安装 windows平台开发python,我采用vscod…

Python基础合集 练习26 (turtle库的使用)

turtle是标准库 import turtle as t 窗口最小单位为像素 t.steup(width,height,起始点,起始点) 不是必须的 t.setup(800, 400) 不设置后面的起始点默认在中间 空间坐标体系 绝对坐标 四个象限 t.goto(x,y) 让某个位置的海龟到达某个地方 t.goto(100,100) t.goto(10…

使用flask开启一个简单的应用

Flask是非常流行的 Python Web框架,它能如此流行,原因主要有如下几点: 。有非常齐全的官方文档,上手非常方便。 。有非常好的扩展机制和第三方扩展环境,.工作中常见的软件都会有对应的扩展。自己动手实现扩展也很容易。 。社区活跃度非常高。…

【可解释学习】PyG可解释学习模块torch_geometric.explain

PyG可解释学习模块torch_geometric.explain PhiloshopyExplainerExplanationsExplainer AlgorithmExplanation Metrics参考资料 torch_geometric.explain是PyTorch Geometric库中的一个模块,用于解释和可视化图神经网络(GNN)模型的预测结果。…

RestClient操作文档和DSL查询语法

一、 文档操作 1、新增文档 本案例中,hotel为索引库名,61083为文档idTestvoid testAddDocument() throws IOException {// 1.根据id查询酒店数据Hotel hotel hotelService.getById(61083L);// 2.转换为文档类型HotelDoc hotelDoc new HotelDoc(hotel…

【数据结构】二叉树——链式结构

目录 一、前置声明 二、二叉树的遍历 2.1 前序、中序以及后序遍历 2.2 层序遍历 三、节点个数以及高度 3.1 节点个数 3.2 叶子节点个数 3.3 第k层节点个数 3.4 二叉树的高度/深度 3.5 查找值为x的节点 四、二叉树的创建和销毁 4.1 构建二叉树 4.2 二叉树销毁 4.3 …

2023年7月14日,ArrayList底层

集合框架图: 集合和数组的区别 AarrayList ArrayList底层实现原理 ArrayList的底层实现是基于数组的动态扩容。 初始容量:当创建一个新的ArrayList对象时,它会分配一个初始容量为10的数组。这个初始容量可以根据需求进行调整。 //表示默认的…

在Python中优雅地用多进程:进程池 Pool、管道通信 Pipe、队列通信 Queue、共享内存 Manager Value

Python 自带的多进程库 multiprocessing 可实现多进程。我想用这些短例子示范如何优雅地用多线程。中文网络上,有些人只是翻译了旧版的 Python 官网的多进程文档。而我这篇文章会额外讲一讲下方加粗部分的内容。 创建进程 Process,fork 直接继承资源&am…

zigbee DL-20无线串口收发模块使用(双车通讯,电赛模块推荐)

前言 (1)通常有时候,我们可能会需要让两个MCU进行通讯。而zigbee是最适合两个MCU短距离通讯的模块。他使用极其简单,非常适合两款MCU之间的进行数据交互。 (2)在各类比赛中,经常出现需要两个MCU…