引用注明>> 【作者:张佩】【原文:www.YiiYee.cn/blog】

这是我到新公司后上手的第一个issue。

一线project师找到我,说有一个urgent issue有没有兴趣看一看。当时他已经组织了一个team的人帮助他。我便成了当中一员。

问题确实非常紧急,已经影响了产线的生产。当时正临清明假期,导致非常多人都岌岌可危地面临清明加班的可能。事实上这个问题已经报出来好几个月。但一线project师一直以绥靖政策对待。以各种方式把问题发生率减少到客户接受的水平。绥靖政策长时间以来是有效的,但近期却突然失效,发生率飙升到了20%。一线project师才真的着急了。

问题定性

组一个team的人debug同一个问题,在我以前是未以前历过的。初始的想法当然是觉得人多力量大。但真正实施的时候,也可能遭遇人多嘴杂的窘况。

在怎样定性issue的问题上大家意见不一致。

从一线project师描写叙述的情况看。问题发生时没有蓝屏。先前曾debug过此问题的同事说,问题发生时仅仅有一个进程和线程活着。其他的全部进程都处于block状态(除了ideal进程)。最关键的是,他仍然能够通过Windbg单步。

有人把issue定性为系统崩溃、BSOD或系统异常,这是全然不对的。假设被这样定性,CPU必须是hang住的。但如今CPU却是活的。还能够单步。况且并没有蓝屏发生,定性为BSOD就更不对了。

应该属于什么类型的问题呢?从描写叙述上来看,系统是活的,仅仅是失去了响应而已。所以我把issue定性为系统的Software Hang。

可能的情况非常多,比方一个系统进程突然跑偏了,导致系统界面失去响应。

或者像本例中的情况:系统内核自己陷在一个死循环里面,无暇处理其他任务。

分析问题

我第一天仅仅拿到了dump文件。由于仅仅有一台能live debug的系统,使用上非常难统筹。

我拿到dump文件后。第一动作是赶紧看那个唯一live的线程。这类issue的问题点非常集中。所以我非常有把握能够非常快找到一些实用线索。

ChildEBP RetAddr  Args to Child
85846dd0 8fd95b75 ffffffff 85846ef0         nt!KeClockInterruptNotify+0x28a
(Inline) -------- -------- -------- --------     hal!HalpTimerClockInterruptEpilogCommon+0xa
85846de0 00000000 000000d1 00000000 hal!HalpTimerClockInterruptCommon+0x3e
85846de0 00000000 000000d1 00000000 hal!HalpTimerClockInterrupt+0x1cb
85846ef0 80c66050 8584762c 00000002 win32k!ENUMAREAS::ENUMAREAS+0xb9
8584711c 80da5d98 80da5d98 00000000 win32k!bSpBltScreenToScreen+0x2f8
858474dc 80da5d98 80da5d98 00000000 win32k!SpBitBlt+0x2bc
85847510 80da5d98 80da5d98 00000000 win32k!SpCopyBits+0x27
85847654 000006ce 000000a0 00000027 win32k!NtGdiBitBltInternal+0xa39
85847700 80d440a8 80c7d5e0 80c7d598 win32k!zzzBltValidBits+0xc6557
85847768 85847b18 85847ad0 80dac008 win32k!xxxEndDeferWindowPosEx+0x20b
858477a8 00000000 8312c5a0 80dac008 win32k!xxxProcessDesktopRecalc+0x10b
858477e0 d4b8b27d 85847d00 80dac008 win32k!xxxProcessEventMessage+0x7a
……
85847d3c 0118fb90 00000000 00000000 nt!KiSystemServicePostCall

调用栈上面有时钟中断的处理函数。

当硬件中断发生的时候。CPU会亲自把当前活动线程的运行权抢占下来给ISR用。所以这是非常正常的事情。并非发生了所谓的“系统异常”。

去掉中断相关处理函数后,调用栈的最后一个函数就是win32k!ENUMAREAS::ENUMAREAS。

我把这个函数的汇编代码稍微研究了一下,发现这里面居然存在一个死循环!当然,不是全部的循环都天生会成为“死循环”。

死循环仅仅可能发生在特殊的状态下。

从汇编代码看。Win32k正运行在一个循环中。这个循环仅仅会进入一次。

同事给我提供了一个实用信息:每次问题发生的时候,看到的都是在运行同一个函数。结合这些信息,当然就能够断定这个循环是“死循环”了。

但当我把这个发现提出来准备team内讨论时,却受到了冷遇。没有人相信或愿意和我讨论这个死循环。debug team的一位同事没看分析过程,但把同一个问题问了我两遍:你真有把握这是死循环吗?令我非常郁闷。

