Learn runqlat in 5 minutes

内容预告

learn X in 5 系列第一篇. 本篇主要介绍进程时延统计方式和 rawtracepoint.

runqlat

"高负载场景下应用为何卡顿", "进程 A 为什么得不到调度". 当我们在工作生活中产生这样的疑问, 目标进程的调度时延是一个不错的观测切入点. runqlat 可以帮我们完成这项统计, 以下是父子线程通过 pipe 通信, 通过 runqlat.bt 统计得到双方的时延信息:

我们做了些手脚, 让线程 524785 运行条件更为恶劣, 通过上图可以发现双方调度时延有着显著的区别. 采集数据使用的 runqlat.bt 是使用 bpftrace 重写的 bcc/tools/runqlat, bpftrace 语法参考上篇文章:

$ wget -qO - https://raw.githubusercontent.com/lilstaz/perf-tool-examples/main/bpftrace/runqlat.bt
BEGIN
{
    if (!$1) // 1
    {
        printf("Specify the pid of the task first\n");
        exit();
    }
    @pid = $1;
    printf("Tracing latency of task %d. Hit Ctrl-C to end.\n", @pid);
}

rt:sched_wakeup, // 2
rt:sched_wakeup_new
{
    $wakee = (struct task_struct*)arg0;
    if ($wakee->tgid == @pid) // 3
    {
        @qt[$wakee->pid] = nsecs;
    }
}

rt:sched_switch // 4
{
    $prev = (struct task_struct*)arg1;
    $next = (struct task_struct*)arg2;

    if ($prev->tgid == @pid && $prev->state == TASK_RUNNING) // 5
    {
        @qt[$prev->pid] = nsecs;
    }

    if ($next->tgid == @pid && @qt[$next->pid]) // 6
    {
        @[@pid] = hist((nsecs - @qt[$next->pid]) / 1000);
        delete(@qt[$next->pid]);
    }
}
# 运行效果
$ wget -qO - https://raw.githubusercontent.com/lilstaz/perf-tool-examples/main/bpftrace/runqlat.bt| bpftrace - // 7
Attaching 5 probes...
Tracing latency of task 524783. Hit Ctrl-C to end.

@[524783]: 
[128, 256)             2 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@|

@[524785]: 
[8K, 16K)             79 |@@@@@@@@@@@@@                                       |
[16K, 32K)           307 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@|
[32K, 64K)            28 |@@@@                                                |

该脚本实现统计指定进程中所有线程的调度时延(从加入运行队列进程实际运行), 其中:

  1. 强制用户输入待观测的进程号, 将用户输入的进程号赋值给全局变量 @pid;

  2. 通过 , 分隔挂载点, 可以将该段代码段挂在多个挂载点上. 这里 rtrawtracepoint 的缩写, 会在下一节介绍;

  3. 该挂载点第一个参数是被唤醒进程的 PCB, 该结构体保存了进程的 tgid(对应用户态的进程 ID), pid(对应用户态的线程 ID), comm 进程名等信息. 当满足条件 $wakee->tgid == @pid, 则说明目标进程的某线程进入了唤醒逻辑, 即将被放置在运行队列上, 我们使用线程号作为 @qt 哈希表的键, 将此时系统纳秒数保存下来;

  4. 进程发生切换时会执行以下代码段;

  5. 我们使用 $prev 标志被剥夺运行权的进程, 使用 $next 标志即将获得运行权的进程. 在 linux 中, 运行中以及在队列等待的进程状态都为 TASK_RUNNING. 若 $prev 进程状态为 TASK_RUNNING, 说明它将重新入队. 我们需要更新它的入队时间为当前时间;

  6. $next 线程终于在运行队列熬到头, 即将拥有 CPU 的运行权, 通过计算当前时间和入队时间之间的差值, 我们就可以得到该线程的调度时延. 通过 hist() 函数, bpftrace 可以帮忙把数据统计成直方图;

  7. 如果无法运行请下载最新版本的 bpftrace.

rawtracepoint

tracepoints 是内核内置静态的事件源, 接口稳定, 且包含了大部分子系统, 是优质的信息来源, 例如对于调度子系统有以下可用的 tracepoint:

