一次由QQ浏览器性能分析引起的性能问题

一、背景

近来有个项目用到了sysinternals出品的监控对象:sysmon.exe。然则有反馈,sysmon.exe进程(下文为方便描述简称为sysmon)在某一特定前提下,持续占用cpu,一样平常为会跑满cpu的一个逻辑核。例如,假如cpu为双核4线程,则sysmon的占用在25%。且sysmon占用的内存持续升高。

本文中的图是在调试情况中随本文的编写而截的,以是各位看到图中的sysmon进程PID不同等。

图 1. sysmon的高cpu、内存占用

二、问题定位与阐发

问题初步定位

应用ProcessExplorer察看sysmon进程各个线程的cpu应用环境,发明各个线程的cpu占用率并不高。然则发明有大年夜量的新线程被创建,同时又有大年夜量的旧线程被销毁。可见,cpu光阴片被主要用于线程的创建、销毁操作了。

图 2. 血色为线程销毁、绿色为线程创建

同时察看sysmon的句柄应用环境,发明句柄数量非常的高,竟然在异常短的光阴内达到了万的量级。

图 3. 句柄数量非常

以是接下来的阐发重点将环抱线程的大年夜量创建和销毁以及句柄的非常创建展开。

调试器中定位问题

I. 定位线程大年夜量创建销毁的问题

由图2可知,创建的线程都因此sysmon.exe+0x494a8这个地址开始的。顺着这个线索摸以前,发明sysmon+0x495aa这个客栈返回地址,范围落在sysmon.exe+0x494a8这个函数的范围内。顺着sysmon+0x495aa这个栈回溯线索往回找,终极确定sysmon+0x1820e为出问题的函数,下图为sysmon各个子线程的栈回溯:

图 4. 定位到出问题的函数

阐发进行到这里先暂时告一段落,稍后再验证阐发的是否精确。接下来,跟进第二个问题:内存占用过高。

II. 定位内存占用过高问题

由图3可知,sysmon在段光阴内创建了大年夜量的句柄,在调试器中看下这些句柄的应用环境:

图 5. 句柄全局统计

由上图可以看到,短短几分钟之内,句柄数目由图3的4.5万多个增添到现在的72942个,此中Event类型的句柄占了72878个,比例高达99%,而且这些event都必要人工重置状态。这隐含着一个信息,在法度榜样没有人工重置这些event的状态前,event不会被开释。短光阴内创建如斯量级的事故且不开释,光阴久了,内存占用自然就上去了。

在调试器中确认问题

I. 内存占用过高问题切实着实认

经由过程上一节的阐发,我们初步定位了cpu以及内存占用过高的缘故原由。接下来我们来确定一下,是否是由于这个缘故原由导致的。趁着各位刚看完内存占用过高的问题,我们先从这个问题入手。

认识windows编程的人可以知道,创建event一样平常会调用CreateEvent这个函数。那么我们在此函数高低如下断点:

解释一下这个断点的意思:首先断在kernel32.dll中的CreateEventW函数调用的开始,然后打印一下客栈调用,着末规复法度榜样的履行。

OK,下面是履行结果的一部分:

图 6. 在创建Event时获取栈回溯调用

上图是大年夜概履行了5秒钟阁下的样子,从右边下拉条来看输出大年夜概有几千条,也便是5秒钟内创建了几千个Event,这和我们上面察看到的句柄暴增征象项符。往上翻一下每次的栈回溯输出,发明都是sysmon+0x181b7这个相近启程了CreateEventW的调用。同时这里先埋个负担:留意OpenTraceW函数。

这里同样还有别的一个必要留意的地方,便是图6中的着末一个sysmon模块的函数调用返回地址:sysmon+0x181b7。结合图4中我们定位的,离导致新线程不绝被创建的地址sysmon+0x1820e很近。至于这两个地址的因果关系,详见下文阐发。

II. 线程大年夜量创建销毁的问题切实着实认

刚才提到 sysmon+0x181b7与sysmon+0x1820e两个地址很近,下图可以让各位在直不雅上懂得一下两个地址究竟有多近:

图 7. sysmon+181b7与sysmon+1820e

以是,根据现在掌握的证据来看,故事很有可能是这样的:

1. 大年夜量创建线程

2. 新创建的线程中又大年夜量创建了Event

3. 创建的线程退出了,然则因为Event必要Manual Reset,以是Event并没有被开释