第二天当Live Debug环境再次被建立起来,我能亲自上手的时候,我就首先在这个函数里面单步了好一会儿。确定它确实是在我发现的那4行汇编代码里面循环不止的。事实证明,无论我按多少次F10,指令寄存器都再没有离开过那4行汇编代码(除了发生时钟中断抢占的情况)。我当时心里立马舒缓了非常多,由于我还是非常害怕的,万一不是死循环,就颜面扫地了。

死循环的逻辑非常easy,仅仅有4行汇编。两个跳转(jump)语句在互相跳来跳去,是一个典型的while循环。

901b7b0f   394104         cmp     dword ptr [ecx+4],eax
901b7b12  7e5e             jle        win32k!ENUMAREAS::ENUMAREAS+0xb6 (901b7b72)901b7b72  034908         add      ecx,dword ptr [ecx+8]
901b7b75  eb98             jmp     win32k!ENUMAREAS::ENUMAREAS+0x53 (901b7b0f)

花几分钟时间把这4行代码反汇编成C语言。由于这时候我们已经有了win32k的private symbol,所以反出来的内容可读性非常强。

while (p->yBottom <= this->yBoundsTop)
{(char*)P += p->sizeScan;
}

这是一个普通的循环。

但假设循环体里面的p->sizeScan值为0。就能导致死循环。

这正是原因所在。

猜猜猜

每一个资深Debugproject师都有一样本领,那就是“猜”。闭源调试的时候。“猜”是不二利器。当然了不能乱猜。否则会导致怨狱的——不能变成猜疑。

我发现当死循环发生时。yBottom的值是1200,yBoundsTop的值是1780。我对1200非常敏感。由于目标平台的分辨率是1800×1200。

再加上通过private symbol展现出来的变量名称,更添加了猜的底气。所以就先猜1200是屏幕的高。

1780是什么呢?结合Win32k类的名称ENUMAREAS以及变量名称yBoundsTop,猜它是某个窗体的左顶点y轴位置。

继续猜。再细致看调用栈中各个函数的名称,能够大致知道它在干什么:通过GDI接口实现BitBlt亦即绘图操作。

如今基本给出一个合理猜想:问题发生的时候,有一个窗体被移出了屏幕以外区域,当这个窗体试图刷新UI界面时,有概率会导致Win32K的死循环。

下一步是依据上述合理猜想。给出一个解决方式:測试过程中,避免一切移动窗体位置到屏幕外的动作。

一线project师在了解了这个情况后还不放心。在解决方式中又加了一条:避免一切窗体最小化动作。

当这个方法报告给客户后,客户立马进行了測试。在上千次測试过程中,问题再也没有发生。而且。到如今为止的一个多月时间里,issue再也没有发生。

技术细节

当事实确定下来后,大家都非常吃惊。

由于win32k是一个非常稳定的OS模块,假设真是win32k的bug,其影响一定非常地深远。这个问题如今已经报告给微软,微软project师还在分析,并已承认是系统Bug的事实。但微软project师更倾向于觉得这不是Win32k的bug,而是传给Win32K的參数被破坏了。最后的结果怎样。还需等待。

假设我是这位微软project师,我就会把这个调用栈上相关函数的实现逻辑,先好好地分析一遍。对比出问题和正常情况下的參数区别,区别出此issue是由代码逻辑导致的,还是异常參数导致的。假设是异常參数导致的,需确定是參数传入时就有问题。还是后面出的问题。

以此逐步厘清。

while (p->yBottom <= this->yBoundsTop)
{(char*)P += p->sizeScan;
}

当中this的类型为ENUMAREAS。其部分定义例如以下:

win32k!ENUMAREAS+0x000 iDir               +0x004 xBoundsLeft           +0x008 yBoundsTop            +0x00c xBoundsRight         +0x010 yBoundsBottom

p指向的结构体为SPRITESTATE,其前面部分定义例如以下:

_SPRITESTATE+0x000 yTop+0x004 yBottom              +0x008 sizeScan

我分析觉得:p指向一个类型为SPRITESTATE的变长结构体数组,当中变量sizeScan表示到达下一个结构体的长度。当问题发生时,p已经枚举到了数组中的最后一个结构体。其成员变量sizeScan等于0。由于这个循环缺少了异常处理。从而使得p一直在处理最后一个结构体,在它身上做死循环,并奇怪地把整个系统都hang住了。当我通过Windbg强制跳出这个死循环后,系统奇迹般地活了过来。

