Learn runqlat in 5 minutes

2023-11-11 21:44
文章标签 learn minutes runqlat

本文主要是介绍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;  // 1field: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), // 1TP_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]

这篇关于Learn runqlat in 5 minutes的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/393017

相关文章

Learn ComputeShader 09 Night version lenses

这次将要制作一个类似夜视仪的效果 第一步就是要降低图像的分辨率, 这只需要将id.xy除上一个数字然后再乘上这个数字 可以根据下图理解,很明显通过这个操作在多个像素显示了相同的颜色,并且很多像素颜色被丢失了,自然就会有降低分辨率的效果 效果: 但是这样图像太锐利了,我们加入噪声去解决这个问题 [numthreads(8, 8, 1)]void CSMain(uint3 id

机器学习-有监督学习-分类算法:最大熵模型【迭代过程计算量巨大,实际应用比较难;scikit-learn甚至都没有最大熵模型对应的类库】

最大熵模型(maximum entropy model, MaxEnt)也是很典型的分类算法了。 它和逻辑回归类似,都是属于对数线性分类模型。在损失函数优化的过程中,使用了和支持向量机类似的凸优化技术。而对熵的使用,让我们想起了决策树算法中的ID3和C4.5算法。 理解了最大熵模型,对逻辑回归,支持向量机以及决策树算法都会加深理解。本文就对最大熵模型的原理做一个小结。 一、熵和条件熵 熵

生信机器学习入门3 - Scikit-Learn训练机器学习分类感知器

1. 在线读取iris数据集 import osimport pandas as pd# 下载try:s = 'https://archive.ics.uci.edu/ml/machine-learning-databases/iris/iris.data'print('From URL:', s)df = pd.read_csv(s,header=None,encoding='utf-8'

如何高效的完成Minutes of Meeting

每周三晚上是我们微信群的固定活动时间,交流工作中的SAP技术和非技术问题,分享经验和学习新知识。如果有感兴趣的小伙伴找我加微信群。  本周有小伙伴提出,最近因为每天都在开会和参加workshop,没有时间写Minutes of Meeting(会议纪要),每晚都要加班才能完成,很是困扰,问怎么才能高效的完成Minutes of Meeting。 的确如果工作安排很紧凑,想要尽快的写好Minut

OpenCV2.4.10之samples_cpp_tutorial-code_learn-----ImgTrans(仿射变换)

本系列学习笔记参考自OpenCV2.4.10之opencv\sources\samples\cpp\tutorial_code和http://www.opencv.org.cn/opencvdoc/2.3.2/html/genindex.html 本博文将继续学习opencv-tutorial-code中的ImgTrans,这里讲主要介绍仿射变换。仿射变换是直角坐标系的一种,描述的是一

OpenCV2.4.10之samples_cpp_tutorial-code_learn-----ImgTrans(图片边框与图片卷积)

本系列学习笔记参考自OpenCV2.4.10之 opencv\sources\samples\cpp\tutorial_code和 http://www.opencv.org.cn/opencvdoc/2.3.2/html/genindex.html 本博文将继续介绍如何给一张图片添加边框以及如何对一张图片进行卷积。核心函数为copyMakeBorder与filter2D 1.co

OpenCV2.4.10之samples_cpp_tutorial-code_learn-----ImgTrans(Canny边缘检测)

本系列学习笔记参考自OpenCV2.4.10之 opencv\sources\samples\cpp\tutorial_code和 http://www.opencv.org.cn/opencvdoc/2.3.2/html/genindex.html 本博文接下来将介绍图像变换相关的Demo,如下图所示: CannyDetector_Demo.cpp(Canny边缘检测)

OpenCV2.4.10之samples_cpp_tutorial-code_learn-----ImgProc(图像处理)

本系列学习笔记参考自OpenCV2.4.10之 opencv\sources\samples\cpp\tutorial_code和 http://www.opencv.org.cn/opencvdoc/2.3.2/html/genindex.html       本博文将继续学习 OpenCV2.4.10中tutorial-code下的ImgProc,还有对于涉及到的知

OpenCV2.4.10之samples_cpp_tutorial-code_learn------安装配置与第一个Opencv程序

本系列学习笔记参考自OpenCV2.4.10之 opencv\sources\samples\cpp\tutorial_code和 http://www.opencv.org.cn/opencvdoc/2.3.2/html/genindex.html opencv作为一个开源的二维图形库,提供了一套完整的二维图像处理等相关算法的C/C++实现。自opencv2.0版

分类学习-支持向量机(Scikit-learn)

手写体数字识别 1、手写体数据读取 from sklearn.datasets import load_digitsdigits = load_digits() #获得的手写体数据图片存储在digits变量中print(digits.data.shape) 2、数据分割 from sklearn.cross_validation import train_te