[Win32]一个调试器的实现(三)异常 – Zplutor

这一篇就距的成绩:怎样处置EXCEPTION_DEBUG_EVENT这种调试事变。这种调试事变是调试器与被调试指引航线举行互相的最次要普通的,使后退的文字中你会预告调试器怎样应用它达到断点、单步表现等管理。因而,就这种调试事变的处置很释放,调试器的作者可以动机需求举行差数的处置。不过,在对其举行处置先发制人必不得已认识某些就非常的知,这是本文的重力。(工具书软件调试)

非常花色品种

动机使相等可以回复表现时产生非常。,可分为三出色的型的非常,是毛病非常,捕获物非常和悬非常。

非常和非常毛病装捕获机普通可以使复职,你可以使复职手术后回复表现。二者都的差数之处依赖,毛病回复时,从得分式的弹性启动非常;装捕获机是鉴于非常的得分式的的下又得分式的表现。譬如,三以下阐明:

i1

i2

i3

i2形成物毛病非常,当回复i2开端表现;设想弹性装捕获机的异议,当回复i3开端表现。

终止非常是一个人庄重地的毛病,该顺序可以不再继续。

动机非常的动机,可分为武器装备和软件非常非常非常。武器装备非常CPU异议,Windows下面的指定的遗传密码下定义了一个人武器装备非常:

非常

代理

EXCEPTION_ACCESS_VIOLATION

0xC0000005

顺序尝试读写一个人不成作客的地址时异议。譬如,在触球读0的内存地址。

EXCEPTION_ARRAY_BOUNDS_EXCEEDED

0xC000008C

衣服作客污水时异议。

EXCEPTION_BREAKPOINT

0x80000003

弹性断点时异议。

EXCEPTION_DATATYPE_MISALIGNMENT

0x80000002

顺序读取一个人几乎不异已的的唱片时异议。

EXCEPTION_FLT_DENORMAL_OPERAND

0xC000008D

设想一个人浮点十进位的点运算的管理数是不定期地的,这种非常被突然拿出来。同样的人的非定期地,该值太小,可在一个人规范的体式表现。

EXCEPTION_FLT_DIVIDE_BY_ZERO

0xC000008E

浮点十进位的点除法是。0当非常。

EXCEPTION_FLT_INEXACT_RESULT

0xC000008F

浮点十进位的点数管理的结实不克不及迫使表现成十进位的当非常。

EXCEPTION_FLT_INVALID_OPERATION

0xC0000090

该非常表现阻拦在本表正中鹄的剩余把正式送入精神病院连贯。

EXCEPTION_FLT_OVERFLOW

0xC0000091

浮点十进位的点数的索引超越所能表现的最大犯得着非常。

EXCEPTION_FLT_STACK_CHECK

0xC0000092

举行浮点十进位的点数运算时栈产生过多或下溢当非常。

EXCEPTION_FLT_UNDERFLOW

0xC0000093

浮点十进位的点数的索引不足所能表现的最小犯得着非常。

EXCEPTION_ILLEGAL_INSTRUCTION

0xC000001D

顺序尝试表现一个人残废者的得分式的当非常。

EXCEPTION_IN_PAGE_ERROR

0xC0000006

顺序要作客的内存页不在场的身体反省内存中时异议。

EXCEPTION_INT_DIVIDE_BY_ZERO

0xC0000094

除数是整体的除法0当非常。

EXCEPTION_INT_OVERFLOW

0xC0000095

整体管理的结实过多当非常。

EXCEPTION_INVALID_DISPOSITION

0xC0000026

非常处置器汇成一个人残废者的处置的当非常。

EXCEPTION_NONCONTINUABLE_EXCEPTION

0xC0000025

一个人不克不及继续实行的异议,设想顺序继续表现,这将通向非常。

EXCEPTION_PRIV_INSTRUCTION

0xC0000096

顺序触球表现一个人电流CPU方式难承认的事的得分式的当非常。

EXCEPTION_SINGLE_STEP

0x80000004