$ grep sched: /sys/kernel/debug/tracing/available_events
...
sched:sched_migrate_task
sched:sched_switch
...

tracepoint 本质是内核中的打印语句, 它们以固定的格式被打印, 可以通过 /sys/kernel/debug/tracing/events/<子系统>/<名称>/format 文件查看打印格式. 以 sched:sched_switch 为例:

$ cat /sys/kernel/debug/tracing/events/sched/sched_switch/format
name: sched_switch
ID: 313
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;  // 1
        field:pid_t next_pid;   offset:56;      size:4; signed:1;
        field:int next_prio;    offset:60;      size:4; signed:1;

print fmt: "prev_comm=%s prev_pid=%d prev_prio=%d prev_state=%s%s ==> next_comm=%s next_pid=%d next_prio=%d", ...) : 

假设我们对即将获得运行权的进程名感兴趣, 我们可以用以下语句打印标号 1 处定义的信息:

$ bpftrace -e 't:sched:sched_switch {printf("%s\n", args->next_comm)}'
swapper/5
pthread_pipe
pthread_pipe
...

tracepoint 很完美, 它总是在代码关键路径出现. 对于理解内核代码, 抑或调试都非常好用. 但在以下场景:

  1. 对观测脚本的性能有要求;

  2. 希望从原数据结构获取更丰富的信息. 使用 rawtracepoint 或许是更合适的.

那么 rawtracepoint 是什么? 它可以理解为 tracepoint 的另一面, /sys/kernel/debug/tracing/available_events 中可用的事件, rawtracepoint 都可以使用, 但相对 tracepoint 提供特定的值, rawtracepoint 直接提供内核数据结构. 即更接近内核的 '第一手数据'. 再次以 sched:sched_switch 为例, 相关代码(include/trace/events/sched.h)如下:

TRACE_EVENT(sched_switch,
 TP_PROTO(bool preempt, struct task_struct *prev, struct task_struct *next), // 1

 TP_fast_assign(
  memcpy(__entry->next_comm, next->comm, TASK_COMM_LEN);
  __entry->prev_pid = prev->pid;   // 2
  __entry->prev_prio = prev->prio;
  __entry->prev_state = __trace_sched_switch_state(preempt, prev);
  memcpy(__entry->prev_comm, prev->comm, TASK_COMM_LEN);
  __entry->next_pid = next->pid;   // 3
  __entry->next_prio = next->prio;
 ),

 TP_printk("prev_comm=%s prev_pid=%d prev_prio=%d prev_state=%s%s ==> next_comm=%s next_pid=%d next_prio=%d", ...)
);

根据标号 1 的定义, 该 tracepoint 接受调度子系统给它传递的三个参数 preempt, prev, next, 并通过 TP_printk() 打印到环形缓冲区. 在第一节 runqlat 中, 为了过滤特定进程的调度时延, 我们需要获取进程切换时两个进程的 tgid 以及 pid. 但根据标号 2 和 3, tracepoint 只能获取到 pid. 为了直接使用 preempt, prev, next 三个变量, 我们可以使用 rawtracepoint, 在 bpftrace 中简写为 rt. 回顾第一节的代码:

