cc682/NetRoc
http://netroc682.spaces.live.com/
配置调试器
本节包含以下内容:
使用日志文件
指定模块和函数所有者
控制异常和事件
设置路径和加载文件
使用日志文件
调试器可以为调试会话记录日志文件。这些日志包括调试器命令窗口中所有内容,包含输入的命令和调试器的输出。
在WinDbg中,一些菜单和工具栏操作会在调试器命令窗口显示一些信息。日志文件不会记录这些文本。
打开新的日志文件
用下面的方法之一来打开新的日志文件或覆盖掉旧的日志文件:
添加到已存在的日志文件中
使用下面的方法之一来将命令窗口文本添加到日志文件末尾:
关闭日志文件
使用下面方法之一来关闭打开的日志文件:
检查日志文件状态
用下面的方法之一检查日志文件状态:
指定模块和函数所有者
!analyze 和!owner 扩展命令使用名为Triage.ini 的文件来决定调试器遇到的符号的所有者。
使用这些扩展命令时,函数或模块的标识会显示在"Followup"之后。
Triage.ini 是在Windows调试工具包安装目录下的\triage 子目录中的一个文本文件。Windows调试工具包中有一个Triage.ini 文件的示例。
警告 如果在已有的Windows调试工具包目录下升级新版本,会将目录下的所有文件覆盖掉,包括Triage.ini 。修改或替换掉Triage.ini 文件之后,最好将它保存一份拷贝到其他目录中。重新安装调试器后,就可以使用备份的Triage.ini 来覆盖默认的。
Triage.ini 文件的格式
虽然Triage.ini文件用于帮助确定中断到调试器中的函数的所有者,文件中的 "owner" 字符串却可以是对调试有益的任何值。该字符串可以是编写或维护代码的人的名字,或者当某个模块或函数出错时的简短的操作流程说明。
文件中每一行都具有下面的语法。
Module[!Function]=Owner
可以在模块或函数名的末尾添加一个星号(*)作为通配符。
所有者字符串中不能加入空格。If strings do exist in the owner string, they are ignored。(不明白……)
关于该语法的更多信息,查看 特殊Triage.ini语法。
下面是一个triage.ini文件示例。
module1=Person1
module2!functionA=Person2
module2!functionB=Person3
module2!funct*=Person4
module2!*=Person5
module3!singleFunction=Person6
mod*!functionC=Person7
Triage.ini 和!owner
将模块或函数名传递给!owner 扩展命令时,调试器在"Followup"后显示模块或函数的所有者。
下例使用了上面例子中的Triage.ini 文件。
0:000> !owner module2!functionB
Followup: Person3
根据这个文件,"Person3"拥有module2!functionB,"Person4"拥有module2!funct*。这两个字符串都能和传递给!owner 的参数匹配,所以使用了更完整的匹配。
Triage.ini 和!analyze
使用!analyze 扩展命令时,调试器在堆栈中查找顶层的异常帧(top faulting frame),并尝试确定该帧的模块和函数的所有者。如果找到,则会显示所有者信息。
如果调试器没有找到所有者,则会继续根据下面的堆栈帧来查找,直到调试器找到所有者或所有的堆栈都被检查过了。
如果找到了所有者,则在"Followup"后显示他的名字。如果调试器搜索了整个堆栈也没有找到任何信息,则不会显示名字。
下面的示例使用了上面例子中的Triage.ini 。
假设第一个堆栈帧是MyModule!someFunction 。调试器在Triage.ini 文件中没有找到MyModule 。然后,它会继续查找堆栈中的第二帧。
假设第二帧是module3!anotherFunction。调试器找到了一条关于module3的信息,但是该模块中没有和anotherFunction 匹配的信息。所以调试器继续查找第三帧。
假设第三帧是module2!functionC 。调试器首先查找能精确匹配的信息但是未找到。所以会截掉函数名并在Triage.ini 中找到了module2!funct*。这个匹配结束搜索,因为调试器确认了所有者是"Person4"。
调试器会显示类似下面的输出。
0:000> !analyze
*******************************************************************************
* *
* Exception Analysis *
* *
*******************************************************************************
Use !analyze -v to get detailed debugging information.
Probably caused by : module2 ( module2!functionC+15a )
Followup: Person4
---------
更加完整的匹配比短的匹配优先级更高。但是,相对于函数名,模块名称的匹配总是首选。如果Triage.ini 文件中没有module2!funct*,调试器会选择module2!* 来匹配。如果module2!funct* 和module2!* 都没有,则会选择mod*!functionC 匹配。
如果省略掉感叹号和函数名,并在模块名后加上!*,则表示指定模块中所有函数。如果该模块中的某个函数也被单独指明了,则更精确的那个具有优先权。
如果使用"default"作为模块名或函数名,它等同于通配符。例如,nt!*和nt!default 相同,default 和*!*相同。
如果找到一个匹配,但是等号右边是ignore ,则调试器使用堆栈中的下一帧来继续查找。
可以在所有者名字前加上last_ 或maybe_ 。该前缀在运行!analyze 时给予所有者更少的优先权。调试器会在使用maybe_ 匹配之前先用其他定义来匹配。调试器同样会先选择maybe_的匹配,然后再选择last_ 匹配。
示例 Triage.ini
Windows调试工具包中包含一个Triage.ini的示例。可以在该文件中增加需要的模块和函数所有者。如果不想要全局性的预设值,将该文件开头的default=MachineOwner 行删除。
控制异常和事件
在用户模式和内核模式应用程序中有很多方法用于截获和处理异常。激活的调试器、即时调试器或内部的错误处理程序都是异常处理的通常方法。
关于这些错误处理方式优先等级的更多信息,查看启用即时调试。
当Microsoft Windows操作系统允许由调试器来处理异常时,产生异常的程序会中断到调试器。即应用程序停止运行而调试器被激活。之后,调试器可以用各种方式处理掉异常或者分析情况。最后,调试器可以结束进程或恢复它的执行。
如果调试器跳过异常并继续程序执行,操作系统如同没有附加调试器一样会查找其他异常处理器。如果异常被处理掉,则程序继续运行。但是,如果异常仍然没有处理,系统会给予调试器第二次处理机会。
使用调试器分析异常
当异常或事件中断到调试器时,可以用调试器检查被执行的代码或者查看进程内存。通过修改某些值或者跳转到程序的另一个位置,可能可以解决掉这个异常。
使用gh (Go with Exception Handled) 或 gn (Go with Exception Not Handled) 命令恢复程序执行。
如果在调试器的第二次异常处理机会时使用gn 命令,则程序被终止。
内核模式异常
内核模式代码产生的异常比用户模式异常要更加严重。如果内核模式异常没有被处理,则会产生bug check并且系统停止。
和用户模式异常一样,如果有内核调试器附加到系统上,在错误检查屏幕(即蓝屏)产生前,会先通知调试器。如果没有附加调试器,则直接蓝屏。这种情况下,系统可能会创建崩溃转储文件。
从调试器控制异常和事件
可以设置调试器处理异常和事件的方式。
调试器可以为每个异常或事件设置中断方式:
- 事件发生时直接中断到调试器("第一次处理机会")。
- 在其他错误处理器已经接收到处理机会之后。("第二次处理机会")。
- 在发生事件时发送一条信息到调试器,但是继续执行。
- 调试器跳过事件。
调试器也可以为每个异常和事件设置处理方式。调试器可以将事件当作已处理异常或未处理异常来对待。(当然,并不是实际出错的事件是不需要处理的。)
可以用如下方法来控制中断方式和处理方式:
SX*命令、-x* 命令行选项和sx* Tools.ini 关键字用于设置特定事件的中断方式。添加-h 选项来设置事件的处理方式。
有4个特殊的事件代码(cc, hc, bpec, 和ssec)只能指定处理方式。
使用.lastevent (Display Last Event) 命令显示最近一次异常或事件。
控制中断方式
为异常或事件设置中断方式可以使用以下选项。
命令
|
条件名
|
说明
|
SXE
或
-xe
|
Break
(Enabled)
|
当异常发生时,目标立即中断到调试器。该中断在其他任何错误处理器被调用之前。这种方法称为第一次异常处理。
|
SXD 或 -xd
|
Second chance break
(Disabled)
|
调试器不会在第一次异常处理机会时中断(虽然会显示一条信息)。如果其他错误处理其不能处理异常,则目标停止执行并中断到调试器。这种方法称为第二次异常处理。
|
SXN 或 -xn
|
Output
(Notify)
|
当异常产生时,目标应用程序不会中断到调试器。但是,调试器中会显示一条有关的信息。
|
SXI 或 -xi
|
Ignore
|
异常发生时,目标程序不会中断带调试器,并且不会显示信息。
|
如果某个异常预先没有使用SX* 设置过,则目标进程在第二次机会时中断到调试器。所有事件的默认方式在下面的"事件定义和默认设置"主题中列出。
使用WinDbg图形界面设置中断方式,可以在Debug菜单打开Event Filters ,并在Event Filters 对话框中点击要设置的事件,并选择Enabled、Disabled、Output或Ignore。
控制处理方式
除非使用gh (Go with Exception Handled) 命令,否则所有事件都不会被处理。
所有异常都不会被处理,除非使用了sx* 命令和-h选项。
另外,SX* 选项可以配置非法句柄、STATUS_BREAKPOINT 中断指令和单步异常的处理方式。 (这个配置和他们的中断配置是分开的。)配置中断方式时,这些事件分别名为 ch、bpe和sse。在配置异常处理方式时,他们分别名为hc、bpec和ssec (完整的事件列表,查看下面的"事件定义和默认设置"节。)
CTRL+C 事件(cc)可以配置处理方式,但是没有中断方式。如果程序接收到了CTRL+C事件,总是会中断到调试器。
当为cc、 hc、 bpec和 ssec 事件使用SX*命令,或对某个异常将SX* 和-h 选项一起使用时,会遇到下面一些情况。
命令
|
方式
|
说明
|
SXE
|
Handled
|
重新开始执行时事件已被处理。
|
SXD, SXN, SXI
|
Not Handled
|
重新开始执行时事件未被处理。
|
使用WinDbg图形界面设置中断方式,可以在Debug菜单打开Event Filters ,并在Event Filters 对话框中点击要设置的事件,并选择Handled 或Not Handled。
自动命令
调试器允许设置一些命令用于当事件或异常中断到调试器时自动执行。可以分别为第一次异常处理和第二次异常处理设置一个命令字符串。使用SX* 命令或Debug | Event Filters 菜单命令设置。每个命令字符串可以包含用分号隔开的数条命令。
不管中断方式如何,这些命令都会被执行。换句话说,即使中断方式为"Ignore",命令仍然会被执行。如果中断方式为"第二次处理机会",则第一次处理机会的命令在异常第一次发生时,调用任何其他处理程序前被执行。命令字符串可以以运行命令结尾,如g (Go), gh (Go with Exception Handled)或gn (Go with Exception Not Handled)。
事件定义和默认设置
可以修改以下这些异常的中断方式和处理方式。下表同时指明了他们的默认中断方式。
以下异常的默认处理方式都是"Not Handled"。修改这些方式时要特别小心。如果将方式修改为"Handled",则所有第一次异常和第二次异常都被认为是已处理,原有的所有异常处理函数都会被跳过。
事件代码
|
含义
|
默认中断方式
|
asrt
|
断言错误(Assertion failure)
|
中断(Break)
|
av
|
访问违例(Access violation)
|
Break
|
dm
|
数据未对齐(Data misaligned)
|
Break
|
dz
|
除零(Divide by zero)
|
Break
|
eh
|
C++ EH异常(C++ EH exception)
|
Second-chance break
|
gp
|
页保护违例(Guard page violation)
|
Break
|
ii
|
非法指令(Illegal instruction)
|
Second-chance break
|
iov
|
整数溢出(Integer overflow)
|
Break
|
ip
|
页面I/O错误(In-page I/O error)
|
Break
|
isc
|
非法系统调用(Invalid system call)
|
Break
|
lsq
|
非法加锁次序(Invalid lock sequence)
|
Break
|
sbo
|
栈缓冲区溢出(Stack buffer overflow)
|
Break
|
sov
|
栈溢出(Stack overflow)
|
Break
|
wkd
|
唤醒调试器(Wake debugger)
|
Break
|
aph
|
应用程序挂起(Application hang)
这个异常在Windows操作系统结束停止相应的进程时触发(即挂起)。
|
Break
|
3c
|
子程序终止(Child application termination)
|
Second-chance break
|
ch hc
|
非法句柄(Invalid handle)
|
Break
|
Number
|
所有编号的异常(Any numbered exception)
|
Second-chance break
|
注意 可以使用ah (Assertion Handling)命令覆盖指定地址的asrt 中断方式。ch 和hc 事件是同一个异常。控制中断方式时,使用sx* ch;控制异常处理方式时,使用sx* hc。
可以修改以下这些异常的中断方式和处理方式。下表同时指明了他们的默认中断方式。
以下异常的默认处理方式都是"Handled"。由于这些异常是用来和调试器通信的,所以一般不能把它们设置为"Not Handled",否则调试器会跳过这些异常并由其他异常处理器来处理。
应用程序可以使用DBG_COMMAND_EXCEPTION (dbce) 来和调试器通信。这个异常类似断点,但是可以使用SX*命令来指定该异常发生时的对待方式。
事件代码
|
含义
|
默认中断方式
|
dbce
|
专用调试器命令异常(Special debugger command exception)
|
跳过(Ignore)
|
vcpp
|
专用Virtual C++异常(Special Visual C++ exception)
|
Ignore
|
wos
|
WOW64单步异常(WOW64 single-step exception)
|
Break
|
wob
|
WOW64断点异常(WOW64 breakpoint exception)
|
Break
|
sse ssec
|
单步异常(Single-step exception)
|
Break
|
bpe bpec
|
断点异常(Breakpoint exception)
|
Break
|
cce cc
|
CTRL+C 或CTRL+BREAK
当目标程序是控制台程序并输入了CTRL+C或CTRL+BREAK。
|
Break
|
注意 上表中最后三个异常有两个不同的事件代码。控制中断方式时,使用 sse, bpe, 和cce。控制异常处理方式时,使用ssec, bpec和 cc。
可以修改下面这些事件的中断方式。由于他们不是异常,所以和异常处理方式无关。
事件代码
|
含义
|
默认中断方式
|
ser
|
系统错误(System error)
|
Ignore
|
cpr[:Process]
|
创建进程(Process creation)
当通过CDB的-o 命令行选项或 WinDbg .childdbg (Debug Child Processes) 命令启用子进程调试时,该事件才可控制。进程名可以包含任意扩展名和星号(*)、问号(?)通配符。
|
Ignore
|
epr[:Process]
|
进程退出(Process exit)
当通过CDB的-o 命令行选项或 WinDbg .childdbg (Debug Child Processes) 命令启用子进程调试时,该事件才可控制。进程名可以包含任意扩展名和星号(*)、问号(?)通配符。
|
Ignore
|
ct
|
线程创建(Thread creation)
|
Ignore
|
et
|
线程退出(Thread exit)
|
Ignore
|
ld[:Module]
|
加载模块(Load module)
如果指定了Module,则当名字为指定值的模块加载时发生中断。如果没有指定Module,任何模块加载时都会中断。调试器只会记录最近一次的ld设置。不支持对多个模块多次设置。Module可以指定模块的名字或地址。如果指定名字,Module可以包含通配符和说明。(关于该语法的更多信息,查看字符串通配符语法。) 在ld和Module之间需要加上一个冒号或者空格。
|
输出(Output)
|
ud[:Module]
|
卸载模块(Unload module)
如果指定了Module,则当名字为指定值的模块加载时发生中断。如果没有指定Module,任何模块加载时都会中断。调试器只会记录最近一次的ud设置。不支持对多个模块多次设置。Module可以指定模块的精确名字或地址。如果Module是精确名字,调试器会使用保存的模块列表和地址将它立即转换为地址来记录。如果Module包含通配符,则字符串模板会被保存下来在之后的卸载事件发生时用来匹配。
极少数情况下,调试器在卸载事件发生时有地址匹配的模块,但是没有它的模块名信息。因此,如果Module 包含通配符,这种情况下调试器无法确定被卸载模块的名字,所以任何模块被卸载都会中断。在ud和Module之间需要加上一个冒号或空格。
|
Output
|
out[:Output]
|
目标程序输出(Target application output)
如果指定了Output,仅当接收到和模板字符串匹配的输出时才中断。Output 可以包含数个通配符和说明。 (关于该语法的更多信息,查看字符串通配符语法。) 但是,Output中不能包含冒号或者空格。匹配不是大小写敏感的。在out和Output之间应该加上一个冒号或者空格。
|
Ignore
|
ibp
|
初始断点(Initial break point)
(该事件在开始调试会话和重起目标机时发生。)
|
用户模式:Break。可以使用-g 命令行选项将这个方式修改为"Ignore"
内核模式:Ignore 。可以通过几种方法设置为"Enabled" 关于修改该方式的更多信息,查看崩溃和重起目标机。
|
iml
|
初始模块加载(Initial module load)
(仅内核模式)
|
Ignore。可以通过几种方法设置为"Break" 关于修改该方式的更多信息,查看崩溃和重起目标机。
|