Angelo Lee's Blog
This is my kingdom .If i don't fight for it ,who will ?

在工作中,许多情况下需要将遇到的问题告知客户。但是一般来说,一个问题的重现是需要场景和时间的。如果让客户为了一个不确定有没有价值的去追踪的问题花费时间,很显然,这是不可取的。通过Windbg可以轻松实现对dump文件的抓取,这样就可以将问题(bug)出现时的场景、现象完全展示给客户,如果你会一点调试技术的话,那无疑是锦上添花了。

通过简单Dump能获得的基本信息

如果有出错程序的dump, 哪怕dump不是在合适的时机获取的, 也可以分析出有用的信息.

  • 通过vertarget查看系统版本和系统运行了多长时间.
  • 通过!peb查看环境变量的情况. 由于很多第三方软件都习惯把自身路径添加到环境变量中, 所以这里很多时候可以看出一些已经安装的软件.
  • 同时还能看到当前进程所加载的DLL和对应路径.

检查DLL和对应路径时, 可以重点检查如下的一些项目:

  1. 有没有防毒程序的DLL加载.
  2. 有没有类似MSVCRTD或MFC42D的debug版本DLL加载. 如果有, 请说明程序使用的某些组件是否是在Debug模式下编译的.
  3. 通过MFC42这样的DLL大致判断程序时如何开发的. 比如是否使用MFC或ATL. 如果加载了mscoree, 询问一下程序是否使用了托管代码.
  4. 通过lmvm命令检查值得怀疑的DLL的详细版本和公司名称. 必要情况下还可以使用sos!savemodule命令把DLL保存到本地以检查链接情况, 等等.
  5. 通过lmf命令还可以检查是否有unload的module. 很多问题是由正在使用的module被unload导致的, 也有很多问题最后导致某些module被unload. 看看这里的unload module是不是正常情况.
  6. 检查module的数量, 是否有动态生成的module加载. 如果进程中加载的module过多, 则容易导致内存地址碎片. 比如对于asp.net程序, 很多module是对应于aspx页面动态编译生成的, 需要通过设置debug=false来激活batch compilation, 以减少动态module的产生.
  7. 通过dump文件的尺寸来判断内存使用情况. 一般dump的大小与实际内存使用的大小一致.
  8. 检查某些系统dll来判断某些系统组件是否已经升级到最新. 比如检查mscorwks的版本就可以判断.NET Framework的版本以及是否打过SP1, 检查msado15就可以判断MDAC的版本.

Crash dump中需要重点关注的信息


Crash都是发生在某一函数中, 而95%的crash都是由下面两种情况中的一种导致的:

  • 发生crash的函数获得了错误的参数.
  • 发生crash的函数使用了损坏了的内部数据.

代码的执行过程是对数据进行变化的过程. 对同一段代码, 在相同环境下, 如果使用的数据都相同, 那么执行的结果肯定是唯一的. 如果函数发生崩溃, 那么肯定是使用到的数据跟理想情况有差别. 函数使用的数据来源: 一是函数传入的参数, 二是函数体引用到的成员变量或全局变量.

 

因此要分析crash dump, 大多数情况都是寻找错误数据的来源. 下面是一些常见的, 导致数据错误的例子:

  1. 使用了未初始化的变量. 比如没有分配内存的指针, 没有初始化的CriticalSection.
  2. 错误地计算了函数参数. 比如调用函数的时候弄错了传入参数的顺序, 字符串操作时算错了字符串长度.
  3. 错误地使用数据导致发生corruption. 比如double free导致heap curruption, 在多线程环境下忘记同步导致据全局变量计算错误, COM的AddRef和Release调用不配对.
  4. 违背程序逻辑地使用数据. 比如在程序加载必要的资源以前就开始使用这些资源的函数.

由此可见, crash dump的分析完全取决于程序的情况. 能否从crash dump中挖掘出有用的信息, 取决于:

  • 对目标程序的熟悉程度. 包括程序的架构, 重要函数的作用, 重要的数据结构, 函数之间的调用逻辑, 关键函数的实现细节.
  • 对基础知识的掌握程度, 包括汇编, 异常, 内存, API, 消息, CRT, 等等.