Win32K里的死循环相关推荐

  1. (转)关于Android中为什么主线程不会因为Looper.loop()里的死循环卡死?引发的思考,事实可能不是一个 epoll 那么 简单。...

    ( 转载请务必标明出处:http://www.cnblogs.com/linguanh/, 本文出自:[林冠宏(指尖下的幽灵)的博客]) 前序 本文将会把一下三个问题阐述清楚以及一个网上的普遍观点的补 ...

  2. python queue 多进程_python中的Queue与多进程(multiprocessing)

    最近接触一个项目,要在多个虚拟机中运行任务,参考别人之前项目的代码,采用了多进程来处理,于是上网查了查python中的多进程 一.先说说Queue(队列对象) Queue是python中的标准库,可以 ...

  3. 进程间通信-Queue

    1. Queue的使用 可以使用multiprocessing模块的Queue实现多进程之间的数据传递,Queue本身是一个消息列队程序,首先用一个小实例来演示一下Queue的工作原理: #codin ...

  4. 【612页】Android 大厂面试题及解析大全(中高级)

    前言 包含了腾讯.字节跳动.小米.阿里.滴滴.美团.58.拼多多.360.新浪.搜狐等一线互联网公司面试被问到的题目,涵盖了中高级Android技术点. 多线程并发问题,基本是面试必问的. 大部分同学 ...

  5. Java多线程(二):Thread类

    Thread类的实例方法 start() start方法内部会调用方法start方法启动一个线程,该线程返回start方法,同时Java虚拟机调用native start0启动另一个线程调用run方法 ...

  6. STM32硬件错误HardFault_Handler的处理方法

    在用Keil对STM32的程序进行仿真时程序有时会跑飞,停止仿真程序会停在HardFault_Handler函数里的死循环while(1)中.这说明STM32出现了硬件错误. STM32出现硬件错误可 ...

  7. bd3.2 Python高级

    四.设计模式 1. 单例 1.1 请手写一个单例 class SingleMode(object):_instance = Nonedef __new__(cls, *args, **kwargs): ...

  8. 一文看懂Python多进程与多线程编程(工作学习面试必读)

    进程(process)和线程(thread)是非常抽象的概念, 也是程序员必需掌握的核心知识.多进程和多线程编程对于代码的并发执行,提升代码效率和缩短运行时间至关重要.小编我今天就来尝试下用一文总结下 ...

  9. 【Python】编程笔记9

    文章目录 进程和线程 一.基础知识 二.多进程(multiprocessing) 1.初体验 2.Pool(进程池) (1)非阻塞 (2)阻塞 (4)代码解读 (3)分析 3.子进程 (1)开启子进程 ...

最新文章

  1. 总结接口与类和抽象类的关系
  2. .NET MVC-去掉验证
  3. Struts国际化步骤
  4. MOCTF-Web-还是水题
  5. RDB和AOF速度测试
  6. 预防死锁可以破坏哪些死锁的必要条件
  7. 前端面试题汇总(JS 基础篇)
  8. 第十三节:使用Lombok简化你的代码
  9. 游戏筑基开发之变量、运算符、转义字符的注意点(C语言)
  10. 主要空间数据挖掘方法
  11. XP侧边栏(XP桌面秀)
  12. mac pycharm如何打开setting
  13. java将时间撮转成日期,Java将时间戳转换为日期
  14. 去日本东京旅行(1)-- 机场篇
  15. java编程思想----8,对象的容纳
  16. (Web、触屏)微信支付功能调用以及QQ回调地址配置、以及遇到的一些坑
  17. 中国省份城市 数据表
  18. 运用DXperience12.2创建甘特图日程管理应用
  19. 【小程序】如何开发属于自己的一款小程序
  20. 中软融鑫 java笔试题_中软融鑫2014校招java开发工程师笔试题

热门文章

  1. (零 ) 天猫精灵接入Home Assistant-总说明
  2. 计算机平面设计会学cad吗,请问学建筑与室内设计哪样好是不是都要学CAD
  3. java 下载微信图片_java 微信服务器下载图片到自己服务器
  4. Pycharm远程开发教程
  5. 输入 xxxhub 的背后到底发生了什么
  6. ClearCase建立VOB的流程
  7. HC06 ,ZS040 (没有按键的)AT配置相关
  8. 如何快速将AS2.0/AS3.0/fla/swf/ppt等课件产品转化为html5跨平台交互课件。
  9. Windows 下编程检测显示器信息及插拔
  10. 轻量级服务器 虚拟化,轻量级应用虚拟化服务器之客户端测试