Learn runqlat in 5 minutes

news2025/1/15 13:01:41

内容预告

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]

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

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

相关文章

SQL必知会(二)-SQL查询篇(5)-用通配符进行过滤

第6课、用通配符进行过滤 LIKE&#xff1a;匹配文本 LIKE&#xff1a;针对未知值进行过滤。通配符搜索只能用于文本字段。 1&#xff09;百分号%通配符 %表示任何字符出现任意次数。 需求&#xff1a;找出所有以词 Fish 起头的产品 SELECT prod_id, prod_name FROM Product…

Linux-基础知识

1.快捷键 ctrlc 强制停止 ctrld 退出或登出 history 查看历史命令&#xff08;&#xff01;/ctrlr输入内容去匹配历史命令&#xff09; 光标移动快捷键 ctrla,跳到命令开头 ctrle,跳到命令结尾 ctrl键盘左键&#xff0c;向左跳一个单词 ctrl键盘右键&…

Python 使用tkinter的Menu菜单command参数与bind方法共用触发事件

用普通函数作为媒介&#xff0c;使用event_generate()方法模拟触发bind()事件来创建一个模拟的event对象&#xff0c;并将其传递给绑定的事件处理函数。 运行结果 示例代码 import tkinter as tk# 菜单事件 def menuEvent(event):print(event.x, event.y)label.config(textf鼠…

【Linux】Centos7 shell实现MySQL5.7 tar 一键安装

&#x1f984; 个人主页——&#x1f390;个人主页 &#x1f390;✨&#x1f341; &#x1fa81;&#x1f341;&#x1fa81;&#x1f341;&#x1fa81;&#x1f341;&#x1fa81;&#x1f341; 感谢点赞和关注 &#xff0c;每天进步一点点&#xff01;加油&#xff01;&…

SpringBoot集成easyexcel实现动态模板导出

添加依赖 <dependency><groupId>com.alibaba</groupId><artifactId>easyexcel</artifactId><version>3.3.2</version></dependency><dependency><groupId>org.apache.poi</groupId><artifactId>poi-o…

补坑:Java的字符串String类(3):再谈String

不太熟悉字符串的可以看看这两篇文章 补坑&#xff1a;Java的字符串String类&#xff08;1&#xff09;-CSDN博客 补坑&#xff1a;Java的字符串String类&#xff08;2&#xff09;&#xff1a;一些OJ题目-CSDN博客 字符串创建对象 public static void main(String[] args) …

【pytorch深度学习】使用张量表征真实数据

使用张量表征真实数据 本文为书pytorch深度学习实战的一些学习笔记和扩展知识&#xff0c;涉及到的csv文件等在这里不会给出&#xff0c;但是我会尽量脱离这一些文件将书本想要表达的内容给展示出来。 文章目录 使用张量表征真实数据1. 加载图像文件2. 改变布局3. 加载目录下…

Nacos入门到运行-超详细~windwos

&#x1f4da;目录 ⚙️简介:⚡️Nacos下载⌛解压到文件⚙️配置信息☘️修改 application.properties ⛵运行程序☘️安全问题☄️程序出现问题查看方式 ⛳Nacos开启鉴权⚡️跳过Token获取数据⚓接口请求&#xff1a; ✍️结束&#xff1a; ⚙️简介: Nacos:正如官网说的,一个…

【JAVA学习笔记】 68 - 网络——TCP编程、UDP编程

项目代码 https://github.com/yinhai1114/Java_Learning_Code/tree/main/IDEA_Chapter21/src 网络 一、网络相关概念 1.网络通讯 1.概念:两台设备之间通过网络实现数据传输 2.网络通信:将数据通过网络从一台设备传输到另一台设备 3. java.net包下提供了一系列的类或接口&a…

Ansible命令使用

ansible ansible的命令 ansible命令模块Pingcommand 模块shell 模块copy 模块file 模块fetch 模块cron 模块yum 模块service 模块user 模块group 模块script 模块setup 模块get_url模块stat模块unarchive模块unarchive模块 ansible的命令 /usr/bin/ansible  Ansibe AD-Hoc 临…

