调试器无法终止一个或多个进程_在调试器里看QQLive捉迷藏

    前些日因为安装腾讯课堂客户端而安装了腾讯视频,程序目录和主程序名都叫QQLive。我很少用电脑看视频,本来是不想安装的,但担心腾讯课堂依赖它,就安装了。

    安装之后,有时腾讯视频的窗口会跳出来,五颜六色的窗口蹦到前台,一旦发现,我就将其关闭。

    近几日发现了一个更大的问题。一个名叫QQLiveService的进程频繁触发页面错误,每秒钟600多次,几天下来累计值上亿,在系统里排名第一。因为频繁的页面错误,导致CPU净时间的累计值也很高,超过50分钟,注意了单位是分钟。今天的CPU差不多是以光速在工作,一个纳秒就可以执行几条指令,所以50分钟意味着CPU在这个进程上跑了很久很久。概而言之,QQLiveService成了系统里的CPU大户。

6868c64bacc60a7035e003e95fe672cd.png

    看上面的任务管理器截图,可以看到QQLiveService进程累计触发1亿3千2百多万次缺页异常,排名第一,轻松超过2-5名的累计总值,就连一向喜欢吃CPU的杀毒软件(MsMpEng)也甘拜下风。

    凭借多年的经验,一定是哪里出问题了,不然不应该有如此多的页面错误。

    熟悉老雷的朋友看到这,可能说:赶紧上调试器吧!

    是可以上调试器,不过,总是一上来就上调试器有点老生常谈了。今天换个工具吧,上VTune!近一两年认识老雷的朋友们,特别是金融行业的同行们,更习惯看我上VTune。

  启动VTune,选择Local Host > Attach to Process,指定QQLiveService的进程ID,让VTune开启强大的软硬件监视机制,全城布控,搜索蛛丝马迹。

    让VTune持续收集数据5分钟左右,停止收集,VTune开始加载符号,自动分析,因为要从美国搬运符号文件回来,这个过程有点慢,倒一杯庐山的云雾茶来,一边品味茶香,一边翻看案头的闲书等待......

    用了差不多十分钟,VTune把报告准备好了,先看看概要信息。

f3fdaedb3d44a2037a428271b195fdaf.png

    排在前五名的热点函数都是微软家的,都来自NT内核。而且前三名都与页表有关。看来QQLiveService触发如此多的页面错误,把NT内核折腾的挺惨,不停地忙活页表。前三个函数的CPU净时间加起来大约是1.6S

    继续看VTune的报告,打开Bottom-up视图。刚才几个函数都是内核空间的,现在从Bottom-up视图可以看到排在最前面的用户态函数是Process32NextW。

bd09093d922ca972aa7140ac344c2d27.png

    看过我以前文章的同行知道,Process32NextW是个“臭名昭著”的API,使用它枚举进程,做一次两次可以,如果频繁调用,那么副作用就会很大。所以一看到它,问题找到一半了。

    既然已经上了VTune,那就再用VTune帮我定位一下出问题的线程吧,观察VTune的线程视图,12028号线程有很明显的周期性运行规律,测量一下,每秒钟一个尖峰。

2d04e2310aa31ba40b46e40eea78daff.png

    选择一个尖峰放大,再测量,大约是22ms,也够长的。

0c72de66e205276979a465fea0c1c6c8.png

    如此看来,QQLiveService进程每1秒会执行一轮比较重的操作,耗时22ms,这期间,大约触发600多个页面错误(page fault)。

    到底进程里面在干什么呢?

    这下不上调试器不行了。

    唤出WinDBG,附加到QQLiveService,切换到12028号线程。

48fabd7f742ed97c70b25baccc8e2189.png

    观察运行时间:

0:000> .ttime

Created: Fri Mar 22 19:07:01.209 2019 (UTC + 8:00)

Kernel:  0 days 0:41:21.781

User:    0 days 0:11:01.453

    内核空间的时间高达41分钟,用户空间超过11分钟。

    对KERNEL32!Process32NextW设置断点,很快命中。执行kv观察调用经过。

2b376c5b4739716a08ecdf44956595a9.png

    明显的消息循环痕迹,观察消息参数,著名的113号消息,熟悉Win32编程的各位,这就是著名的WM_TIMER。

    为了消除大家的疑虑,打开winuser.h,上个截图吧。

