王朝网络
分享
 
 
 

VC下发布的Release版程序的异常捕捉

王朝vc·作者佚名  2006-01-31
宽屏版  字体: |||超大  

寻找Release版程发生异常退出的地方比Debug版麻烦得多。发生异常的时候windows通常会弹出一个错误对话框,点击详细信息,我们能获得出错的地址和大概的出错信息,然后可以用以下办法分析我们的程序。

一. 用MAP文件定位异常代码位置。

1. 如何生成map文件

打开“Project → Project Settings”,选择 C/C++ 选项卡,在“Debug Info”栏选择“Line Numbers Only”(或者在最下面的 Project Options 里面输入:/Zd),然后要选择 Link 选项卡,选中“Generate mapfile”复选框,并再次编辑 Project Options,输入:/mapinfo:lines,以便在 MAP 文件中加入行信息。然后编译工程则可以在输出目录得到同名的.map文件。

2. 使用map文件定位发生异常的代码行

编译得到的map文件可以用文本方式打开,大致是这样的格式:(括号内是PomeloWu填加的注释)

0729 (←工程名)

Timestamp is 42e9bc51 (Fri Jul 29 14:19:13 2005) (←时间戳)

Preferred load address is 00400000 (←基址)

……(Data段描述,省略)

Address Publics by Value Rva+Base Lib:Object

0001:00000000 ?_GetBaseMessageMap@C0729App@@KGPBUAFX_MSGMAP@@XZ 00401000 f 0729.obj

……(↑这一行开始是函数信息,下面省略)

Line numbers for .\Release\ShowDlg.obj(C:\0729\ShowDlg.cpp) segment .text

24 0001:00003f90 28 0001:00003fce 29 0001:00003fd1 30 0001:00003fd4

……(行号信息,前面的数字是行号,后一个数字是偏移量,下面省略)

在获得程序异常的地址以后,首先通过函数信息部分定位出错的OBJ和函数。做法是用获得的异常地址与Rva+Base栏地址进行比较(Rva,偏移地址;Base,基址)。找到最后一个比获得的异常地址小的那个函数,那就是出错的函数。

之后,用获得的异常地址减去该函数的Rva+Base,就得到了异常行代码相对于函数起始地址的偏移。在“Line number for”部分找到相对应的模块,并把其后的行号信息与上面减得的偏移量对比,找到最接近的一个,前面的行号大致就是目标行了。

二. 获得错误的详细信息。

实际上,光靠Windows的错误消息对话框提供的信息量是很有限的,用自己写的exception filter可以获得更多的错误信息。用SetUnhandledExceptionFilter设定自定义错误处理回调函数替换Win32默认的top-level exception filter:

² SetUnhandledExceptionFilter的函数原型:

LPTOP_LEVEL_EXCEPTION_FILTER SetUnhandledExceptionFilter(

LPTOP_LEVEL_EXCEPTION_FILTER lpTopLevelExceptionFilter

// exception filter function

);

² SetUnhandledExceptionFilter返回当前的exception filter。应当保存这个函数指针并在不再需要使用自定义错误处理函数的时候当作参数再次调用SetUnhandledExceptionFilter。

² lpTopLevelExceptionFilter 是自定义的exception filter函数指针,如果传入NULL值则指定UnhandledExceptionFilter来负责异常处理。lpTopLevelExceptionFilter其函数原型应该是与UnhandledExceptionFilter同型:

LONG WINAPI UnhandledExceptionFilter(

STRUCT _EXCEPTION_POINTERS *ExceptionInfo // address of

// exception info

);

² lpTopLevelExceptionFilter的返回值应该是下面3种之一:

EXCEPTION_EXECUTE_HANDLER = 1

EXCEPTION_CONTINUE_EXECUTION = -1

这两个返回值都应该由调用UnhandledExceptionFilter后返回。

EXCEPTION_EXECUTE_HANDLER 表示进程结束

EXCEPTION_CONTINUE_EXECUTION表示处理异常之后继续执行

EXCEPTION_CONTINUE_SEARCH = 0

进行系统通常的异常处理(错误消息对话框)

² lpTopLevelExceptionFilter的唯一的参数是_EXCEPTION_POINTERS结构指针。

typedef struct _EXCEPTION_POINTERS { // exp

PEXCEPTION_RECORD ExceptionRecord;

PCONTEXT ContextRecord;

} EXCEPTION_POINTERS;

其中PCONTEXT是一个指向进程上下文结构的指针,保存了各个寄存器在异常发生的时候的值,详细信息参考《Windows核心编程》。

ExceptionRecord则指向另一个结构体EXCEPTION_RECORD:

