选题:proj208-performance-and-diagnosis-tool
项目名称:高负载下性能分析与异常诊断工具
随着现代操作系统的发展,在当前众多的计算机系统中,由于应用负载和系统复杂度的不断提高,系统在高负载情况下可能会出现性能问题和异常情况,而传统的用户态常用的性能工具在高负载情况下常常会失效,表现不佳,无法提供准确的诊断结果,给用户带来不便和风险,这时一个具有高性能、高可靠性 ,同时对系统影响低、准确性强的性能分析和异常诊断工具就显得尤为重要。因此,本项目旨在开发一种高负载情况下的高性能工具,用于分析各类异常情况并对系统进行故障诊断,对于提高系统的性能和稳定性也具有重要意义。
本项目工具实现后应该具有以下特征:
- 高性能:该工具需要在高负载情况下保持较高的性能,能够实时监控系统的各项指标,并能够对各种异常情况进行快速分析和诊断。
- 高可靠性:该工具需要保证在各种复杂的环境下都能够正常工作,而且能够准确地诊断和分析各种异常情况,提供准确的故障诊断报告。
- 对系统影响低:该工具需要在后台运行并对系统的性能影响尽可能小,不会影响到系统的正常运行。
- 准确性强:该工具需要提供准确的诊断结果,不会给用户带来误导和风险,并且支持多种异常情况的诊断。
- 易用性好:该工具需要提供友好的用户界面,简单易用。用户能够通过界面直观地了解系统的运行情况和异常情况,并能够快速地找到解决问题的方法。
本项目的预期目标如下:
- 编写性能工具,采集有关数据,输出火焰图。
- 能够在高负载下进行压力测试,并可靠地完成性能监测和异常分析。
- 在高负载场景下,对工具进程测试,寻找可优化的部分。
在高负载下进行性能分析和故障诊断是一项非常复杂和挑战性的任务,需要掌握足够的技术和工具,才能够准确地找到程序中的性能瓶颈,提高系统的运行效率和稳定性。
以下是一些常见的性能分析、异常诊断技术和工具介绍:
- 内核模块:内核模块(Kernel Module)是一种可以动态加载和卸载到内核中的可执行代码,它可以扩展和增强操作系统的功能。内核模块通常编写为C程序,可以访问操作系统内核的数据结构和函数,并在系统启动时加载,以便为系统提供额外的功能和服务。在性能分析和故障诊断方面,内核模块可以通过跟踪系统调用、监测内核函数调用和分析内核数据结构等方面来帮助找到系统的性能瓶颈和故障点。通过内核模块,可以动态地监测内核状态,了解系统内部的运行机制,快速地定位和解决问题。
- eBPF:eBPF是一个能够在内核运行沙箱程序的技术,提供了一种在内核事件和用户程序事件发生时安全注入代码的机制,使得非内核开发人员也可以对内核进行控制。随着内核的发展,eBPF 逐步从最初的数据包过滤扩展到了网络、内核、安全、跟踪等,而且它的功能特性还在快速发展中,早期的 BPF 被称为经典 BPF,简称cBPF,正是这种功能扩展,使得现在的BPF被称为扩展BPF,简称eBPF。eBPF技术可以在内核空间中执行用户自定义的代码,并以非常低的开销捕获和处理网络数据包、系统调用、内核函数调用等等。eBPF技术可以通过监测各种系统事件和数据,如CPU使用率、内存分配、文件系统访问、网络数据包等,提供实时的、精准的、低开销的监测和分析。
- DTrace:DTrace是一种功能强大的跨平台性能分析和故障诊断工具,DTrace的主要特点是它可以在运行时对应用程序和操作系统进行深入的跟踪和分析,而且不需要重新编译或重启系统。它可以跟踪各种系统活动,例如系统调用、内核事件、用户态应用程序的函数调用、文件系统操作等等,它还可以通过用户自定义的脚本来收集和分析数据,并提供非常丰富的数据可视化和分析工具。
- Linux性能工具箱:Linux性能工具箱是一组命令行工具,用于在Linux系统中进行性能分析和调试,可以帮助开发人员和系统管理员识别性能瓶颈,优化系统和应用程序的性能,以及诊断和解决各种问题。以下是一些常见的Linux性能工具箱组件:
- top:top命令是一个实时性能监控工具,可以显示系统的资源使用情况,包括CPU、内存、I/O等。
- vmstat:vmstat命令可以显示Linux系统的虚拟内存统计信息,通过vmstat命令可以查看系统的负载情况,以及系统内部的运行状况。
- strace:strace命令可以跟踪进程的系统调用,通过strace命令可以查看应用程序和系统之间的交互,以及应用程序的性能瓶颈。
- perf:perf命令是一个性能分析工具,可以用于跟踪进程的CPU使用情况、内存使用情况、锁竞争等性能指标。
本项目旨在开发一种在高负载场景下的性能分析与异常诊断工具,而在这种场景下用户态常用的性能工具常常会失效,因此我们把研究重点放在了内核模块和eBPF上面,经过综合考虑,以及和导师们的探讨,最后选择了使用内核模块的方式去进行工具的开发,在下文中我们会说其原因。
目前,我们已经基本实现了开源工具diagnose-tools在高版本(Linux 5.19内核)中的复现,成功复现的功能模块介绍如下所示:
模块名称 | 功能描述 |
---|---|
sys-cost | 统计系统调用的次数及时间 |
irq-delay | 监控中断被延迟的时间 |
irq-stats | 统计中断/软中断执行次数及时间 |
load-monitor | 监控系统Load值。每10ms查看一下系统当前Load,超过特定值时,输出任务堆栈。这个功能多次在线上抓到重大BUG。可以分别监控Load/Load.R/Load.D/Task.D等指标。 |
run-trace | 监控进程在某一段时间段内,在用户态/内核态运行情况 |
perf | 对线程/进程进行性能采样,抓取用户态/内核态调用链 |
kprobe | 在内核任意函数中,利用kprobe监控其执行,并输出火焰图 |
exit-monitor | 监控任务退出。在退出时,打印任务的用户态堆栈信息 |
mutex-monitor | 监控长时间持有mutex的流程 |
exec-monitor | 监控进程调用exec系统调用创建新进程 |
alloc-top | 统计内存分配数量,按序输出内存分配多的进程 |
high-order | 监控分配大内存的调用链 |
reboot | 监控系统重启信息,打印出调用sys_reboot系统调用的进程名称以及进程链 |
开源工具diagnose-tools在高版本(Linux 5.19内核)中功能的演示:
执行指令uname -r
,查看内核的版本:
在使用模块功能之前,需要使用sudo diagnose-tools install
命令安装KO模块:
可以看到已经成功安装,这里以 load-monitor 功能为例,进行使用说明。
执行指令 diagnose-tools load-monitor --activate="load=6"
,设置负载阈值为6,运行结果:
可以看到该功能已经成功被激活,使用stress-ng进行加压到6后,执行指令 diagnose-tools load-monitor --report
查看结果:
以上仅展示了局部的运行结果,可以看到超负载阈值后可以打印的相关信息,可能信息的量过于庞大,我们可以依次执行 diagnose-tools load-monitor --report > load-monitor.log
和 diagnose-tools flame --input=load-monitor.log --output=load-monitor.svg
命令将捕获的信息生成火焰图:
最后执行指令diagnose-tools load-monitor --deactivate
即可退出该功能模块。
其他功能也采用类似的方法进行使用,这里就不过多演示,后期我们会出一份详细的使用说明书,让大家可以更好的学习。
负载值的监控模块实现的功能:使用者可以在指令中设置要监控的时长,然后在这个时间段内每一个时间戳记录下一分钟平均负载值,目的是为了后期在解决高负载问题时,可以让使用者观察到负载值的变化趋势,这样有助于使用者结合自己解决问题的操作采用更加有效的方法去发现问题,并进行有效解决。
目前,我们该功能模块仅实现了雏形,运行结果仅作为展示,没有实际场景意义:
目前该功能模块以及其他功能模块还在开发和完善中,大家敬请期待!!!
-
问题描述:在编写libbpf程序获取avenrun[0] (即一分钟平均负载值) 的过程中,由于avenrun数组是全局变量,也就意味着无法通过挂载函数来实现读取这一变量中的值。
解决办法:通过在/proc/kallsyms文件中读取avenrun变量的地址,然后通过用户态程序传入到内核态程序,最后内核态程序调用 bpf_probe_read_kernel() 函数读到这一变量值。这一问题虽然得到了一个很好的解决,但每次都要通过用户空间程序读取avenrun变量的地址,这在高负载环境下是非常不友好的。
-
问题描述:如何利用eBPF技术去获取进程的堆栈信息?
解决办法:这个问题的关键在于如何利用libbpf程序去遍历进程,我们通过在网上查询发现可以通过BPF迭代器去解决这个问题,迭代器类型选择 iter/task ,运行结果如下:
但是发现用户态堆栈的信息打印不完全,如果想解决这个问题,仍得通过符号表,这避免不了和用户态程序进行频繁的交互,这同样在高负载环境下是非常不友好的。
-
问题描述:针对赛题要求,我们应该在内核模块和eBPF技术之间如何抉择呢?
解决办法:每个技术都有每个技术的应用场景,也都有各自的利弊,尽管eBPF技术相对来说更加灵活,但是从上文(一)中我们可以看到eBPF的局限性以及它实现功能的复杂性,因此考虑到我们需要一个功能更加强大、在高负载下性能更好、更加可靠的解决方案,最终我选择了内核模块。
-
问题描述:众所周知把一个在低版本上运行的工具在高本版上复现是一件非常不容易的事情,在这个过程中可以说是问题不断,主要碰到的问题就是低版本工具中的函数在高版本中不存在,数据结构及其字段也发生了很大的变化
解决办法:首先确定出现问题的低版本函数功能,然后在高版本源码中寻找与其功能相同的函数,同样数据结构及其字段也采用同样的方法,虽然我们目前已经成功将该工具在高版本上跑通,但是有些功能还是未能实现,我们目前仍在完善该工具的代码,以下是我们做出的部分修改展示,可以作为参考,后期会根据进展对这些代码进行开源
会议目的:进行操作系统大赛的选题
会议内容:根据我们自身对Linux操作系统的熟悉程度和擅长领域,我们决定从性能和诊断工具的方向出发,最终选定了《proj208-performance-and-diagnosis-tool》这一赛题
下一步计划:进行项目调研,一周后进行汇总,选择工具的开发技术,并和项目导师取得联系
会议目的:进行项目调研的汇总以及开发工具技术的选择
会议内容:经过汇总筛选,我们确定了内核模块和eBPF技术作为重点研究对象,并发现了赛题导师开源的diagnosis-tool工具,决定进行复现和学习,但还未联系到项目导师,暂定采用内核模块的方法进行开发
下一步计划:刘冰负责复现该工具,张子恒和南帅波负责对这个工具的各个模块进行学习
会议目的:对近期学习情况进行一个反馈
会议内容:刘冰这块已经基本完成了赛题导师开源工具的复现,张子恒和南帅波在赛题导师开源工具学习过程中无不感叹这个工具的强大以及代码编写的巧妙,因此也感到很迷茫,如果做内核模块的话,又该如何进行优化和完善呢?于是我们动摇了,决定去做eBPF方向
下一步计划:张子恒负责编写利用eBPF技术去监控一分钟平均负载值的程序;刘冰负责编写利用eBPF技术去遍历所有进程内核态堆栈的程序;南帅波负责编写利用eBPF技术去遍历所有进程用户态堆栈的程序
会议目的:解决在编写eBPF程序过程中遇到的问题
会议内容:刘冰和南帅波遇到的问题是虽然利用eBPF 的迭代器技术可以实现遍历打印进程的内核态堆栈,但是无法通过地址找到用户态堆栈名称。张子恒遇到的问题是由于负载的avenrun数组变量是全局变量,那么也就无法通过挂载函数来实现读取这一变量中的值,想到的解决办法是通过在/proc/kallsyms文件中读取avenrun变量的地址,然后通过用户态程序传入到内核态程序读到这一变量值,这也就意味着每次都要通过用户空间程序的读取,但这在高负载环境下是非常不友好的。针对这些问题,我们讨论后始终找不到一个好的解决办法,因此决定寻求老师们的帮助。
下一步计划:找陈莉君老师去探讨利用eBPF技术去实现这一工具的可行性;找谢宝友老师进行沟通,确定具体的开发方案
会议目的:探讨具体开发方案
会议内容:在找陈老师进行沟通后,我们进行了内核模块和eBPF技术的对比,发现内核模块突出的是性能而eBPF突出的是安全,而根据赛题要求的是开发一个高负载下性能分析与异常诊断工具,可见对于这个工具来说性能相对来说是更重要的,因此我们决定去做内核模块这个方向,并在和谢老师商讨后确定了具体地开发方案。
下一步计划:刘冰继续去完善diagnosis-tool工具中的复现;张子恒和南帅波负责对已复现模块进行优化,以及横向扩展
- 虽然最后没有选择用eBPF技术去实现这个工具,但是这个过程中学到了很多知识,像BPF迭代器、libbpf的内核态程序如何获取内核代码中的全局变量等,同时也达到了在真正的开发过程中,在实践上和内核模块的对比。
- 通过对谢宝友老师开源工具diagnosis-tool的学习,让我们对内核模块有了更深层次的理解,这对我们后期开发有着非常大的帮助。
- 在内核模块中可以灵活利用tracepoint、kprobe等性能机制
- 在开发过程中,让我们对Linux内核源码中cpu、内存、网络等方面代码有了从理论到实践上的认识。
- 学习了如何把采集到的数据生成火焰图,以及如何利用脚本生成图像。
时间 | 任务 |
---|---|
2023.5.15 - 2023.5.21 | 完成工具的基本雏形,并有文档记录 |
完成工具在功能上的扩充,并有文档记录 | |
2023.5.22 - 2023.5.25 | 完成工具在功能上的优化,并有文档记录 |
2023.5.26 - 2023.5.29 | 完成在高负载场景下对工具的测试及优化,并有文档记录 |
2023.5.30 - 2023.5.31 | 完成文档的汇总,并提交作品 |