一般来说, 分析一个dump可以通过下面的步骤入手:

  1. 看清楚是何种异常导致的崩溃.
  2. 对齐symbol, 找到发生崩溃的函数名字, 以及对应的汇编代码和源代码.
  3. 列出callstack
  4. 检查callstack是否合理
  5. 检查发生崩溃的函数是否得到了正确的参数.
  6. 检查发生崩溃的函数使用的数据是否正确.
  7. 结合上面的信息, 构思来龙去脉, 然后用资料来证明, 或者反驳自己的猜想.
  8. 通过进一步的操作来获取更有意义的资料. 比如激活pageheap后, 重新抓取dump, 或者干脆进行Live debug

关于CRT的文章: http://www.codeproject.com/KB/cpp/Short_Story_VCPP_CRT.aspx?msg=2319521


Dump File Define:

WinDbg中的定义:A file that contains a snapshot of certain memory regions and other data related to an application or operating system. A crash dump file can be stored and then used to debug the application or operating system at a later time.

A user-mode crash dump file can be created by Windows when an application crashes, and a kernel-mode crash dump file can be created by special Windows routines when Windows itself crashes. There are several different types of crash dump files.

我的理解是dump文件相当于一个事故的现场,将触发问题(现象)发生的条件(例如进程、内存空间等)从内存中保存并转移集中起来,这样做的目的是方便给调试者提供详细的信息,以便发现问题的根本点。

什么是内存转储文件, 即dump?

======================

内存转储文件时一个进程或系统在某一给定的时间的快照. dump文件的种类有很多, 不同种类的dump文件包含不成程度的数据.

 

用户态内存转储文件 VS 内核态内存转储文件

======================

如果你抓一个进程的dump文件, 那么你抓取的是用户态的dump文件.

如果你抓取整个系统的内存dump文件, 那么你抓取的是内核态的dump文件.

一般来说, hung, crash, memory leak, exception的处理都不需要知道操作系统那段时间在做什么的, 所以抓取kernel dump浪费硬盘空间, 而且关于.net的debugging在内核态是几乎不可能的.

 

迷你DUMP(mini dump) VS 完整DUMP(full dump)

======================

通常, 大家对dump的分类是mini dumps 或 full dumps, 实际上这个概念有点不那么正确. full dump实际上是带有额外信息的mini dump.

 

Full Dump就是使用了/ma开关抓取了的dump文件, 这个开关意味着你抓取了如下的信息

  • 完整内存数据- full memory data
  • 句柄数据- handle data
  • 未加载的模块信息- unloaded module information
  • 基本内存信息- basic memory information
  • 模块信息- module information
  • 包括时间信息在内的线程和栈信息- thread and stack information including thread time information.

总之, 你会在一个文件中拿到比你想要的还多的信息.

 

Mini Dump是使用了/mdi开关抓取的dump文件, 这个开关意味着以下的信息会被抓取:

  • 模块- module
  • 线程- thread
  • 栈- stack
  • 栈上的指针引用的任何内存- any memory that is referenced by a pointer on a stack
  • 一些读写段- some read-write segments

这些东西大多会被用来查看dump文件被生成的时候, 线程正在执行什么. mini dump的尺寸一般只有几兆大小, 所以它的好处是它会被很快地被写好, 并且不占什么空间, 缺点是, 你不会得到多少关于.net的信息.

 

这里的开关指的是windbg.exe的.dump命令的开关.

 

另外, 当你运行引用程序崩溃的时候, 你会被询问是否要把错误信息发送给Microsoft, 那, 这种情况下你发送的就是mini dump, 没有什么personal的信息会被发送出去.


弄明白什么是dump文件之后我们还需要知道dump文件的分类。

Dump文件也一样,在创建的时候也是有区分的,我所接触到的就2大类,也只有这2类:Kernel-ModelUser-Model.中文的意思是内核模式(或者叫核心态模式)和用户模式。至于什么是内核模式什么是用户模式,你可以看一下用户模式与内核模式。这里需要注意的是,所指的分类是指根据创建dump文件的环境来区分,如果你调试的程序错误发生在调用win32 API或者系统服务的时候,那么,该dump文件属于Kernel-Model,反之,属于用户模式。

How to getdump file?

Tool Comparison:

Feature

ADPlus

Dr. Watson

CDB
and
WinDbg

UserDump

Creating a dump file when an application crashes (postmortem debugging)

Yes

Yes

Yes

Yes