符号留下印象的TF位为1时,每个得分式的将通向非常。次要用于单步调试。

EXCEPTION_STACK_OVERFLOW

0xC00000FD

栈过多当非常。

侮辱有很多异议指定的遗传密码,但某些不容易变得流行,但这些非常在应用最高级释放宣言计划。某些普通的的非常EXCEPTION_ACCESS_VIOLATIONEXCEPTION_INT_DIVIDE_BY_ZERO EXCEPTION_STACK_OVERFLOW

软件非常顺序行使RaiseException作用异议,C++throw期末考试宣称是行使作用突然拿出来非常。非常指定的遗传密码软件非常可以在呼叫RaiseException在稍微指定的的由顺序员。经过throw突然拿出来非常的指定的遗传密码结算单是由汇编者指定的,为Visual C++的汇编者来说,非常的指定的遗传密码无不0xE06D7363,对应“.msc”的ASCII码。

武器装备和软件非常非常都可以经过Windows构造化非常处置机制提出捕获物和处置,这种机制可以在非常的分离继续做指令表,或块的非常处置的表现。而C++仅仅的异议处置的捕获物和处置提出机制throw结算单突然拿出来的非常。,简略地说,这是经过反省使相等非常指定的遗传密码0xE06D7363来决议的。留存,C++仅仅的异议处置机制来应验非常处置块,但不克不及继续在非常的分离。竟C++非常处置是Windows构造化非常处置策划。

非常的配电

非常事变,将阅历一个人复杂的分派课程。普通来说,一个人异议是下面的结实:

1.非常处置,应用顺序毛病辞职顺序。

