【腾讯TMQ】抽丝剥茧定位Windows客户端CPU占用问题

2024-04-14 04:38

本文主要是介绍【腾讯TMQ】抽丝剥茧定位Windows客户端CPU占用问题,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

摘要

本文主要展示了从电脑管家CPU占用过高问题发现到解决的全过程。包括分析问题的思路、解决问题的方法、压力测试的设计、优化前后数据对比等。同时,在末尾分享了自动弹窗工具的设计思路,以及笔者对于测试自动化的一些思考和看法。

一、导火索

某天,我们接到一例用户反馈——问题的核心的在于管家在没有触发任何漏洞、扫毒、垃圾清理和体检的场景下,却占用了比较高的CPU资源。截图如下:

但是这个问题在测试过程中是从未出现,而且从用户反馈的场景描述中,也提取不出必现路径和关键逻辑。为此,我们主动联系该用户,在用户许可并且积极配合的前提下,获取其真实的机器环境和场景,抓取相关的管家信息,从而进一步进行分析和定位。

二、定位分析

联系用户抓取ETL文件进行分析(该工具的使用可参见微软官网申明:https://msdn.microsoft.com/library/windows/hardware/mt270977(v=vs.85).aspx)。

从收集上来的etl性能日志来看,CPU的异常主要是由管家进程的:A.dll(消耗大概10%的CPU)、B.dll(消耗大概16%的CPU)、C.dll(消耗大概7%的CPU)这三个模块影响,定位到模块之后,继续向下深挖,定位到三个模块下的具体函数调用关系链时,我们发现这三个模块下,资源占用最高的函数都有一个共同点,那就是他们都是通过微软的API-SetWinEventHook函数向系统注册的回调函数。
SetWinEventHook函数本质是windows系统向外提供的一种消息处理机制,每当有特定消息发出后,在目标应用程序处理该消息之前,SetWinEventHook程序就会先捕获该消息,提前调用注册的回调函数处理并可以决定是否继续将消息往下传送。具体有关于SetWinEventHook的使用可参见https://msdn.microsoft.com/en-us/library/dd373640(VS.85).aspx官方使用文档说明。这里不再展开讨论。
由于每个模块调用SetWinEventHook进行注册的回调函数都不相同,其消息的过滤策略以及内部逻辑都不一样,所以其占用的CPU的数值会有所区别。其中占用最高的B.dll模块是因为没有处理好窗口消息的过滤,A.dll模块其实本身对于消息的过滤机制处理的较为完善,之所以占用CPU比C.dll要高一些的原因在于A.dll的回调函数处理中,某个注册表读取的操作消耗了资源。
那排除三个模块业务上的干扰的因素,我们提取问题的核心本质:当三个模块同时都注册了SetWinEventHook到底会造成什么结果?为什么会让管家一直占用如此高的CPU资源?为什么用户机器会出现高占用,而测试机器却没有呢?
相信下面抽象出来的模型图,能够很清晰的展现出问题的本质。

由此可见,每一个窗口消息过来之后,windows相当于调用三次管家的模块进行处理。如果用户的windows消息数量处于某临界值以下时,问题的表现并不明显,而一旦用户机器上有某进程不停的创建窗口消息,那将导致管家一直在处理消息函数,从而占用大量的系统资源。

三、复现场景

猜测是用户环境中,某一软件在频繁的创建窗口消息,从而导致SetWinEventHook函数不停的向注册的回调函数分发数据,每一个分发的数据都需要一定的处理时间,占用一定的CPU资源,因此从用户感知的层面,就出现管家CPU一直占用过高的情况。为此,我们测试方开发了一个小软件,用于模拟在电脑上频繁创建窗口。虽然没有完全复现出用户CPU占用情况,但是可以看出当窗口数骤然剧增时,管家Tray进程的占用量也明显增加。

四、解决方案

4.1不同角色任务分配
4.1.1开发侧:
(1)、优化窗口消息处理模块代码,由A.dll模块提供公共模块用于注册窗口监听事件,统一管理;
(2)、A.dll持续优化窗口信息过滤模式。
4.1.2测试侧:
(1)、SetWinEventHook加入代码扫描规则—所有管家代 码中,只允许出现一次调用该函数的场景(即A.dll中);
(2)、增加对于弹窗功能的压力测试和性能测试。
4.2 代码逻辑优化
SetWinEventHook是由微软提供的系统api,其本身触发管家回调函数,进行消息处理的逻辑是没有问题的,因此我们重点要优化的是管家对于回调消息的处理逻辑:由于A.dll模块在窗口消息过滤方面比较完善,其CPU占用较高的原因是由于回调函数内部有一个读取注册表的操作,当不断接受窗口消息时,就会引发其不断的进行注册表读取操作,从而引其高CPU的占用。当A.dll解决掉注册表的性能后,其注册SetWinEventHook的功能所占用的CPU是一个可接受的范围内。因为A.dll下的窗口信息接收和过滤能力是经过几轮优化后,被实践证明其功能实现已经是比较成熟的。因此各个业务方不再自己独立进行窗口监听,而是统一由A.dll中注册和监听窗口消息。
优化后的处理流程:

窗口消息的注册监听统一由A.dll模块管理,由于其冗余消息的过滤策略比较完善,由A.dll来统一管理并获取窗口信息分发给各业务,可以大幅减少各个业务获取窗口或者宿主进程信息的次数。

五、压力测试

当上述优化项改动完成基本的功能测试后,为了便于以后能实时发现和解决窗口弹窗过多的问题,我们开发了一个简单的弹窗小工具,对管家进行压力测试(具体工具的设计和使用见附录)。
1、短时间内触发多个弹窗,抓取PC的etl文件进行分析。
以约5-7s内触发100个窗口为例,抓取同一PC修改前后的管家版本的etl进行分析,连续抓取10次后,查看管家进程占用CPU数据。抓取数据波动比较稳定的时刻,进行压力测试场景,抓取etl文件,分析优化前后tray资源的占用情况:初始模块,弹窗压力测试下,抓取数据波动比较稳定的情况下,tray占用cpu的权重大约为6%-8%之间,如下图:

替换优化后的模块,再进行弹窗压力测试,tray占用的cpu的权重下降到2%左右,如下图:

抓取10次数据对比图:

由上述对比图可以发现,在不停触发弹窗的场景下,可以明显感知到,优化后,tray进程的CPU占用资源明显下降。由此,该弹窗工具既可以在一定程度上复现用户电脑出现的场景,又可以验证我们针对本次CPU占用过高的问题的解决措施的有效性。

六、总结和思考

6.1、总结:

6.2、思考:
用户的环境的复杂度会远超于我们测试时的环境,对于用户反馈的Bug,尤其是测试环境无法复现的Bug要重点关注,抽丝剥茧、层层分析背后的原因,并且根据分析后的结果,迅速采取强有效的措施解决。
同时,又要及时吸取经验教训,通过各种手段(如codereview、压力测试等)保证该问题不会重复出现。同时,在以后的测试分析场景中,对于类似的一些功能,也需要考虑到可能会导致CPU升高的一些特殊场景。
最后,便是自动化的实现。自动化测试作为软件测试的一种技术手段,常常会被想象成是测试人员走向人生巅峰的必备技能,从而导致其重点在于自动化而非测试本身,容易陷于解决技术问题,而忽略了其结果是否能满足测试的需要。在此,笔者推荐测试专家JamesA.Whittaker提出过的测试构建方法:寻找缺陷—提炼模式—识别机械部分–开发工具。详细思路可见笔者附录:弹窗工具的设计。

附:弹窗工具的设计

此附录为笔者参考测试专家JamesA.Whittaker和史亮所提到过的测试工具构建方法和自动化弹窗工具设计实践结合的展示,希望能带给大家一个新的看待自动化的视角:

1、寻找缺陷:发现或收集软件的缺陷or问题。

本次发现的问题是管家客户端CPU占用过高问题。

2、提炼模式:分析缺陷的根本原因,提炼一个模式,用它捕获相似的缺陷,一个模式就相当于一种攻击手段,这个过程需要回答如下几个问题:

(1)何时实施该攻击?
管家安装完成并正常运行。

(2)该攻击会捕获何种问题?

该攻击会导致管家进程的CPU占用资源飙升。

(3)利用该攻击如何识别软件问题?

执行该攻击的同时抓取windows的性能日志文件ETL,通过ETL文件分析管家的资源占用情况,识别攻击是否会引发软件异常问题。

(4)如何实施攻击?

短时间内生成大量的windows窗口消息。

(5)样例和分析?

参见前文提到过的问题和分析。

3、开发自动化工具:识别出攻击过程中机械的部分,编写工具去自动化模式的应用。
此处的测试自动化不是自动的执行测试用例,而是提供计算机辅助功能,其目的是让计算机完成高负荷的运算,让人专注于富有智力挑战的任务。
首先,识别本次攻击过程的机械部分—毫无疑问就是如何产生大量的windows窗口信息。
是否需要自动化:需要。
原因:
1、大量的windows消息包含大量重复性的创建or显示or关闭等等一系列的操作;
2、手工的点击速度完全无法模拟机器在短时间内产生大量窗口信息(通过代码可能1s就可以创建100个窗口,而手工点击最多也就也就两三个)。
自动化弹窗工具设计流程,如下图:

使用说明:
通过OpenParaOfWindows.txt中的三个参数和可以灵活的配置不同的弹窗场景。
其参数说明:详见附件中的使用说明(附件扫码下载)。

这篇关于【腾讯TMQ】抽丝剥茧定位Windows客户端CPU占用问题的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



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

相关文章

Vue3绑定props默认值问题

《Vue3绑定props默认值问题》使用Vue3的defineProps配合TypeScript的interface定义props类型,并通过withDefaults设置默认值,使组件能安全访问传入的... 目录前言步骤步骤1:使用 defineProps 定义 Props步骤2:设置默认值总结前言使用T

Web服务器-Nginx-高并发问题

《Web服务器-Nginx-高并发问题》Nginx通过事件驱动、I/O多路复用和异步非阻塞技术高效处理高并发,结合动静分离和限流策略,提升性能与稳定性... 目录前言一、架构1. 原生多进程架构2. 事件驱动模型3. IO多路复用4. 异步非阻塞 I/O5. Nginx高并发配置实战二、动静分离1. 职责2

解决升级JDK报错:module java.base does not“opens java.lang.reflect“to unnamed module问题

《解决升级JDK报错:modulejava.basedoesnot“opensjava.lang.reflect“tounnamedmodule问题》SpringBoot启动错误源于Jav... 目录问题描述原因分析解决方案总结问题描述启动sprintboot时报以下错误原因分析编程异js常是由Ja

MySQL 表空却 ibd 文件过大的问题及解决方法

《MySQL表空却ibd文件过大的问题及解决方法》本文给大家介绍MySQL表空却ibd文件过大的问题及解决方法,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友参考... 目录一、问题背景:表空却 “吃满” 磁盘的怪事二、问题复现:一步步编程还原异常场景1. 准备测试源表与数据

解决Nginx启动报错Job for nginx.service failed because the control process exited with error code问题

《解决Nginx启动报错Jobfornginx.servicefailedbecausethecontrolprocessexitedwitherrorcode问题》Nginx启... 目录一、报错如下二、解决原因三、解决方式总结一、报错如下Job for nginx.service failed bec

SysMain服务可以关吗? 解决SysMain服务导致的高CPU使用率问题

《SysMain服务可以关吗?解决SysMain服务导致的高CPU使用率问题》SysMain服务是超级预读取,该服务会记录您打开应用程序的模式,并预先将它们加载到内存中以节省时间,但它可能占用大量... 在使用电脑的过程中,CPU使用率居高不下是许多用户都遇到过的问题,其中名为SysMain的服务往往是罪魁

MySQ中出现幻读问题的解决过程

《MySQ中出现幻读问题的解决过程》文章解析MySQLInnoDB通过MVCC与间隙锁机制在可重复读隔离级别下解决幻读,确保事务一致性,同时指出性能影响及乐观锁等替代方案,帮助开发者优化数据库应用... 目录一、幻读的准确定义与核心特征幻读 vs 不可重复读二、mysql隔离级别深度解析各隔离级别的实现差异

C++ vector越界问题的完整解决方案

《C++vector越界问题的完整解决方案》在C++开发中,std::vector作为最常用的动态数组容器,其便捷性与性能优势使其成为处理可变长度数据的首选,然而,数组越界访问始终是威胁程序稳定性的... 目录引言一、vector越界的底层原理与危害1.1 越界访问的本质原因1.2 越界访问的实际危害二、基

Python多线程应用中的卡死问题优化方案指南

《Python多线程应用中的卡死问题优化方案指南》在利用Python语言开发某查询软件时,遇到了点击搜索按钮后软件卡死的问题,本文将简单分析一下出现的原因以及对应的优化方案,希望对大家有所帮助... 目录问题描述优化方案1. 网络请求优化2. 多线程架构优化3. 全局异常处理4. 配置管理优化优化效果1.

MySQL中优化CPU使用的详细指南

《MySQL中优化CPU使用的详细指南》优化MySQL的CPU使用可以显著提高数据库的性能和响应时间,本文为大家整理了一些优化CPU使用的方法,大家可以根据需要进行选择... 目录一、优化查询和索引1.1 优化查询语句1.2 创建和优化索引1.3 避免全表扫描二、调整mysql配置参数2.1 调整线程数2.