Creating a dump file when an application "hangs" (stops responding but does not actually crash)

Yes

No

Yes

Yes

Creating a dump file when an application encounters an exception

Yes

Yes

Yes

Yes [??]

Creating a dump file while an application is running normally

No

No

Yes

No [??]

Creating a dump file from an application that fails during startup

No

No [??]

Yes

Yes

Shrinking an existing dump file

No

No

Yes

No

 

实际上Windbg就是比CDB多了个UI界面,显得友好一点,实际上也更方便一点,命令的使用都是一样的。

2). Dump 的抓取

.dump 命令:

.dump [Options] <File Name>

Options 表示有很多选项:

 /o表示可以重写 (overwrites)一个已经存在的dump文件并使用相同的文件名。

/f表示有2层意思:如果在内核模式,将创建一个完整的Kernel-Model dump文件。该文件包含所有出错时的内存信息。要注意的是,这样的话dump文件会很大。另一层的意思是如果在用户模式下,将创建包含进程所占的内存大小,程序执行的情况,以及相应的处理和一些有用的其它信息。

/m[options],该命令是创建一个小的内存dump文件(内核模式下)或者一个minidump(用户模式下)。这样创建出来的dump文件体积小,又包含有用的信息。如果配合相应的选项,创建出来的dump文件可以说是“短小精悍“了!

.dump /m c:\example_dump.dmp
生成标准的minidump, 转储文件通常较小,便于在网络上通过邮件或其他方式传输。 这种文件的信息量较少,只包含系统信息、加载的模块(DLL)信息、 进程信息和线程信息。
dump /ma c:\example_dump.dmp
 带有尽量多选项的minidump(包括完整的内存内容、句柄、未加载的模块,等等),文件很大,如果条件允许(本机调试,局域网环境), 推荐使用这种。

.dump /mf c:\example_dump.dmp

该命令的意思是创建一个mini dumpc:\盘下,并且命名为example_dump,该文件的后缀名是.dmp。不同的是,在该文件中,添加了所有目标程序可访问的(内存)页面信息在里面。这样更方便调试。

抓取的时机:

实际上,察看dump文件是个很浩大的工程,也和枯燥。所以,如果能够提供给客户一个比较精确的dump文件,他看起来相对来说减少了很多时间,而且又有效率,那么对你的performance也是大有帮助的。

上面说到了2dump文件:full dump mini dump

Full dump 很大,包含的信息多,创建时候相对省力,只需要在debugger中断的时候用下.dump命令就可以了。Mini dump需要找到异常第一次发生的地方,这其中,还有许多异常是程序预期的,也就是说,你需要有判断该异常是不是导致程序中断的最终原因的能力。

一般的说,如果调试器出现second chance,那么你可以往上去追踪相同的异常。确定是该异常引发的下面一系列异常,ok,这时候可以抓dump(mini dump)

至于出现second chance的原因,是由于调试程序的时候,如果发生了异常(first chance),程序就会被挂起,那么调试器就会得到通知,如果该异常被处理了,程序将继续运行,如果应用程序没有处理这些异常,调试器会再次接到通知,这时,就是second chance。如果在这时候抓取dump, 那么应该是个full dump

更好用的是ADPlus工具,可以设定条件断点等等之类的东西,之后我将继续为大家讲解。


Debug Diagnostic Tool 和 WinDBG 的比较

这篇文章中, 比较了二者的区别, 介绍了一些简单的用法, 比如分别如何抓hung dump, crash dump.

http://blogs.msdn.com/tess/archive/2008/05/21/debugdiag-1-1-or-windbg-which-one-should-i-use-and-how-do-i-gather-memory-dumps.aspx

 

解释Symbol

解释什么是Symbol, 怎么用Symbol.

http://blogs.msdn.com/tess/archive/2005/12/05/why-do-i-get-weird-function-names-on-my-stack-a-discussion-on-symbols.aspx

 

如何使用adplus抓dump

http://blogs.msdn.com/tess/archive/2006/01/11/511773.aspx

 

如何编写adplus的配置文件, 抓dump

http://blogs.msdn.com/tess/archive/2005/11/30/498297.aspx


什么是ADPlus?

===============

