Debugging Tools for Windows |
在用户模式和内核模式应用程序中有很多方法用于截获和处理异常。激活的调试器、即时调试器或内部的错误处理程序都是异常处理的通常方法。
关于这些错误处理方式优先等级的更多信息,查看启用即时调试。
当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 (完整的事件列表,查看下面的"事件定义和默认设置"节。)A
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) 命令启用子进程调试时,该事件才可控制。 进程名可以包含一个可选的文件扩展名和星号(*)或问号(?)作为通配符。调试器只保存最近一次的cpr设置。支持对不同进程进行分开设置。在cpr 和 Process 之间需要有一个冒号或者空格。 如果省略Process ,则该设置应用于所有创建出来的子进程。 |
Ignore |
epr[:Process] | 进程退出(Process exit) 当通过CDB的-o 命令行选项或 WinDbg.childdbg (Debug Child Processes) 命令启用子进程调试时,该事件才可控制。 进程名可以包含一个可选的文件扩展名和星号(*)或问号(?)作为通配符。调试器只保存最近一次的cepr 设置。支持对不同进程进行分开设置。在cepr 和 Process 之间需要有一个冒号或者空格。如果省略Process ,则该设置应用于所有子进程退出的事件。 |
Ignore |
ct | 线程创建(Thread creation) | Ignore |
et | 线程退出(Thread exit) | Ignore |
ld[:Module] | 加载模块(Load module) 如果指定了Module,则当名字为指定值的模块加载时发生中断。Module可以指定模块的名字或地址。如果指定名字,Module可以包含通配符和说明。(关于该语法的更多信息,查看 字符串通配符语法。) 调试器只会记录最近一次的ld设置。不支持对多个模块多次设置。在ld和Module之间需要加上一个冒号或者空格。 如果省略Module ,则任何模块加载都会触发事件。 |
Output |
ud[:Module] | 卸载模块(Unload module) 如果指定了Module,则当名字为指定值的模块加载时发生中断。Module可以指定模块的精确名字或地址。如果Module是精确名字,调试器会使用保存的模块列表和地址将它立即转换为地址来记录。如果Module包含通配符,则字符串模板会被保存下来在之后的卸载事件发生时用来匹配。 极少数情况下,调试器没有卸载事件的名字信息,只能通过地址来进行匹配。因此,如果Module 包含通配符,这种情况下调试器无法确定被卸载模块的名字,所以任何模块被卸载都会中断。 调试器只会记录最近一次的ud设置。不支持对多个模块多次设置。在ud和Module之间需要加上一个冒号或空格。 如果没有指定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" 关于修改该方式的更多信息,查看崩溃和重起目标机。 |