本文主要是介绍【腾讯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占用问题的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!