ADPlus是一个Microsoft Product Support Services (PSS) 的工具, 可以用来排查任何进程或者应用程序的停止响应(hang), 或者崩溃(crash)的错误. 经常地, 你可以使用ADPlus(ADPlus.vbs)作为Microsoft Internet Information Server (IIS) Exception Monitor 和 User Mode Process Dump 的替代工具. 这两个是PSS经常使用的工具, 用来隔离引起进程停止响应或者异常退出的原因.

 

ADPlus有哪些功能?

===============

ADPlus 是基于控制台的 Microsoft Visual Basic 脚本。

它使 Microsoft CDB 调试程序自动生成包含来自一个或多个进程的调试输出的内存转储和日志文件。

每当 ADPlus 运行时,调试信息(包含调试信息的内存转储和文本文件)都将放置在本地文件系统或远程网络共享目录上的新的、唯一命名的文件夹(例如,C:\Temp \Crash_Mode__Date_01-22-2001__Time_09-41-08AM)中。

此外,ADPlus 创建的每一个文件均具有唯一的名称(例如,PID- 1708__Inetinfo.exe__Date_01-22-2001__Time_09-41-08AM.log),以避免较新的文件覆盖较旧的文件。

 

ADPlus 可用于任何用户模式进程或服务,例如 Internet 信息服务 (IIS)、Microsoft Transaction Server (MTS) 或 Microsoft COM+ 应用程序。

 

应该什么时候使用ADPlus?

================

ADPlus 用于向 Microsoft Product Support Services 支持专业人员提供调试信息,支持专业人员需要这些调试信息来确定在复杂环境中出现的问题的原因。

 

如果您遇到下列问题,应使用 ADPlus 捕获调试信息:

  • 进程停止响应。
  • 进程在单处理器计算机上占用 100% 的 CPU、在双处理器计算机上占用 50% 的 CPU、在四处理器计算机上占用 25% 的 CPU,依此类推。
  • 进程崩溃或意外关闭。

到哪里下载ADPlus?

================

ADPlus 随最新的 Microsoft Debugging Tools for Windows 一起提供。要获取最新的 Microsoft Debugging Tools for Windows,请访问下面的 Microsoft 网站:

http://www.microsoft.com/whdc/devtools/debugging/default.mspx

 

ADPlus是如何工作的?

================

ADPlus 具有两种操作模式:

  • “Hang”模式用于解决进程挂起、100% CPU 使用率以及不涉及崩溃的其他问题。当您在挂起模式下使用 ADPlus 时,在运行脚本之前必须等待,直到进程停止响应(与崩溃模式不同,挂起模式不是持久性的)。
  • “Crash”模式用于解决 Dr. Watson 错误导致的崩溃问题,或者用于解决导致程序或服务意外退出的任何其他类型的错误。当您在崩溃模式下使用 ADPlus 时,必须在发生崩溃启动 ADPlus。可以通过“-notify”开关对 ADPlus 进行配置,以便通知管理员或计算机发生了崩溃。

Hang模式

在此模式下,ADPlus 在脚本结束运行后立即为在命令行中指定的所有进程生成完全内存转储。创建的每一个 .dmp 文件都会放在包含运行 ADPlus 时的日期/时间戳的文件夹中。每一个文件名都包含进程名、进程 ID,以及运行 ADPlus 时的日期/时间戳。在进程内存正被转储到某一文件时,该进程被冻结。在创建完内存转储文件之后,通过将非侵害性连接/分离与 CDB 调试程序一起使用来恢复进程。

 

Crash模式

在此模式下,ADPlus 将 CDB 调试程序连接到在命令行中指定的所有进程。ADPlus 自动配置调试程序以监视下列类型的异常:

  • 无效句柄
  • 非法指令
  • 整数被零除
  • 浮点数被零除
  • 整数溢出
  • 无效的锁定顺序
  • 访问冲突
  • 堆栈溢出
  • C++ EH 异常
  • 未知异常

ADPlus的命令行开关选项

===============

要使用 ADPlus,您必须为脚本指定一系列命令行开关或参数。ADPlus 至少需要两个开关:

  • 一个开关指定操作模式
  • 另一个开关指定要对其执行操作的目标进程。