Xilinx DDR3 MIG系列——Xiinx DDR3官方手册ds176_7series_MIS

本节目录 一、官方手册ds176_7series_MIS 1、DDR3功能支持 2、MIG官方手册资源 3、Vivado DDR3 MIG IP资源表的导出与查看本节内容 Xilinx官方提供了手册&#xff0c;以便硬件开发者设计DDR3的硬件电路&#xff0c;和FPGA开发者使用MIG官方ip核完成项目的逻辑开发。 针对Xilin…

类和对象(2):构造函数,析构函数

一、构造函数 1.1 概念 构造函数是一种特殊的成员函数&#xff0c;名字与类名相同&#xff0c;创建类类型对象时编译器自动调用——初始化对象&#xff0c;在对象整个生命周期内只调用一次。 PS: 1. 构造函数无返回值&#xff1b;2. 构造函数支持重载。 class Date { public:…

【沐风老师】3DMAX克隆修改器插件教程

3DMAX克隆修改器插件&#xff0c;它通过增量平移、旋转和缩放输入几何体来创建对象的副本。在某些方面&#xff0c;它类似于 3ds Max 的内置阵列工具&#xff0c;但有一个主要优点 -克隆是完全参数化的&#xff0c;因此您可以随时更改重复项的数量及其分布。其他功能包括随机变…

Yum配置、相关命令和常见问题

搭建光盘源 将系统盘读取出来&#xff0c;找到系统盘下存放软件包的目录 2.配置yun仓库 输入命令进入仓库编辑 #必须以.repo结尾 :wq 回车保存退出 3.命令行输入yum repolist 查看yum仓库 配置硬盘源 1.将硬盘源拷贝到目录&#xff0c;或者挂载到目录 2.指定repo文件baseu…

Vue3-组合式API生命周期函数

一进入页面的请求一律放在setup中执行 如果有些代码需要在mounted生命周期中执行&#xff0c;并且写成函数的调用方式可以调用多次&#xff0c;并不会冲突&#xff0c;而是按照顺序依次执行 <script setup>onMounted(()>{console.log("mounted生命周期函数-逻辑…

SQL必知会(二)-SQL查询篇(7)-使用函数处理数据

第8课、使用函数处理数据 表8-1 DBMS 函数的差异 函数语法提取字符串的组成DB2、Oracle、PostgreSQL 和 SQLite 使用 SUBSTR()&#xff1b;MariaDB、Mysql 和 SQL Server 使用 SUBSTRING()数据类型转换Oracle 使用多个函数&#xff0c;每种类型的转换有一个函数&#xff1b;D…

指针传 1

1. 内存 在计算机中内存划分为⼀个个的内存单元&#xff0c;每个内存单元的⼤⼩取1个字节。每个内存单元放了八个bite位&#xff0c;就像我们在高中时住的八人间&#xff0c;那么每个人就代表了一个bite位。 每个内存单元也都有⼀个编号&#xff08;这个编号就相当 于我们所住…

聊天机器人框架Rasa资源整理

Rasa是一个主流的构建对话机器人的开源框架&#xff0c;它的优点是几乎覆盖了对话系统的所有功能&#xff0c;并且每个模块都有很好的可扩展性。参考文献收集了一些Rasa相关的开源项目和优质文章。 一.Rasa介绍 1.Rasa本地安装 直接Rasa本地安装一个不好的地方就是容易把本地…

Django框架FAQ

文章目录 问题1:Django数据库恢复问题2:null和blank的区别问题3:Django创建超级用户报错问题4:Django同源策略 问题1:Django数据库恢复 问题: 从仓库拉下来的Django项目,没有sqlite数据库和migrations记录,如何通过model恢复数据库 解决方法: # 步骤1:导出数据 # 不指定 ap…

如何配置《动手学强化学习》的环境

如何配置《动手学强化学习》的环境 网站&#xff1a;https://hrl.boyuai.com/chapter/intro github仓库&#xff1a;https://github.com/boyu-ai/Hands-on-RL/tree/main 可以看到该教程要求使用gym0.18.3版本的gym库&#xff0c;本教程可以用于解决绝大多数需要使用Pendulum-…