`
linx_bupt
  • 浏览: 15091 次
  • 来自: 北京
社区版块
存档分类
最新评论

定位导致Windows蓝屏的代码的方法

阅读更多

 

一、简述

       Windows操作系统在遇到内核级别的错误或异常的时候,会通过蓝屏的方式终止计算机的运行,目的是为了防止计算机进一步受到伤害,起到保护操作系统和计算机的作用。但是,对于用户来说,计算机的蓝屏,是一件非常令人恼怒的事情,毕竟计算机蓝屏时提供的错误信息对于普通用户来说太不友好了,除了前篇一律的“请检查最近安装的硬件或软件”之类的没什么价值的提示之外,还给出了一段二进制的错误码,让人无从找到出错的原因。

       有的用户可能比较有钻研精神,会Baidu一下或Google一下,看看这错误代码到底是什么含义,以及如何解决这样的蓝屏问题,从网上搜寻的答案要么是计算机中病毒了,建议重装系统,要么是告诉你硬件驱动有问题等等笼统的说法。

这些说法对于普通用户来讲,可能他们说那好,我重装系统吧,我换一个硬件吧,大多就能解决问题,可对于我们程序员,如果是因为自己的软件导致了系统的蓝屏,这样的答案就显得有答非所问了,对自己去定位到底是什么组件和自己的软件冲突了,甚至是哪一行代码写的有问题导致系统的蓝屏,是没有丝毫的帮助的。大多数的程序员对于蓝屏问题是一筹莫展的,不知道去调查蓝屏问题应该如何下手,其实,Windows是提供了工具WinDbg来分析蓝屏问题的,利用好了WinDbg就掌握了定位蓝屏问题的切入点。

 

二、WinDbgdump文件介绍

通过设置,当系统发生错误时,如蓝屏了,就会在系统目录下产生一个Dump文件,如MEMORY.DMP 。这个文件的主要意义在于分析系统错误发生的原因,以作出解决的方法。

它可分为三种类型:

1、完全内存转储。这个文件比较大,和物理内存相当,包含了程序崩溃前系统及用户模式下的所有信息。

2、核心内存转储。这个文件大小约物理内存的三分之一,主要包含崩溃前系统内核的运行情况。一般为了分析内核错误,就选用这种文件。

3、小内存转储(128K)。这个文件小,刚好一个页面文件大小。它包含了相对比较少的信息,主要可用于微软的在线分析。

以上三种形式的文件可以在我的电脑——〉鼠标右键——〉属性——〉高级——〉故障及恢复中设置。可以根据自己的需要,设定dump文件的存储路径。

如下图:



<!--[endif]-->

 

1 设置dump文件

一般性的,只是为了定位引发蓝屏问题的进程和函数,只需要小内存转储即可。可想而知,核心内存转储提供了更多的信息,在小内存转储提供的信息不能满足您的要求的时候,可以试着去分析核心内存转储,但完全内容转储几乎是用不上的,为什么,文件太大了。

有一点需要注意,如果没有启用虚拟内存页面文件(比如您认为您的内存足够大不需要虚拟内存的时候),是不能确保计算机在崩溃时自动生成内存转储文件的。尤其是在选择核心内存转储时,一定要启动虚拟内存页面文件。

设置好计算机在宕机时自动产生dump文件后,在计算机蓝屏时,就会在您指定的目录下,生成宕机时刻的内存转储信息,通过这些信息,就能知道是谁(哪个进程的哪个函数)引起了这次蓝屏时间了。

那我们怎么分析dump文件呢?这儿就需要使用到我们的调试利器——WinDBG了,

首先看看什么是WinDbg

WinDbg是在windows平台下,强大的用户态和内核态调试工具。相比较于Visual Studio,它是一个轻量级的调试工具,所谓轻量级指的是它的安装文件大小较小,但是其调试功能,却比VS更为强大。它的另外一个用途是可以用来分析dump数据。

虽然WinDbg也提供图形界面操作,但它最强大的地方还是有着强大的调试命令,一般情况会结合GUI和命令行进行操作,常用的视图有:"thread""stack" "command",其中command视图是默认打开的。 下面简单介绍常用的命令:

  查看stackkbkpkP

  查看内存:dddadb

  分析死锁:!cs!lock

  自动分析:!analyze

  加载dll: .load.reload

显示加载的模块信息: lmlmvm

 

三、使用WinDbg分析dump文件

WinDbg的使用教程可以参考官方文档,这儿只介绍如何通过WinDbg分析蓝屏时产生的dump文件,并找出dump文件中有助于定位引发蓝屏的进程和执行代码的信息。

打开WinDbg ,首先要设置符号文件路径。符号文件是用来分析错误原因位置信息的文件,这个文件可以从微软的网站随着WinDbg 一起下载,当然了比较大了。如果不愿意下载,也可以设置远程地址连接到微软网站上的符号文件。

设置符号文件的地址:在WinDbg 的菜单File--> Symbol File Path 中,设置:

SRV*DownstreamStore*http://msdl.microsoft.com/download/symbols(两个*之间为临时文件的路径,可自己任意设置),如图:


2 设置符号文件的地址

然后就可以载入dump文件了,如图:


3 载入dump文件

         载入dump文件之后,就可以通过WinDbg提供了shell来分析dump文件了。在命令行中输入:!analyze –v,可以得到dump文件的详细信息。以下是分析出来的内容:

 

kd> !analyze -v
IRQL_NOT_LESS_OR_EQUAL (a)
An attempt was made to access a pageable (or completely invalid) address at an
interrupt request level (IRQL) that is too high.  This is usually
caused by drivers using improper addresses.
If a kernel debugger is available get the stack backtrace.
Arguments:
Arg1: 00000014, memory referenced
Arg2: 00000002, IRQL
Arg3: 00000000, bitfield :
	bit 0 : value 0 = read operation, 1 = write operation
	bit 3 : value 0 = not an execute operation, 1 = execute operation (only on chips which support this level of status)
Arg4: 80507bbe, address which referenced memory

Debugging Details:
------------------
READ_ADDRESS:  00000014 
CURRENT_IRQL:  2
FAULTING_IP: 
nt!MiCheckForControlAreaDeletion+3c
80507bbe 8b4014          mov     eax,dword ptr [eax+14h]
CUSTOMER_CRASH_COUNT:  1
DEFAULT_BUCKET_ID:  DRIVER_FAULT
BUGCHECK_STR:  0xA

PROCESS_NAME:  tdps.exe

LAST_CONTROL_TRANSFER:  from 8051413e to 80507bbe

STACK_TEXT:  
a980ac84 8051413e 81b53e88 8054a100 810fd070 nt!MiCheckForControlAreaDeletion+0x3c
a980aca0 8051ec6d 811ab970 ffffffff 8191b3f0 nt!MiRestoreTransitionPte+0x74
a980acb8 8051f21e 00000400 80522004 c0600028 nt!MiRemovePageFromList+0xd1
a980acc0 80522004 c0600028 c00052a8 00000000 nt!MiRemoveAnyPage+0x56
a980ad00 8051cd61 00a5500a 00bb50e8 0012f6b4 nt!MiCopyOnWrite+0x116
a980ad4c 8054051c 00000001 00a5500a 00000001 nt!MmAccessFault+0x9f9
a980ad4c 7c91dab3 00000001 00a5500a 00000001 nt!KiTrap0E+0xcc
WARNING: Frame IP not in any known module. Following frames may be wrong.
0012f6b4 00000000 00000000 00000000 00000000 0x7c91dab3
……(省略)
 

 

         从以上dump文件中分析出来的信息中,我们可以得到如下2条有用的信息:

1、引起蓝屏的进程

 

……
PROCESS_NAME:  tdps.exe
……
 

2、引起蓝屏时进程最后代码执行信息

 

……
STACK_TEXT:  
a980ac84 8051413e 81b53e88 8054a100 810fd070 nt!MiCheckForControlAreaDeletion+0x3c
a980aca0 8051ec6d 811ab970 ffffffff 8191b3f0 nt!MiRestoreTransitionPte+0x74
a980acb8 8051f21e 00000400 80522004 c0600028 nt!MiRemovePageFromList+0xd1
a980acc0 80522004 c0600028 c00052a8 00000000 nt!MiRemoveAnyPage+0x56
a980ad00 8051cd61 00a5500a 00bb50e8 0012f6b4 nt!MiCopyOnWrite+0x116
a980ad4c 8054051c 00000001 00a5500a 00000001 nt!MmAccessFault+0x9f9
a980ad4c 7c91dab3 00000001 00a5500a 00000001 nt!KiTrap0E+0xcc
WARNING: Frame IP not in any known module. Following frames may be wrong.
0012f6b4 00000000 00000000 00000000 00000000 0x7c91dab3
……
 

         上述信息的解读为:

l  进程tdps.exe中,地址0x0012f6b4处的代码调用了内核nt.dll中的KiTrap0E函数

l  nt.dll中的KiTrap0E函数调用了nt.dll中的MmAccessFault函数

l  nt.dll中的MmAccessFault函数调用了nt.dll中的MiCopyOnWrite函数

l  nt.dll中的MiCopyOnWrite函数调用了nt.dll中的MiRemoveAnyPage函数

l  nt.dll中的MiRemoveAnyPage函数调用了nt.dll中的MiRemovePageFromList函数

l  nt.dll中的MiRemovePageFromList函数调用了nt.dll中的MiRestoreTransitionPte函数

l  nt.dll中的MiRestoreTransitionPte函数调用了nt.dll中的MiCheckForControlAreaDeletion函数

l  之后,计算机蓝屏了

对我们来说,最有用的信息已经解读出来了:

tdps.exe中的0x0012f6b4处的代码调用,引起了计算机蓝屏!准确的说,是计算机蓝屏前,计算机执行的最后的指令。

 

四、map文件和cod文件

         dump文件中,我们分析出的最重要的信息就是代码地址0x0012f6b4以及执行代码的进程tdps.exe了。可是,现在又遇到了另外的一个问题,如何通过dump文件给出的函数调用地址来找到函数所在的代码呢?

         这儿需要用到两个在编译过程中产生的中间文件:*.map文件和*.cod文件。

         *.map文件是linker*.obj文件链接成二进制文件时所生成的映射表,系统的linker load在载入这个二进制文件时需要参考它。通过map文件,我们能定位到代码地址0x0012f6b4所在的obj文件,而每一个obj文件是由一个*.c文件编译而来的,所以,我们能通过map文件找到代码地址0x0012f6b4对应的c文件。

         *.cod文件是编译器将源代码的C文件翻译成的汇编代码文件。

我们通过map文件找到引发蓝屏的*.cod文件,而通过*.cod文件,则能找到出错的c代码。

VC6中设置编译选项,产生map文件和cod文件:


4 Project Settings – Link中,勾选Generate mapfile


5 Project Settings – C/C++ -- Listing Files中,Listing file type栏选择Assembly, Machine Code, and Source

         这样,编译出来的中间文件中,就会产生map文件和cod文件(准确的说,是包含汇编、机器码和源码对应关系的cod文件,这一点很重要)。接下来,我们将会介绍,如何通过1)dump文件给出的代码地址2)map文件3)cod文件,找到引发蓝屏的那行代码。

 

五、根据代码地址找到源代码

         tdps.exe在编译过程中生成的map文件是tdps.map,我们首先看一下map文件的内容:

 

tdps

 Timestamp is 4ee8562a (Wed Dec 14 15:54:18 2011)

 Preferred load address is 00400000

 Start         Length     Name                   Class
 0001:00000000 00166fbdH .text                   CODE
 0002:00000000 0000310fH .rdata                  DATA
 0002:00003110 0000015eH .edata                  DATA
 0003:00000000 00000104H .CRT$XCA                DATA
 0003:00000104 00000104H .CRT$XCZ                DATA
 0003:00000208 00000104H .CRT$XIA                DATA
 0003:0000030c 00000109H .CRT$XIC                DATA
 0003:00000418 00000104H .CRT$XIZ                DATA
 0003:0000051c 00000104H .CRT$XPA                DATA
 0003:00000620 00000104H .CRT$XPX                DATA
 0003:00000724 00000104H .CRT$XPZ                DATA
 0003:00000828 00000104H .CRT$XTA                DATA
 0003:0000092c 00000104H .CRT$XTZ                DATA
 0003:00000a30 0007c925H .data                   DATA
 0003:0007d358 006d1d30H .bss                    DATA
 0004:00000000 000000c8H .idata$2                DATA
 0004:000000c8 00000014H .idata$3                DATA
 0004:000000dc 00000438H .idata$4                DATA
 0004:00000514 00000438H .idata$5                DATA
 0004:0000094c 00000afaH .idata$6                DATA

  Address         Publics by Value             Rva+Base     Lib:Object
 0001:00002cc0    _FrameWorkInit             00403cc0 f   frmwkcom.obj
…………
0001:00129140   _ThreadCtrlProc@4        0052a140 f   lps_CtrlProc.obj
 0001:0012e0d3  _MacPrimitiveProcess     0052f0d3 f   lps_CtrlProc.obj
 0001:0013019f  _RlcPrimitiveProcess     0053119f f   lps_CtrlProc.obj
……
 

我们对如下两条信息感兴趣:

l  程序的载入地址0x00400000,这个地址是Windows操作系统装载程序的默认起始地址。

l  Rav+Base的值,这个值表示程序中函数所在的地址。

我们在dump文件中得到了蓝屏前计算机执行的最后一段代码的地址为0x0012f6b4,这段地址是相对于程序起始地址的偏移地址。那么它的Rva+Base地址为:

0x0052f6b4 = 0x00400000 + 0x0012f6b4

         根据0x0052f6b4这个地址,我们在map文件的Rav+Base这一列中查找,找到小于这个地址的最后一列,即0x0052f0d3这一列,可以看见这一列对应的函数名称为_MacPrimitiveProcess,对应的obj文件为lps_CtrlProc.obj。这意味着,在lps_CtrlProc.c文件的MacPrimitiveProcess()函数中的某个地方,就是dump给出的地址所在的代码。

         接下来计算相对于lps_CtrlProc.obj的偏移地址:

005e1 = 0x0052f6b4 – 0x0052f0d3

         我们再看看lps_CtrlProc.cod文件:

 

……
202  : #ifdef _LOG_INFO
; 203  : 	//log输出
; 204  : 	LOG_DEBUG(ENTITY, LOGSID_630X_TD_PSCHEDULER, "STRU_SETUP_REQ_DwPCH", MHD( DLC, MP( PRIM_CLASS_PHY, P_CPHY_RL_SETUP_REQ_DwPCH), 0), MHD(0), sizeof( STRU_SETUP_REQ_DwPCH), (char*)(t_pRcv->m_acDataBuf));        													
  00538	8b f4		 mov	 esi, esp
  0053a	68 d6 00 00 00	 push	 214			; 000000d6H
  0053f	6a 10		 push	 16			; 00000010H
  00541	ff 15 00 00 00
……
  005e1	8b fc		 mov	 edi, esp
……
 

         我们通过偏移地址005e1找到了一行汇编代码,而这个汇编代码的地址正是dump文件给出的地址0x0012f6b4。而此处汇编是lps_CtrlProc.c文件的第202,203,204行的汇编展开。

 

六、总结

         Dump文件记录了计算机在蓝屏之前最后一刻的内存信息,其中就包含了引发计算机蓝屏的原因、引发进程和该进程最后一刻引发蓝屏时执行的代码执行的地址。

map文件和cod文件则提供了代码执行的地址与源码的对应关系。(引申:这种对应关系能让我们准确的定位一些使用一般方法难以定位的问题,诸如空指针错误,内存越界导致程序崩溃等)

将这两个步骤结合起来,我们就可以解译出计算机在蓝屏时所保存下来的最后指示信息,找出计算机在最后一刻执行的哪一行代码。

         最后说一句,通过这种方法,我们只是找到了dump文件指出的导致计算机文件的蓝屏的那一行代码。但是这行代码跟引起计算机蓝屏有何关系,需要如何修正代码来解决蓝屏的问题,则需要读者根据自己遇到的情况,做出进一步的分析和判断了。

 

  • 大小: 41.7 KB
  • 大小: 27.6 KB
  • 大小: 78.1 KB
  • 大小: 62.9 KB
  • 大小: 63.4 KB
1
0
分享到:
评论
1 楼 warnerhit 2011-12-20  
太强大了。。我电脑玩游戏的时候偶有出现蓝屏。我原来以为是超内存引起的。

相关推荐

Global site tag (gtag.js) - Google Analytics