抽丝剥茧定位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(gh_2052d3e8c27d)

原文发表时间:2017-03-28

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏架构师之路

换IP的是你,凭啥重启的却是我?

一、缘起 很多公司,技术经常遇到这样的场景: 1)硬件升级,要换一台高配机器 2)网络重新规划,若干服务器要调整机架 3)服务器当机,要重新部署恢复服务 … ?...

3987
来自专栏IT技术精选文摘

如何实现系统的可扩展性和高可用性

概述 可扩展性,高可用性和性能 可扩展性,高可用性,性能和关键任务这些术语对不同组织或组织内的不同部门来说意味着不同的事情。它们经常被互换,造成混乱,导致管理...

99510
来自专栏coolblog.xyz技术专栏

I/O模型简述

最近在学习 Java NIO 方面的知识,为了加深理解。特地去看了 Unix/Linux I/O 方面的知识,并写了一些代码进行验证。在本文接下来的一章中,我将...

3767
来自专栏大魏分享(微信公众号:david-share)

深度分析:Istio替代Spring Cloud的合理性

一、现有微服务架构 微服务本质上是分布式架构、分布式应用、分布式计算。 分布式计算可以带来的好处有:性能、可靠性、弹性、可扩展性、可用性、稳健性。 而从应用开发...

2.1K8
来自专栏搜云库

分布式和集群区别?什么是云计算平台?分布式的应用场景?

分布式是指将一个业务拆分不同的子业务,分布在不同的机器上执行,集群是指多台服务器集中在一起,实现同一业务,可以视为一台计算机,一个云计算平台,就是通过一套软件系...

2855
来自专栏Young Dreamer

好用的前端页面性能检测工具—sitespeed.io

引言 最近在做HTTP2技术相关调研,想确认一下HTTP2在什么情境下性能会比HTTP1.x有显著提升,当我把http2的本地环境(nginx+PHP)部署完成...

51110
来自专栏程序员互动联盟

【专业技术第四讲】如何检测浏览器的快慢?

现在做浏览器的大概有下面几个方向吧 1. 从事浏览器外壳的工作,开发基于浏览器的各种应用和扩展; 2. 做浏览器内核优化的,大概又分为几个部分: a. 渲染模块...

34712

比较服务网格体系结构

如果你正在围绕微服务构建您的软件和团队,那么你应该正在寻找更快迭代和灵活扩展的方法。服务网格可以帮助你在保持(或增强)可见性和控制的同时实现这一点。在这篇博客中...

3916
来自专栏Java架构沉思录

Nginx是门好技能,希望你也会

Nginx很多人都知道可以用来做反向代理和负载均衡。但实际上Nginx可以做的远不止这些。

992
来自专栏WeTest质量开放平台团队的专栏

我是如何一步步攻破一家互联网公司的

最近在研究Web安全相关的知识,特别是SQL注入类的相关知识。接触了一些与SQL注入相关的工具。周末在家闲着无聊,想把平时学的东东结合起来攻击一下身边某个小伙伴...

1462

扫码关注云+社区

领取腾讯云代金券