typedef struct _EXCEPTION_RECORD { // exr

DWORD ExceptionCode;

DWORD ExceptionFlags;

struct _EXCEPTION_RECORD *ExceptionRecord;

PVOID ExceptionAddress;

DWORD NumberParameters;

DWORD ExceptionInformation[EXCEPTION_MAXIMUM_PARAMETERS];

} EXCEPTION_RECORD;

DWORD ExceptionCode;

异常代码,指出异常原因。常见异常代码有:

EXCEPTION_ACCESS_VIOLATION = C0000005h

读写内存冲突

EXCEPTION_INT_DIVIDE_BY_ZERO = C0000094h

除0错误

EXCEPTION_STACK_OVERFLOW = C00000FDh

堆栈溢出或者越界

EXCEPTION_GUARD_PAGE = 80000001h

由Virtual Alloc建立起来的属性页冲突

EXCEPTION_NONCONTINUABLE_EXCEPTION = C0000025h

不可持续异常,程序无法恢复执行,异常处理例程不应处理这个异常

EXCEPTION_INVALID_DISPOSITION = C0000026h

在异常处理过程中系统使用的代码

EXCEPTION_BREAKPOINT = 80000003h

调试时中断(INT 3)

EXCEPTION_SINGLE_STEP = 80000004h

单步调试状态(INT 1)

DWORD ExceptionFlags;

异常标志

0,表示可修复异常

EXCEPTION_NONCONTINUABLE = 1,表示不可修复异常。在不可修复异常后尝试继续执行会导致EXCEPTION_NONCONTINUABLE_EXCEPTION = C0000025H异常。

struct _EXCEPTION_RECORD *ExceptionRecord;

当异常处理程序中发生异常时,此字段被填充,否则为NULL

PVOID ExceptionAddress;

发生异常的地址(EIP)

DWORD NumberParameters;

规定与异常相关的参数数量(0-15),是ExceptionInformation数组中元素个数。

DWORD ExceptionInformation[EXCEPTION_MAXIMUM_PARAMETERS];

异常描述信息,大多数异常都未定义此数组,仅有EXCEPTION_ACCESS_VIOLATION 异常的描述信息:

ExceptionInformation[0],描述导致异常的操作类型

= 0 读异常

= 1 写异常

ExceptionInformation[1],发生读写异常的内存地址

也就是说,只要注册了自己写的这个exception filter,一旦发生异常,进入这个exception filter,从参数我们就能获得各种需要的信息了。而这个exception filter需要做的就是保存这些信息,然后将异常处理的事情交还给系统就行了:

// in the beginning

// Install the unhandled exception filter function

g_previousFilter = SetUnhandledExceptionFilter(MyUnhandledExceptionFilter);

// exception filter

LONG WINAPI MyUnhandledExceptionFilter(PEXCEPTION_POINTERS pExceptionInfo)

{

WriteLogFile(pExceptionInfo); // 写入文件

if ( g_previousFilter )

return g_previousFilter( pExceptionInfo );

else

return EXCEPTION_CONTINUE_SEARCH;

}

三. 使用COD文件精确分析异常原因

说精确分析多少有点言过其实,发生异常的情况各不相同,分析真正原因很可能是一件极其复杂的事情。不过用COD文件能比MAP文件更精确地定位产生异常的位置。结合汇编代码和自定义的exception filter获得的错误情报寄存器状态等各种信息,找到异常发生的直接原因是很容易的。

1. 如何生成cod文件

仍然是打开“Project → Project Settings”,选择 C/C++ 选项卡,在“Category”栏选择“Listing Files”然后在Listing file type栏选择“Assembly with Machine Code”。重新编译工程后则可以在输出目录看到与每一个.cpp文件同名的.cod文件。

2. Cod文件的使用

首先还是利用map文件用获得的程序异常地址通过函数信息部分定位出错的OBJ和函数,并同样记录偏移地址(用获得的异常地址减去该函数的Rva+Base的差值)。然后,在相应的cod文件中(而不是在map文件后面的行号信息部分)来查找出错的函数,找到如下的格式:

?OnPaint@CShowDlg @@IAEXXZ PROC NEAR ; CShowDlg::OnPaint, COMDAT

; 81 : { (←格式为: 行号 : 源代码)

00000 83 ec 64 sub esp, 100 ; 00000064H

(↑偏移地址) (↖机器码) (↑汇编码)

00003 56 push esi

; 82 : if (IsIconic())

(下面省略)

找到出错的函数以后,再用偏移地址就能找到准确的异常发生的地方。然后通过源程序、汇编码即可进行更详尽的分析了。

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
>>返回首页<<
推荐阅读
 
 
频道精选
 
静静地坐在废墟上,四周的荒凉一望无际,忽然觉得,凄凉也很美
© 2005- 王朝网络 版权所有