世界没有免费的午餐,这么大年夜量的Event是必要内存寄放的。以是,大年夜量创建线程导致cpu应用变高,大年夜量创建未开释的Event导致内存应用变高。

看到这里你可能会说,上面都是你的预测,有什么证据支持么?下面我们在调试器中验证我们上面的预测。在正式开始之前,我想先阐述一下我的思路:上面的阐发结论是,新创建的线程导致新创建了Event;以是,假如我们下两个断点,第一个断点在sysmon+0x1820e处,第二个断点在系统的CreateEventW函数上;那么,这两个断点应该是交替射中;因为两个断点一个是位于sysmon模块的线程函数地处,别的一个则为与系统的kernel32.dll模块地址,以是两个断点理论上并没有一定联系,假如征象真的是两个断点交替射中,则可以验证我们上文的预测。

[1] [2] [3]下一页

下面是实际操作,首先是两个断点:

图 8. 两个断点

解释一下,首先是每行最开始的数值是断点编号,0代表0号断点,1代表1号断点;e代表两个断点都是激活(enable)状态;然后是后面的kernel32!CreateEventW和sysmon+0x1820e,代表着该断点的地址(详细见上文阐发);着末双引号里.echo开首的意义为,一旦射中该地址的断点,打印一下当前断点的编号与客栈调用关系,着末规复履行。下面是射中的环境:

图 9. 断点交替射中

可见征象与我们设想同等,印证了我们预测的精确性。可能,有较真的读者照样会说,你现在说的这些都照样间接证据。那么,接下来我们就和这个问题刚一下正面,用代码阐明问题。

呈现此问题的根本证据

上文中的两个地址(sysmon+181b7与sysmon+1820e)都位于函数sysmon+0x180E0中。直接给出此函数的伪代码:

图 10. 导致cpu过高线程的进口函数

这里说一下关键逻辑,留意图10中的第44行处的函数调用sub_140018360,以及本函数的地址sub_1400180e0。本代码片段的意义是,在某个全局旌旗灯号的感化下,有可能会调用到sub_140018360这个函数,同时给这个函数传一个参数为1。

接下来看一下sub_140018360函数做了什么,为了方便阐明问题,伪代码做了部分折叠处置惩罚:

图 11. 导致递归调用的地方

留意69行的线程创建函数,也便是说,在sub_140018360函数的参数为1的环境下,不掉足,会走到69行的线程创建,线程的进口函数是sub_1400180e0。眼熟么?不眼熟的请看下图10。也便是说,sub_1400180e0着末会调用sub_140018360函数,而sub_140018360函数内部创建了一个线程,创建的这个线程又调用回了sub_1400180e0函数,而由图6的阐发可知,恰是因为sub_1400180e0里面的OpenTraceW函数导致创建了大年夜量的Event,从而使内存飙升。这是一个类似递归调用的逻辑,假犹如等走下去,是不会有尽头的。

阐发到这里,就很清楚了。为了拿到某种Trace的结果(上文中提到的负担,OpenTraceW函数),sysmon用了一个类似递归的逻辑,反复重试考试测验获取拿到结果。恰是因为:

1. 递归调用创建线程,导致cpu应用率飙升

2. 上面创建的线程经由过程OpenTraceW创建了大年夜量Event,导致内存飙升

至此,sysmon占用高cpu和高内存的直接缘故原由已经找到。

三、什么缘故原由导致的此问题的孕育发生

在调试历程中,发明如下几个征象:

1. sysmon会重启一次,且重启后的sysmon才会有高cpu和内存占用

2. sysmon重启前TsService.exe会拉起几个进程,然则拉起的进程停止的很快

看到这里,可能各位有个疑问,TsService.exe是什么器械?谜底是,此进程为QQ浏览器的后台办事进程。证据如下(此可履行文件有正常的腾讯署名):

图 12. TsService.exe为QQ浏览器的办事进程

然后,我跟进了一下TsService.exe启动的那几个进程是什么:

图 13. TsService.exe启动的子进程

顺带一提,图13中的子进程,第一行没截全的地方是:set-default-browser –slient;-)。嗯,接下来才是我们必要关注的点,几个xperf子进程的敕令。

写到这里,插个题外话,在办理这个问题查资料的历程中,查到过这么一篇资料:《QQ浏览器机能提升之路-windows机能阐发对象篇》。这篇文章知心的给出了图13中xperf敕令的具体解释:

图 14. 腾讯方面对xper参数的解释

以及踩过的坑:

图 15. 文中提到的启动掉败的环境