0e47bffe20e8f0682a923c370cb6aaff.png

    如此看来,QQLiveService是在使用古老的WM_TIMER消息,触发一个周期性的动作,调用Process32NextW。

    我们不想跟踪微软的代码,执行gu跳出Process32NextW。如果比较调用Process32NextW前后的页面错误数,可以发现每调用一次,都会新增几次页面错误。

    接下来,重点看QQLiveService(名字有点长,为了节约篇幅,以下简称QQLS)为啥要调用Process32NextW这个API。

    QQLS收到Process32NextW返回的进程名后,会构造一个“腾讯字符串”,类名就叫CTXStringW。

    接下来会调用CompareNoCase做比较,这个是关键。简单来说,Process32NextW是用来枚举系统里所有进程的。QQLS每秒钟枚举一次系统里的所有进程,应该是在找某个进程,想看看系统里有谁在,有谁不在。这样寻找,有可能是在找同伴,也有可能是在找对手^_^。

    到底是在找谁呢?因为这个问题很重要,所以深入了解这个比较就非常重要了,因为这个比较操作的一方便是QQLS感兴趣的那个“人”。   

003387cd 53              push    ebx

003387ce 8d8dccfdffff    lea     ecx,[ebp-234h]

003387d4 ff1580203400    call    dword ptr [QQLiveService+0x12080 (00342080)] ds:002b:00342080={Common!CTXStringW::CompareNoCase (550b9200)}

003387da 85c0            test    eax,eax

    没有源代码,只能看汇编了。不过,因为是老雷最熟悉的x86汇编,所以理解起来不费事。

    ECX用来传this指针,前面的压栈操作只有一个,就是参数,du观察。

0:000> du ebx

00342fd4  "QQLive.exe"

    答案有了。原来QQLiveService找的是QQLive。

    是的,没有错,QQLiveService枚举所有进程,找的就是QQLive。至少在引起如此多页面错误的这个代码块中是这样的。

    打开QQLive的程序文件夹,QQLive和QQLiveService都在其中,显然是同门兄弟。

76bdfdfc2d53d3bb0a8da9e2324ec3a0.png

    分析到这里,可以知道QQLiveService枚举进程找的居然是和自己在一个文件夹里的QQLive程序。

    QQLive.exe是腾讯视频的应用程序(GUI)。从QQLiveService的服务描述中看,QQLiveService的功能是“腾讯视频加速服务”。

42725a00d25f51a2943773d6b421f0b7.png

        简单理解,QQLiveService是为QQLive加速的,帮忙的。QQLiveService没有界面,在后台默默帮助QQLive,但是却不知道QQLive何时起来,于是便勤奋地每秒钟枚举一下整个系统,看看QQLive是不是起来了。这有点像一个男生,看中了一个女生,又不好意思明说,于是就在暗中默默地搜索她的行踪。这也有点像经典的捉迷藏(据说英文叫Hide and Seek)游戏,QQLive进程起来时并不告诉想要为自己帮忙的QQLiveService,而是让它满城去找。

    那么,既然都是一家的软件,为什么不能用个更直接的方式来通信呢?比如用个命名的内核对象,比如发个ETW事件,比如写个文件,比如......有太多方法了。

   也许,设计这个逻辑同行没有觉得满城搜索有什么不好。或者就是觉得上面列出的方法不够好,“我就用Process32NextW又怎么了?”

    写到这里,突然想起上一篇文章里谈到的CS教育问题,再引用一下Peter先生的话吧。   

    “I encounter recent CS grads all the time that have absolutely no idea – I mean none, zero, zip, nada – about how a virtual address is translated to a physical address.  It might as well be by magic. Be clear about what I’m saying here:  I’m not saying they don’t know how virtual memory works in detail on some specific processor. I’m saying they don’t understand the concept of virtual memory at all.  You say “page fault” and they look back at you with a blank stare, as if you were reciting one of the Vedas.  As for knowing the differences between running in kernel mode and user mode… forget about it.  Interrupts?  Ha!  Memory mapped I/O? No way. Port space, device registers?  No clue.”

--Peter G. Viscarola

https://www.osr.com/nt-insider/2016-issue1/peter-pontificates-cs-education-yup/

    诚然,莫说是刚毕业的新手,即使是做了很多年开发的程序员,很多人也是对“Page Fault”一知半解。

    也有人说,做互联网的没有必要学如此底层的东西。我觉得这句话害了很多人。Peter在他的文章中曾呼吁身处不同位置的人各尽其力。

    “We, here in the industry, have got to do something to try and stop the stupidizing of CS curricula in the States.  That means you should do something about this, oh gentle and ever-lazy reader.  If you’re on an alumni committee, ask what’s being taught in the CS department.  Make your views known.  If you teach at a University (even part-time), lobby to teach a real operating systems course to undergraduates. Push the department chair (who probably came up during the time when operating systems were still important and already harbors a closet resentment for the fact that assembler language isn’t taught to freshmen) to talk to folks in the industry about our needs and revisit the graduation requirements.”

-- Peter G. Viscarola

    最后说明一下,写这篇文章的目的不是为了批评,也不是为了指责,只是指出问题,与同行们分享和交流。

***********************************************************

正心诚意,格物致知,以人文情怀审视软件,以软件技术改变人生。

欢迎关注格友公众号

5f9d224bb43ccfd61456e6c5a30590ab.png

  • 3
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值