rt:sched_switch
{
    $prev = (struct task_struct*)arg1;
    $next = (struct task_struct*)arg2;
...

这里 $prev $next 其实是获取了该 tracepoint 中 TP_PROTO 定义的第 2 和第 3 个参数. 因为只获取参数, 不需要构建 context 结构体, 使得 rawtracepoint 比 tracepoint 的效率更高.

后记

在 bpftrace git 仓库其实有另一个版本的 runqlat.bt , 但没有提供追踪特定进程的功能. 当年用它查问题写了相当绕且丑的逻辑. 知道有 rawtracepoint 之后, 对调度子系统的追踪观测明显方便了几个数量级.

在 tracepoint 无法满足你的场景, 记得翻代码看看它 TP_PROTO, rawtracepoint 总能给你惊喜.

ref

  1. Frequently asked questions about using raw tracepoint with ebpf/libbpf programs[1]

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

CentOs7 NAT模式连接网络

1.配置动态网络 1.1 检查主机网卡配置 检查主机的网络设置 进入控制面板&#xff0c;找到网络共享中心 查看适配器是否都已经开启 1.2 设置虚拟机的网络配置 打开虚拟机网络配置设置&#xff0c;对网卡VMnet8 进行设置 记住网关 全部选择应用&#xff0c;确定 1.3 设置…

数据结构:树的基本概念(二叉树,定义性质,存储结构)

目录 1.树1.基本概念1.空树2.非空树 2.基本术语1.结点之间的关系描述2.结点、树的属性描述3.有序树、无序树4.森林 3.树的常考性质 2.二叉树1.基本概念2.特殊二叉树1.满二叉树2.完全二叉树3.二叉排序树4.平衡二叉树 3.常考性质4.二叉树的存储结构1.顺序存储2.链式存储 1.树 1.…

PyTorch技术和深度学习——三、深度学习快速入门

文章目录 1.线性回归1&#xff09;介绍2&#xff09;加载自由泳冠军数据集3&#xff09;从0开始实现线性回归模型4&#xff09;使用自动求导训练线性回归模型5&#xff09;使用优化器训练线性回归模型 2.使用torch.nn模块构建线性回归模型1&#xff09;使用torch.nn.Linear训练…

文件改名:避免繁琐操作,利用筛选文件批量重命名技巧优化文件管理

在我们的日常生活和工作中&#xff0c;我们经常需要处理大量的文件&#xff0c;从文档、图片到音频和视频等。在这些情况下&#xff0c;一个高效的文件管理策略至关重要。文件重命名的必要性主要体现在两个方面。首先&#xff0c;对于大量文件&#xff0c;手动进行重命名不仅费…

邻接矩阵储存图实现深度优先遍历(C++)

目录 基本要求&#xff1a; 图的结构体&#xff1a; 图的构造&#xff1a; 图的深度优先&#xff08;DFS&#xff09;&#xff1a; 图的打印输出&#xff1a; 完整代码&#xff1a; 测试数据&#xff1a; 运行结果&#xff1a; 通过给出的图的顶点和边的信息&#xff0c…

Sprint Boot 学习路线 4

微服务 Spring Microservices是一个框架&#xff0c;它使用Spring框架更容易地构建和管理基于微服务的应用程序。微服务是一种架构风格&#xff0c;其中一个大型应用程序被构建为一组小型、独立可部署的服务。每个服务具有明确定义的职责&#xff0c;并通过API与其他服务通信。…

S7-1200PLC和SMART PLC开放式以太网通信(UDP双向通信)

S7-1200PLC的以太网通信UDP通信相关介绍还可以参考下面文章链接: 博途PLC开放式以太网通信TRCV_C指令应用编程(运动传感器UDP通信)-CSDN博客文章浏览阅读2.8k次。博途PLC开放式以太网通信TSENG_C指令应用,请参看下面的文章链接:博途PLC 1200/1500PLC开放式以太网通信TSEND_…

Flink之Table API SQL连接器

连接器 Table API & SQL连接器1.概述2.支持连接器 DataGen连接器1.概述2.SQL客户端执行3.Table API执行 FileSystem连接器1.创建FileSystem映射表2.创建source数据源表3.写入数据4.解决异常5.查询fileTable6.查看HDFS Kafka连接器1.添加kafka连接器依赖2.重启yarn-session、…

vue.cli 中怎样使用自定义的组件

目录 创建自定义组件 注册并使用自定义组件 全局注册自定义组件 使用 Props 传递数据 总结 前言 在Vue CLI中使用自定义组件是构建交互式和模块化Web应用的重要一环。Vue CLI为开发者提供了使用自定义组件的灵活性和简便性。通过Vue CLI&#xff0c;你可以创建、注册和使…

【算法练习Day45】最长公共子序列不相交的线最大子数组和

​&#x1f4dd;个人主页&#xff1a;Sherry的成长之路 &#x1f3e0;学习社区&#xff1a;Sherry的成长之路&#xff08;个人社区&#xff09; &#x1f4d6;专栏链接&#xff1a;练题 &#x1f3af;长路漫漫浩浩&#xff0c;万事皆有期待 文章目录 最长公共子序列不相交的线最…

开发者眼中的向量数据库应用领域

目录 引言向量数据库概念向量数据库优势应用领域亚马逊云科技向量数据库向量数据库的使用步骤最后 引言 随着人工智能和大数据技术的快速发展&#xff0c;越来越多的技术倾向于数据存储方面&#xff0c;数据库领域也随着人工智能和大数据的发展而发展&#xff0c;尤其是向量…

月销破30万辆后,比亚迪整了波大的

最近乘联会公布了 2023 年 10 月新能源乘用车厂商销量榜单。 其中最为亮眼犹如鹤立鸡群的榜首&#xff0c;没错依然是我们熟悉的那个迪子&#xff01; 单月销量超 30 万辆&#xff0c;相较去年同期暴涨 38.4%&#xff0c;创下了比亚迪有史以来新高。 同时也成为了国内首个月销…

PEFT概述:最先进的参数高效微调技术

了解参数高效微调技术&#xff0c;如LoRA&#xff0c;如何利用有限的计算资源对大型语言模型进行高效适应。 PEFT概述&#xff1a;最先进的参数高效微调技术 什么是PEFT什么是LoRA用例使用PEFT训练LLMs入门PEFT配置4位量化封装基础Transformer模型保存模型加载模型推理 结论 什…

Java学习 9.Java-数组 讲解及习题

一、数组的定义与使用 1.数组的基本概念 1.1 为什么要使用数组 数组是最简单的一种数据结构 组织一组相同类型数据的集合 数据结构本身是来描述和组织数据的 数据加结构 1.2.1 数组的创建 代码实现 new int 可省略&#xff1b; char[] chars{a,b,c};//定义一个整形类型数…

在线免费语音克隆工具,1分钟,复制你的声音

hi&#xff0c;同学们&#xff0c;我是赤辰。玩自媒体这么多年&#xff0c;总结出凡是用自己的声音做短视频&#xff0c;既有识别度&#xff0c;也更容易上热门&#xff0c;但是录制音频的艰难&#xff0c;试过的都知道&#xff0c;市面上也有很多克隆工具&#xff0c;但是基本…

【Git】Git分支与标签掌握这些技巧让你成为合格的码农

&#x1f389;&#x1f389;欢迎来到我的CSDN主页&#xff01;&#x1f389;&#x1f389; &#x1f3c5;我是Java方文山&#xff0c;一个在CSDN分享笔记的博主。&#x1f4da;&#x1f4da; &#x1f31f;推荐给大家我的专栏《Git》。&#x1f3af;&#x1f3af; &#x1f449…

Qt——连接mysql增删查改(仓库管理极简版)

目录 UI布局设计 .pro文件 mainwindow.h main.cpp UI布局设计 .pro文件 QT core gui QT core gui sql QT sqlgreaterThan(QT_MAJOR_VERSION, 4): QT widgetsCONFIG c11# The following define makes your compiler emit warnings if you use # any …

【算法-链表4】环形链表2的两种解法

今天&#xff0c;带来链表相关算法的讲解。文中不足错漏之处望请斧正&#xff01; 理论基础点这里 环形链表 1. 思路 利用链表相交 我们在环内任意一处断开&#xff0c;然后判断断开处的下一个位置和head是否相交&#xff0c;如果相交&#xff0c;相交处就是环口。 公式法 …

ArcGIS10.8 连接 PostgreSQL 及遇到的两个问题

前提 以前同事用过我的电脑连PostgreSQL&#xff0c;失败了。当时不知道原因&#xff0c;只能使用GeoServer来发布数据了。现在终于搞明白了&#xff0c;原因是ArcGIS10.2版本太老&#xff0c;无法连接PostgreSQL9.4。参考这里 为了适应时代的发展&#xff0c;那我就用新的Ar…

Spark的转换算子和操作算子

1 Transformation转换算子 1.1 Value类型 1&#xff09;创建包名&#xff1a;com.shangjack.value 1.1.1 map()映射 参数f是一个函数可以写作匿名子类&#xff0c;它可以接收一个参数。当某个RDD执行map方法时&#xff0c;会遍历该RDD中的每一个数据项&#xff0c;并依次应用f函…