文中提到了ProcessMonitor和ProcessExplorer会导致xperf掉败,必要先关闭再启动。颠末我的测试,sysmon也是会导致xper.exe抛出上图中相同的差错。

再插一个题外话,xperf为微软官方供给的机能信息网络对象,借助此对象可以分系windows系统上法度榜样的许多机能问题。

还记得本节刚开始提到的两个征象么——sysmon重启后会呈现高占用征象。难道和腾讯浏览器启动的xperf有关?

四、此问题在随意率性机械上的复现

为了证实上面的预测,我做了如下实验。

不卖关子,首先直接给出结论,经由过程xperf和sysmon的“协同共同”,可以在任何机械上复现sysmon高cpu和内存占用的问题。

详细步骤为:

1. 首先最好有一个全新安装的纯净系统(可选)

2. 在系统上安装sysmon办事,详细敕令为,在治理员权限的cmd下履行: sysmon -i

3. 竣事sysmon的办事(由于不绝止会导致xperf敕令报错)

上一页[1] [2] [3]下一页

4. 应用xperf.exe依次履行如下敕令:

5. 从新启动sysmon办事

上述步骤履行完毕后,新启动的sysmon办事的cpu应用率会占满一个逻辑核。内存应用率也会逐步的上去。

假如在sysmon的cpu应用率上去之后,再次履行

则sysmon的应用率则会降回到正常水平。

五、谁关闭了sysmon?

经由过程上面的阐发可知,此问题呈现的根滥觞基本因在于sysmon重启后,所必要的资本(NT Kernel Logger)被占用,导致重启后的sysmon办事无法拿到所需资本,不停在重试。那么到底是什么缘故原由导致sysmon退出呢?

结合上文的阐发,我将排查重点放在了TsService.exe办事上。经由过程对TsService.exe 进程的OpenServiceW函数下断点,终极将问题定位到了PerfTool.dll这个模块中:

图 16. 关闭sysmon办事

六、此问题的办理规划(部分)

I. 规整洁

留意,这里只是根据上文中的阐发,提出几种办理规划,不是终极的独一办理规划。

经由过程图10中的第43和44行可以知道,在调用导致“递归”的函数之前,还有一个if判断,这个判断用sub_14008da0(我已经改名为fnReadRegistryValue)的返回值,与1做逻辑与操作,只有这个操作为真的环境下才会孕育发生“递归”的逻辑。接下来,我们看一下fnReadRegistryValue这个函数干了啥:

图 17. 读取注册表中的设置设置设备摆设摆设项

可以看到,该函数的返回值为Data,而Data中的数据来自于注册表的如下键值:

假云云键值的值,与1做与运算为0的话,即可停止“递归”的逻辑。也便是说,假如这个键值下的数值是偶数,则永世都不会由于这两个函数的“递归”调用,从而不会孕育发生高cpu和内存占用的问题。

那么这个键值是干什么用的呢?从sysmon的赞助文档可知,sysmon是一个敕令行对象,是有启动参数的,结合Options的意义,可知不合的sysmon监控启动参数,会导致这个值的不合。后来的实验也证明这个键值切实着实会根据sysmon的启动参数不合而不合。

以是,一个可行的办理规划是,当cpu占用很高且内存在迟钝上升时,可以暂时将该键值置为0,使法度榜样跳出“递归”逻辑,然后再将此值改回原本的值,以免影响其监控需求。

II. 规划二

由图16可知, TsService.exe关闭sysmon应用的是硬编码。而sysmon是支持安装办事时应用其他办事名的。以是,别的一个可行的办理规划是安装sysmon办事时,应用其他的办事名,防止被TsService.exe关闭。

七、总结

正如孕育发生本文机能问题的“递归”问题一样。原先是机能排查的行径,却终极导致了机能问题,而后必要继承进行机能问题的排查……而此问题的呈现,并不是由单一身分导致,而是多种身分配相助用的结果。因为机能测试的特殊性,并不是所有的机械都必要进行这项测试,以是初始化的那个行径:关闭sysmon,启动xperf进行机能信息网络,并不会在每一台机械上履行。以是,这个问题的呈现,有必然的“随机”性,并不是在每台机械上必现。

上一页[1] [2] [3]

赞(0) 打赏
分享到: 更多 (0)
免责申明:本站所有资料均来自于网络,版权归原创者所有!本站不提供任何保证,不保证真实性,并不承担任何法律责任

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址

阿里云优惠网 更专业 更优惠

阿里云优惠券阿里云大礼包