2.非常被调试器处置了,该顺序是在非常的分离继续(感兴趣毛病非常啊。

3.仅仅的异议是在顺序正中鹄的非常处置顺序处置,该顺序是在非常的分离继续,或继续表现非常处置块。

下面看一眼非常散布课程。为了突出重力,在这里省略了很多特效药:

1.该顺序是一个人异议,Windows捕获到这么非常,进入内核表现。

反省使相等有不定期地的顺序正调试,设想是,发送一个人EXCEPTION_DEBUG_EVENT调试事变给调试器,这是调试器高音部收到该事变;设想否,则跳到4步。

3.调试器收到非常调试事变继,设想行使ContinueDebugEvent当第三个参量为DBG_CONTINUE,即表现调试器已处置了该非常,该顺序是在非常的分离继续,端的非常散布;设想第三个参量是DBG_EXCEPTION_NOT_HANDLED,即表现调试器没处置该非常,跳到4步。

回到用户方式表现,找寻一个人非常处置顺序,能处置非常。。设想你见,在进入非常处置顺序的表现,这么继续表现此顺序动机E的结实,端的非常散布;设想没找到,则跳到5步。

回到内核表现,再次反省使相等有不定期地的顺序正调试,设想是,重行发送一个人EXCEPTION_DEBUG_EVENT调试事变给调试器,这是调试器秒次收到该事变;设想否,跳到7步。

6.调试器秒次处置该非常,设想行使ContinueDebugEvent当第三个参量为DBG_CONTINUE,该顺序是在非常的分离继续,端的非常散布;设想第三个参量是DBG_EXCEPTION_NOT_HANDLED,跳到7步。

7.没非常处置,顺序到应用顺序毛病的完毕。

在这么课程中表达的流程图:

下面用数个范例来变深对课程的变得流行。调试器应用的是上一篇文字的示例指定的遗传密码。设想你熟习非常散布的课程,你可以猛长这把正式送入精神病院。

弹性武器装备非常,收执非常事变后调试事变DBG_CONTINUE行使ContinueDebugEvent

经过调试顺序指定的遗传密码:

 1 #include <stdio.h>
 2 #include <Windows.h>
 3 
 4 int wmain() {
 5 
 6     OutputDebugString(TEXT(Warning!将突然拿出来一个人非常!));
 7 
 8     __try {
 9 
10         int a = 0;
11         int b = 10 / a;
12 
13     }
14     __except(EXCEPTION_EXECUTE_HANDLER) {
15 
16         OutputDebugString(TEXT(进入非常处置顺序。));
17     }
18 }

调试器的OnException作用指定的遗传密码:

 1 void OnException(const EXCEPTION_DEBUG_INFO* pInfo) {
 2 
 3     std::wcout << TEXT(产生了一个人异议。<< std::endl
 4                << TEXT(Exception code: <<std::六<<std::大写<< std::setw(8
 5                <<std::setfill(L0<< pInfo-> <<std::12<< std::endl;
 6 
 7     if (pInfo->dwFirstChance ==真的)
 8 
 9         std::wcout << TEXT(高音部的机遇。<< std::endl;
10     }
11     else {
12 
13         std::wcout << TEXT(秒次机遇。<< std::endl;
14     }
15 }

运转调试器顺序,你会预告它扩展一个人死迂回地,不息输入“An exception was occurred…”要旨,它一向First 机遇。”。组合艺术品工艺品流程图上述的看法:朕以DBG_CONTINUE调试课程中表现继续,意图朕处置非常,从非常的分离开端继续表现。鉴于EXCEPTION_INT_DIVIDE_BY_ZERO是一个人毛病,int b = 10 / a这么宣称将再次表现。但是竟调试器并没举行稍微处置非常的管理,这么结算单将通向非常。因而常常,进入了死迂回地。从这么范例中预告,使相等非常是由结算单通向的__try块白昼渐短,最先捕获物到非常的却是调试器。

弹性武器装备非常,收执非常事变后调试事变DBG_EXCEPTION_NOT_HANDLED行使ContinueDebugEvent

依然应用下面的范例指定的遗传密码,但它会ContinueDebugEvent第三参量替换DBG_EXCEPTION_NOT_HANDLED。运转调试器,输入独自地一次An exception was occurred…”要旨,输入要旨前面的调试课程。,这么非常处置顺序调试课程举行了。课程:朕以DBG_EXCEPTION_NOT_HANDLED继续调试顺序的表现,意图非常处置,因而Windows找寻一个人非常处置顺序。鉴于一个人非常处置顺序的在,它汇成EXCEPTION_EXECUTE_HANDLER,这么被调试成非常处置器表现。设想将EXCEPTION_EXECUTE_HANDLER改成EXCEPTION_CONTINUE_EXECUTION,这么调试课程非常结算单再次表现,结实也成为处于停顿环境环境。。

假设朕将__try__except块脱去,就不能胜任的有稍微的非常处置顺序来处置非常,调试器会秒次收到非常调试要旨。设想你还在DBG_EXCEPTION_NOT_HANDLED行使ContinueDebugEvent,调试课程中会辞职;设想DBG_CONTINUE举行行使,这么继续调试课程,其结实是一个人死迂回地。

外面的两个范例应用武器装备非常和Windows构造化非常处置。设想你正应用一个人软件非常C++的非常处置,这么产生了什么?为您处理以下成绩:

③经过调试顺序指定的遗传密码如次:

 1 #include <stdio.h>
 2 #include <Windows.h>
 3 
 4 int wmain() {
 5 
 6     OutputDebugString(TEXT(Warning!将突然拿出来一个人非常!));
 7 
 8     try {
 9 
10         throw 9;
11     }
12     catch(int前)
13 
14         OutputDebugString(TEXT(进入非常处置顺序。));
15     }
16 }

部分以DBG_CONTINUEDBG_EXCEPTION_NOT_HANDLED行使ContinueDebugEvent,小心的视察调试器的输入,解说一下为什么会因此。

下面的指定的遗传密码出来:

 1 #include <stdio.h>
 2 #include <Windows.h>
 3 
 4 int wmain() {
 5 
 6     OutputDebugString(TEXT(Warning!将突然拿出来一个人非常!));
 7 
 8     try {
 9 
10         throw 9;
11 
12         OutputDebugString(TEXT(将此音讯显示吗?));
13     }
14     catch(int前)
15 
16         OutputDebugString(TEXT(进入非常处置顺序。));
17     }
18 }

部分以DBG_CONTINUEDBG_EXCEPTION_NOT_HANDLED行使ContinueDebugEvent,小心的视察调试器的输入,解说一下为什么会因此。

答复上述的两个范例:软件非常是毛病非常完全势均力敌的的装捕获机?

再谈OutputDebugString

在上述的高音部、在秒个范例,You may notice a small problem:在高音部个人范例,使用调试课程OutputDebugString输入字母串只显示一次;但到处秒个范例却显示两倍。这是因OutputDebugString在内部行使RaiseException,它本质上是软件非常任务,Windows将它异议替换成了OUTPUT_DEBUG_STRING_EVENT调试事变来圆形的调试器。

因而,当朕DBG_CONTINUE行使ContinueDebugEvent时,OutputDebugString处置非常,调试器只收到一次OUTPUT_DEBUG_STRING_EVENT事变;以DBG_EXCEPTION_NOT_HANDLED行使时,该非常处置,调试器会秒次收到OUTPUT_DEBUG_STRING_EVENT。这执意为什么到处秒个范例这些要旨会输入两倍了。

这么,为什么在调试器秒次处置OUTPUT_DEBUG_STRING_EVENT后头,DBG_EXCEPTION_NOT_HANDLED行使ContinueDebugEvent时,调试情人将不能胜任的完毕,因它可是说是吗?OutputDebugString异议属于特别的非常,Windows它有一个人特别的处置。OutputDebugString的踢向是为了向调试器输入调试要旨,没流言蜚语毛病,设想调试课程中行使OutputDebugString在完毕后紧接地,这一定是无法解说的。。

EXCPETION_DEBUG_EVENT的处置

好了,它是那样地的多,终可以回到事情。EXCEPTION_DEBUG_INFO分钟代理这类事变调试要旨的构造。dwFirstChance这是高音部或秒次承兑势均力敌的的非常,为1是高音部,为0是秒次。ExceptionRecord这是一个人EXCEPTION_RECORD构造体,计入非常的分钟要旨:

ExceptionCode 非常指定的遗传密码

ExceptionFlags 非常符号,为0这是一个人继续非常,别的为EXCEPTION_NONCONTINUABLE

ExceptionRecord 得分另一个人告发非常。异议可以嵌套在另一个人非常,链构造的形成物。

ExceptionAddress 非常的得分式的的地址。

ExceptionInformation 设想非常应计入更多的要旨,应用衣服容纳此要旨。

NumberParameters ExceptionInformation衣服中元素的总计。。

从下面的代理可以看出,ContinueDebugEvent的第三个参量为调试器的行动有很大的挤入,因而朕不只可以用DBG_CONTINUE或许DBG_EXCEPTION_NOT_HANDLED,差数的管理应动机非常指定的遗传密码表现,这么,应用彻底地的值行使ContinueDebugEvent。譬如,遭遇战除零非常,朕可以改观非零遗传因子的值,这么DBG_CONTINUE继续调试顺序的表现。又如,朕怀孕不只是在特别使适应下,非常处置顺序,这么朕可以收到非常调试事变的高音部时间DBG_EXCEPTION_NOT_HANDLED继续表现,在秒收执时非常调试事变的处置。

期末考试的解说,为EXCEPTION_DEBUG_EVENTOUTPUT_DEBUG_STRING_EVENT调试事变外,DBG_CONTINUEDBG_EXCEPTION_NOT_HANDLED归结为是同样地的。,都是继续调试顺序的表现,挑剔这两个差数。

示例指定的遗传密码

此指定的遗传密码示例添加一个人全程变量g_continueStatus,在行使ContinueDebugEvent在应用它的第三个参量。OnExceptionOnOutputDebugString作用将更改此值。非常,收执高音部时间DBG_EXCEPTION_NOT_HANDLED调试课程中表现继续,二次收执DBG_CONTINUE继续表现。