可以通过各种方法捕获和处理用户模式和内核模式应用程序中的异常。 活动调试器、验尸调试器或内部错误处理例程都是处理异常的常见方法。
有关这些各种异常处理程序的优先顺序的详细信息,请参阅 “启用事后调试”。
当 Microsoft Windows 操作系统允许调试器处理异常时,生成异常的应用程序 进入 调试器。 也就是说,应用程序停止,调试器变为活动状态。 然后,调试器可以以某种方式处理异常或分析情况。 然后,调试器可以结束进程或使其继续运行。
如果调试器忽略异常并允许应用程序继续运行,作系统将查找其他异常处理程序,就好像没有调试器一样。 如果处理异常,应用程序将继续运行。 但是,如果异常保持不变,则调试器会获得第二次处理这种情况的机会。
使用调试器分析异常
当异常或事件进入调试器时,可以使用调试器检查正在执行的代码和应用程序使用的内存。 通过更改某些数量或跳转到应用程序中的不同点,可以删除异常的原因。
可以通过发出 gh(具有异常处理的 Go) 或 gn(不具有异常处理的 Go) 命令来恢复执行。
如果在调试器处理异常的第二次机会中发出 gn 命令,应用程序将结束。
Kernel-Mode 异常
内核模式代码中发生的异常比用户模式异常更为严重。 如果未处理内核模式异常,则会发出 bug 检查 ,并且系统停止。
与用户模式异常一样,如果内核模式调试器附加到系统,则会在 bug 检查屏幕 (也称为 蓝屏)出现之前通知调试器。 如果未附加调试器,将显示 bug 检查屏幕。 在这种情况下,操作系统可能会创建故障转储文件。
从调试器控制异常和事件
可以将调试器配置为以特定方式响应指定的异常和事件。
调试器可以为每个异常或事件设置中断状态:
事件发生后,该事件可能会立即中断调试器(“第一次机会”)。
在收到其他错误处理程序后,该事件可能会中断(“第二次机会”)。
该事件还可以发送调试器一条消息,但继续执行。
调试器可以忽略该事件。
调试器还可以为每个异常和事件设置处理状态。 调试器可以将事件视为已处理的异常或未经处理的异常。 (当然,不是实际错误的事件不需要任何处理。
可以通过以下操作之一控制中断状态和处理状态:
(CDB 和 NTSD)在命令行上使用 -x、-xe、-xd、-xn 或 -xi 选项。
(CDB、NTSD 和 KD)在 Tools.ini 文件中使用 sxe 或 sxd 关键字。
(仅限 WinDbg)选择“调试”菜单上的事件筛选器以打开“事件筛选器”对话框,然后选择所需的选项。
SX\* 命令、-x\* 命令行选项和 sx\* Tools.ini 关键字通常设置指定事件的中断状态。 可以添加 -h 选项,以便改为设置处理状态。
有四个特殊事件代码(cc、 hc、 bpec 和 ssec)始终指定处理状态,而不是中断状态。
可以使用 .lastevent (显示最后一个事件) 命令显示最新的异常或事件。
控制中断状态
设置异常或事件的中断状态时,可以使用以下选项。
| Command | 状态名称 | Description |
|---|---|---|
| SXE 或 -xe | 休息 (已启用) |
发生此异常时,目标会立即进入调试器。 在激活其他任何错误处理程序之前,此中断会发生。 此方法称为 首次机会处理。 |
| SXD 或 -xd | 第二次机会中断 (已禁用) |
调试器不会因这种第一次机会异常而中断(虽然会显示一条消息)。 如果其他错误处理程序无法处理此异常,则执行将停止,并且将进入调试器进行调试。 此方法称为 第二次机会处理。 |
| SXN 或 -xn | 输出 (通知) |
当此异常发生时,目标应用程序根本不会进入调试器。 但是,将显示一条消息,通知用户此异常。 |
| SXI 或 -xi | 忽视 |
发生此异常时,目标应用程序不会闯入调试器,并且不会显示任何消息。 |
如果 SX* 设置未预期的异常出现,则目标应用程序在第二次机会时进入调试器。 本主题的以下“事件定义和默认值”部分列出了事件的默认状态。
若要使用 WinDbg 图形界面设置中断状态,“调试”菜单上的事件筛选器从“事件筛选器”对话框中的列表中选择所需的事件,然后选择“已启用”、“已禁用”、“输出”或“忽略”。
控制处理状态
所有事件都被视为未经处理,除非使用 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 选项一起使用时,将执行以下作。
| Command | 状态名称 | Description |
|---|---|---|
SXE |
处理 |
在执行恢复时,该事件被认为已经处理完毕。 |
SXD、SXN、SXI |
未处理 |
在执行恢复时,该事件被视为未处理。 |
若要使用 WinDbg 图形界面设置处理状态,请在“调试”菜单上选择“事件筛选器”,从“事件筛选器”对话框中的列表中选择所需的事件,然后选择“已处理”或“未处理”。
自动命令
借助调试器,还可以设置在事件或异常导致中断调试器时自动执行的命令。 可以为首次机会中断设置命令字符串,并为第二次机会中断设置命令字符串。 可以使用 SX\* 命令或 调试 | 事件筛选器 命令来设置这些字符串。 每个命令字符串可以包含多个用分号分隔的命令。
无论中断状态如何,都会执行这些命令。 也就是说,如果中断状态为“忽略”,则仍执行命令。 如果中断状态为“第二次机会中断”,则在异常首次发生时执行首次机会命令,然后再涉及任何其他异常处理程序。 命令字符串可以以执行命令(例如 g(Go)、 gh(Go with Exception Handled)或 gn(Go with Exception Not Handled))结尾。
事件定义和默认值
可以更改以下异常的中断状态或处理状态。 指示其默认中断状态。
以下异常的默认处理状态始终为“未处理”。 请小心更改此状态。 如果将此状态更改为“Handled”,则此类型的所有第一次机会和第二次异常都被视为已处理,并且此配置将绕过所有异常处理例程。
| 事件代码 | Meaning | 默认中断状态 |
|---|---|---|
asrt |
断言失败 |
破 |
av |
访问权限错误 |
破 |
分米 |
数据未对齐 |
破 |
dz |
整数除以零 |
破 |
c000008e |
浮点除以零 |
破 |
啊 |
C++ EH 异常 |
重新开始的机会 |
gp |
防护页违规 |
破 |
ii |
非法指令 |
第二次机会休息 |
iov |
整数溢出 |
破 |
ip |
页内 I/O 错误 |
破 |
isc |
系统调用无效 |
破 |
lsq |
锁序列无效 |
破 |
sbo |
堆栈缓冲区溢出 |
破 |
苏联 |
堆栈溢出 |
破 |
wkd |
唤醒调试器 |
破 |
aph |
应用程序挂起 如果 Windows 操作系统判断进程已停止响应(即“挂起”),则会触发此异常。 |
破 |
3c |
子应用程序终止 |
第二次机会休息 |
ch |
无效句柄 |
破 |
数字 |
任何编号异常 |
第二次机会休息 |
注意可以使用 ah(断言处理)命令替代特定地址的 asrt 中断状态。 ch 和 hc 事件代码引用相同的异常。 控制其中断状态时,请使用 sx* ch。 控制其处理状态时,请使用 sx* hc。
可以更改以下异常的中断状态或处理状态。 指示其默认中断状态。
以下异常的默认处理状态始终为“Handled”。 由于这些异常用于与调试器通信,因此通常不应将其状态更改为“未处理”。 此状态会导致其他异常处理程序在调试器忽略异常时捕获异常。
应用程序可以使用 DBG_COMMAND_EXCEPTION (dbce) 与调试器通信。 此异常类似于断点,但当发生此异常时,可以使用 SX* 命令以特定方式做出反应。
| 事件代码 | Meaning | 默认中断状态 |
|---|---|---|
dbce |
特殊调试器命令异常 |
忽略 |
vcpp |
特殊的Visual C++异常 |
忽略 |
wos |
WOW64 单步异常 |
破 |
wob |
WOW64 断点异常 - |
破 |
sse |
单步异常 |
破 |
bpe |
断点异常 |
破 |
cce |
Ctrl+C 或 Ctrl+BREAK 如果目标是控制台应用程序,并且将 CTRL+C 或 CTRL+BREAK 传递给它,则会触发此异常。 |
破 |
注意 上表中的最后三个例外有两个不同的事件代码。 控制中断状态时,请使用 sse、 bpe 和 cce。 控制其处理状态时,请使用 ssec、 bpec 和 cc。
调试托管代码时,以下异常非常有用。
| 事件代码 | Meaning | 默认状态 |
|---|---|---|
clr |
公共语言运行时异常 |
第二次机会休息 未处理 |
clrn |
公共语言运行时通知异常 |
第二次机会休息 已处理 |
您可以更改以下事件的中断状态。 由于这些事件不是例外,因此其处理状态无关紧要。
| 事件代码 | Meaning | 默认中断状态 |
|---|---|---|
ser |
系统错误 |
忽略 |
cpr[:进程] |
进程创建 设置此事件的中断状态仅适用于用户模式调试。 此事件不会在内核模式下发生。 仅当已通过 -o命令行选项 或通过 .childdbg(调试子进程) 命令激活 CDB 或 WinDbg 中的子进程的调试时,才能控制此事件。 进程名称可以包括可选的文件扩展名和星号()或问号(?)作为通配符。 调试器仅记住最新的 cpr 设置。 不支持单独进程的单独设置。 在 cpr 和 Process 之间包括冒号或空格。 如果省略 Process ,则此设置适用于任何子进程创建。 |
忽略 |
epr[:进程] |
进程退出 设置此事件的中断状态仅适用于用户模式调试。 此事件不会在内核模式下发生。 仅当已通过 -o命令行选项 或通过 .childdbg(调试子进程) 命令激活 CDB 或 WinDbg 中的子进程的调试时,才能控制此事件。 进程名称可以包括可选的文件扩展名和星号()或问号(?)作为通配符。 调试器仅记住最新的 epr 设置。 不支持单独进程的单独设置。 在 epr 和 Process 之间包括冒号或空格。 如果省略 Process ,该设置将应用于任何子进程退出。 |
忽略 |
ct |
线程创建 |
忽略 |
et |
线程退出 |
忽略 |
ld[:Module] |
加载模块 如果指定 模块,则加载具有此名称的模块时,将发生中断。 模块 可以指定模块的名称或地址。 如果使用名称,Module 可能包含各种通配符字符和说明符。 (有关语法的详细信息,请参阅 字符串通配符语法。 调试器只记得最近的 ld 设置。 不支持单独模块的单独设置。 在 ld 和 Module 之间包括冒号或空格。 如果省略 Module ,则会在加载任何模块时触发该事件。 |
输出 |
ud[:Module] |
卸载模块 如果指定 Module,则在卸载具有此名称或此基址的模块时,将发生中断。 模块 可以指定模块的名称或地址。 如果使用名称,Module可以是确切的名称,也可以包含通配符。 如果 Module 是确切名称,则使用当前调试器模块列表立即解析为基址,并将其存储为地址。 如果 Module 包含通配符,则在发生卸载事件时,模式字符串将保留供以后匹配。 极少数情况下,调试器没有卸载事件的名称信息,并且仅按基址匹配。 因此,如果 Module 包含通配符,调试器将无法在此特定的卸载事例中执行名称匹配,并在卸载任何模块时中断。 调试器仅记住最新的 ud 设置。 不支持单独模块的单独设置。 在 ud 和 Module 之间包括冒号或空格。 如果省略 Module ,则会在加载任何模块时触发该事件。 |
输出 |
out[:输出] |
目标应用程序输出 如果指定 输出,则仅当收到与指定模式匹配的输出时,才会发生中断。 输出 可以包含各种通配符和说明符。 (有关语法的详细信息,请参阅 字符串通配符语法。但是, 输出 不能包含冒号或空格。 匹配不区分大小写。 在 输出 和 输出之间包括冒号或空格。 |
忽略 |
ibp |
初始断点 (此事件发生在调试会话的开头,并在重新启动目标计算机后发生。 |
在用户模式下: 中断。 可以使用 -g命令行选项将此状态更改为“忽略”。 在内核模式下: 忽视。 可以通过各种方法将此状态更改为“已启用”。 有关如何更改此状态的详细信息,请参阅 崩溃并重新启动目标计算机。 |
iml |
初始模块加载 (仅限内核模式) |
忽略。 可以通过各种方法将此状态更改为“中断”。 有关如何更改此状态的详细信息,请参阅 崩溃并重新启动目标计算机。 |