下面列出了最常用的开关。您还可以通过运行“ADPlus –help”,或通过阅读调试程序帮助文件 (Debugger.chm) 来查看完整的开关列表。

  • -hang
    此开关将 ADPlus 配置为在挂起模式下运行。您必须将此开关与“-iis”、“-pn”或“-p”开关一起使用。不能将“-hang”与“-crash”开关一起使用。
    注意:当 ADPlus 在挂起模式下运行时,您必须在进程停止响应或占用很高的 CPU 使用率之后启动 ADPlus。
  • -crash
    此开关将 ADPlus 配置为在崩溃模式下运行。您必须将此开关与“-iis”、“-pn”或“-p”开关一起使用。不能将“-crash”与“-hang”开关一起使用。
    注意:当 ADPlus 在崩溃模式下运行时,您必须在进程意外退出或者变得不稳定之前启动 ADPlus。
  • -pn process name
    “-pn”开关用于指定您希望 ADPlus 分析的进程名。要指定多个进程,请使用多个“-pn process name”开关。例如:

    -pn process1.exe -pn process2.exe

  • -p process ID
    “-p”开关用于指定您希望 ADPlus 分析的进程的 ID (PID)。要指定多个进程,请使用多个“-p PID”开关。例如:

    -p 1896 -p 1702

  • -iis
    “-iis” 开关用于调试运行 Internet Information Server (IIS) 4.0 或更高版本的服务器计算机。在将 ADPlus 与“-iis”开关一起使用时,ADPlus 监视所有 IIS 进程内 (Inetinfo.exe) 和进程外 (Mtx.exe/Dllhost.exe) 应用程序。“-iis”开关可以与“-pn”开关或“-p”开关一起使用,也可以单独使用,以分析 IIS 和所有在崩溃模式或挂起模式下运行的 MTS/COM+ 应用程序。
    如果您尝试分析运行 IIS 3.0 或更低版本的服务器计算机,请使用“-pn”开关并将 Inetinfo.exe 指定为要监视的进程。
  • -notify computer nameuser name
    只有当 ADPlus 在崩溃模式下运行时此开关才有效。此开关指示 ADPlus 提醒指定的用户名或计算机名发生了崩溃。当调试程序由于第二次偶然发生的异常从进程分离时,或者当用户按 Ctrl+C 停止调试时,就会通过本地信使服务向远程用户或计算机发送通知。只有正在被调试的计算机上启动了本地信使服务时,才会发送此通知。
  • -quiet
    此开关指示 ADPlus 不显示所有模式对话框。如果您正在从远程命令外壳程序(其中模式对话框可能导致 ADPlus 无限期地等待用户单击“确定”)内运行 ADPlus,则此开关将非常有用。为保证获得最佳效果,请确保这是传递给 ADPlus.vbs 的第一个开关。
  • -o output directory
    此开关指示 ADPlus 在哪里放置调试输出文件。如果使用长文件名,则必须用双引号将它们括起来。此外,还可以使用 UNC 路径 (\\server\share)。如果使用 UNC 路径,则 ADPlus 在紧跟您指定的 UNC 路径的下方创建一个新文件夹。根据正在运行 ADPlus 的服务器命名该文件夹(例如,\\server\share\Web1 或 \\server\share\Web2)。如果 ADPlus 在 Web 场中的多台计算机上(这些计算机全都将其输出放置于同一网络共享上)运行,则此开关十分有用。

使用ADPlus的命令举例

====================

为Hang住的情况抓Dump:

adplus -hang -pn w3wp.exe   为所有的w3wp.exe进程创建内存转储文件.

adplus -hang -p 1244            为进程ID为1244的进程创建内存转储文件.  

adplus -hang –IIS                  为所有跟IIS相关的进程(比如w3wp.exe, inetinfo.exe, aspnet_wp.exe 和dllhost.exe)创建内存转储文件.

 

为Crash的情况抓Dump:

adplus -crash -pn w3wp.exe   在崩溃的时候为所有的w3wp.exe进程创建内存转储文件

adplus -crash -p 1244     在进程ID为1244的进程崩溃的时候为它创建内存转储文件

adplus -crash –IIS           为所有跟IIS相关的进程(比如w3wp.exe, inetinfo.exe, aspnet_wp.exe 和dllhost.exe)创建崩溃时的内存转储文件.

 

摘译自:

How to use ADPlus to troubleshoot "hangs" and "crashes"

http://support.microsoft.com/?scid=kb%3Ben-us%3B286350&x=18&y=10


posted on 2011-10-25 16:48  Angelo Lee  阅读(292)  评论(0编辑  收藏  举报