使用MAP文件快速定位程序崩溃代码行(转)

      作为程序员,平时最担心见到的事情就是程序发生了崩溃,无论是指针越界还是非法操作,都将给我们的应用系统造成巨大的损失。但在一个大型系统的测试过程中,初期出现程序崩溃似乎成了不可避免的事。其实测试中出现程序崩溃并不可怕,反而是测试的成功。我们更为关心的是程序中的哪一行导致了系统崩溃,这样我们才能有针对性的进行改正。
  在VC中,我们可以利用出现程序崩溃时VC的自动跳转,定位到出错代码行。但在大量的压力测试时,尤其是多线程测试时,同时出现几十个错,这时VC本身的出错跳转往往会失灵。
  在这里我们介绍一种辅助查找程序崩溃代码行的好方法,它的核心就是利用编译时生成MAP文件中的信息来定位代码行。
下面就开始我们的介绍。
  首先我们必须生成程序的MAP文件。那么什么是 MAP 文件呢?简单地讲, MAP 文件是程序的全局符号、源文件和代码行号信息的唯一的文本表示方法,是整个程序工程信息的静态文本。它可以在任何地方、任何时候使用,不需要有额外的程序进行支持,仅仅通过一个文本阅读工具如Ultra Edit就可以打开了。而且,这是唯一能找出程序崩溃代码行的救星。
  那么我们应该如何生成MAP文件呢?在 VC 中,我们可以按下 Alt+F7,打开“Project Settings”选项页,选择 C/C++ 选项卡,并在最下面的 Project Options 里面输入:/Zd ,然后要选择 Link 选项卡,选中“Generate mapfile”复选框,并在最下面的 Project Options 里面输入:/mapinfo:lines,表示生成 MAP 文件时,加入行信息。最后按下 F7 来编译生成 EXE 可执行文件和 MAP 文件,此时可以在工程的Debug目录下找到刚刚生成的MAP文件,文件名为“工程名.map”。
  通过上面的步骤,已经得到了 MAP 文件,那么我们该如何利用它呢?让我们从一个简单的实例入手,一步一步演示使用MAP文件定位程序崩溃行的过程。
首先假设我们的VC工程中有下面这个文件:
//*****************************************************
// 程序名称:演示如何通过崩溃地址找出源代码的出错行
// 作者:刘可
// 日期:2003-6-19
// 本程序会产生“除0错误”,所以会导致
// 程序崩溃,弹出“非法操作”对话框。
//******************************************************
#i nclude

int crashtest(int a,int b)
{
int c;
c = a/b;
return c;
}

void main(void)
{
int a = 30;
int b = 0;
int ret;
printf("let's begin crash test...\n");
ret = crashtest(a,b);
}

很显然本程序有“除0错误”,在 Debug 方式下编译,运行时会产生“非法操作”。我们记录下产生崩溃的地址——在我的机器上是 0x0040102f 。这个在不同的机器上可能地址不同,但记下这个地址我们下面将要使用。
我们打开它的 MAP 文件:(这里列出我们比较关心的内容,其他的就略过了)

abort(工程名)

Timestamp is 3ef16533 (Thu Jun 19 15:24:35 2003)

Preferred load address is 00400000

Start    Length    Name        Class
0001:00000000 0001081dH .text        CODE
0002:00000000 000013baH .rdata        DATA
0002:000013ba 00000000H .edata        DATA
0003:00000000 00000104H .CRT$XCA       DATA
0003:00000104 00000104H .CRT$XCZ       DATA
0003:00000208 00000104H .CRT$XIA       DATA
0003:0000030c 00000109H .CRT$XIC       DATA
0003:00000418 00000104H .CRT$XIZ       DATA
0003:0000051c 00000104H .CRT$XPA       DATA
0003:00000620 00000104H .CRT$XPX       DATA
0003:00000724 00000104H .CRT$XPZ       DATA
0003:00000828  00000104H .CRT$XTA       DATA
0003:0000092c  00000104H .CRT$XTZ       DATA
0003:00000a30  00003236H .data        DATA
0003:00003c68  000019c8H .bss        DATA
0004:00000000  00000014H .idata$2       DATA
0004:00000014  00000014H .idata$3       DATA
0004:00000028  00000120H .idata$       DATA
0004:00000148  00000120H .idata$5        DATA
0004:00000268  000004f4H .idata$6       DATA

Address Publics by Value Rva+Base Lib:Object

0001:00000020 ?crashtest@@YAHHH@Z 00401020 f main.obj
0001:0000003c _main 0040103c f main.obj
0001:000000b0 _printf 004010b0 f LIBCD:printf.obj
0001:00000130 __chkesp 00401130 f LIBCD:chkesp.obj
0001:00000170 _mainCRTStartup 00401170 f LIBCD:crt0.obj
0001:000002a0 __amsg_exit 004012a0 f LIBCD:crt0.obj
0001:00000300 __stbuf 00401300 f LIBCD:_sftbuf.obj
0001:00000460 __ftbuf 00401460 f LIBCD:_sftbuf.obj
0001:00000520 __output 00401520 f LIBCD:output.obj
0001:000013c0 ___initstdio 004023c0 f LIBCD:_file.obj
0001:000014f0 ___endstdio 004024f0 f LIBCD:_file.obj
0001:00001510 __CrtDbgBreak 00402510 f LIBCD:dbgrpt.obj
0001:00001520 __CrtSetReportMode 00402520 f LIBCD:dbgrpt.obj
0001:00001580 __CrtSetReportFile 00402580 f LIBCD:dbgrpt.obj
0001:00001600 __CrtSetReportHook 00402600 f LIBCD:dbgrpt.obj
0001:00001620 __CrtDbgReport 00402620 f LIBCD:dbgrpt.obj

  如果仔细浏览 Rva+Base 这栏,我们可以发现第一个比崩溃地址 0x0040102f 大的函数地址是 0x0040103c ,所以在 0x0040103c 这个地址之前的那个入口就是产生崩溃的函数,也就是这行:

0001:00000020 ?crashtest@@YAHHH@Z 00401020 f main.obj

  因此,发生崩溃的函数就是 ?crashtest@@YAHHH@Z,所有以问号开头的函数名称都是 C++ 修饰的名称。所以在我们的源程序中,这个发生崩溃的函数就是 crashtest ()!

  现在我们便轻而易举地知道了发生崩溃的函数名称。把它记下来,然后我们将要直接定位发生崩溃的代码行了。我们注意 MAP 文件的最后部分——代码行信息(Line numbers information),它是以这样的形式显示的:

Line numbers for .\Debug\main.obj(D:\我的工作\技术\出异常例子abort\main.cpp) segment .text

12 0001:00000020 14 0001:0000002b 15 0001:00000035 16 0001:00000038
19 0001:0000003c 20 0001:00000057 21 0001:0000005e 23 0001:00000065
24 0001:00000072 25 0001:00000085

  第一个数字代表在源代码中的代码行号,第二个数是该代码行在所属的代码段中的偏移量。如果要查找代码行号,需要使用下面的公式做一些十六进制的减法运算:

崩溃行偏移 = 崩溃地址(Crash Address)- 基地址(ImageBase Address)- 0x1000

  为什么要这样做呢?因为我们得到的崩溃地址都是由 偏移地址(Rva)+ 基地址(Base)得来的,所以在计算行号的时候要把基地址减去。一般情况下,基地址的值是 0x00400000 。另外,由于一般的 PE 文件的代码段都是从 0x1000 偏移开始的,所以也必须减去 0x1000 。
  所以我们的:崩溃行偏移 = 0x0040102f - 0x00400000 - 0x1000 = 0x2f
我们在MAP 文件的中的代码行信息里查找不超过计算结果0x2f,但却最接近的数。发现是 main.cpp 文件中的:

14 0001:0000002b

  也就意味着在源代码中的第 14 行!让我们来看看源代码,注意注释行和空行也要计算在内,程序的第14行为:

c = a/b;

果然就是第 14 行啊,它发生了“除0异常”!

  方法已经介绍完了,从今以后,我们就可以精确地定位到源代码中的崩溃行,而且只要编译器可以生成 MAP 文件,无论在WIN平台还是UNIX平台,本方法都是适用的。
  本文我们只是列举了一个非常简单的“除0异常”例子,使用MAP文件的效力或许还不十分明显。但相信在我们的大型应用系统调试中,使用MAP文件的辅助方法来快速定位发生程序崩溃的函数以及代码行,将会为我们的程序调试工作节省大量时间和精力,提高我们的调试质量。我们甚至可以要求远地用户直接提供程序崩溃的地址,然后就可以在自己机器上利用MAP文件静态地找到出错的那行,并在程序中进行相应修正了。

 

转载于:https://www.cnblogs.com/Phoenix-Rock/archive/2009/08/13/1545375.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/276422.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

构建安全的Xml Web Service系列之如何察看SoapMessage

上一篇文章地址:构建安全的Xml Web Service系列一之初探使用Soap头 (5-22 12:53) 要分析Xml Web Service的安全性,首先要解决的问题是我们能了解和清楚Soap消息的格式和内容,如果获得不了SoapMessage,分析如何能构建安全Xml w…

前端高效开发必备的 js 库梳理

之前有很多人问学好前端需要学习哪些 js 库, 主流框架应该学 vue 还是 react ? 针对这些问题, 笔者来说说自己的看法和学习总结.首先我觉得在学习任何知识之前必须要有一个明确的学习目标, 知道自己为什么要学它, 而不是看网上说的一股脑的给你灌输各种知识, 让你学习各种库, …

交叉报表crosstab隐藏列名显示_SAP软件 报表查询之 输出格式设置

SAP不仅是功能强大、逻辑严谨的ERP软件,还提供了强大的报表查询功能。SAP的ALV报表展示功能是SAP的一大特点,实现了类似于EXCEL的功能。使用好ALV报表功能可以方便用户从SAP中取到想要的数据,尤其是财务用户。大家在使用SAP报表时&#xff0c…

CSS HACK 区别 IE6、IE7、IE8、Firefox兼容性

转载链接:http://developer.51cto.com/art/201009/226787_1.htm 本文向大家描述一下如何使用CSS HACK区别IE6、IE7、IE8、Firefox兼容性问题,针对不同的浏览器写不同的CSS code的过程,就叫CSS hack,也叫写CSS hack,相信你对本文介…

Flex sdk4 布局与更新

在flex中,主要使用LayoutManager来驱动组件的度量和布局策略, LayoutManager实现一个单例,在Application类构造函数中创建: 1 public functionApplication()2 {3 UIComponentGlobals.layoutManager ILayoutManager(4 Singleton.getInstance("mx.managers::ILayoutManager&…

linux PROC文件系统详解

/proc文件系统下的多种文件提供的系统信息不是针对某个特定进程的,而是能够在整个系统范围的上下文中使用。可以使用的文件随系统配置的变化而变化。命令procinfo能够显示基于其中某些文件的多种系统信息。以下详细描述/proc下的文件。----------------------------…

seo每日一贴_白杨SEO:我看ZAC的外贸SEO应该怎样做?(策略篇)

前言:这是白杨SEO公众号更新第64篇。本该写写头条SEO啥的,最近在师徒培训讲站内SEO时有旁听同学提到后面讲讲谷歌SEO怎么样,因为谷歌全世界搜索市场占有率,所以外贸SEO最主要还是做谷歌SEO。以白杨特意又去了前辈ZAC的SEO每日一贴…

Can't connect to local MySQL server through socket '/tmp/mysql.sock'

转载链接:http://blog.csdn.net/ixidof/article/details/5958904 摘要:解决不能通过mysql .sock连接MySQL问题 这个问题主要提示是,不能通过 /tmp/mysql .sock连到服务器,而php标准配置正是用过/tmp/mysql .sock,但是…

[转]网页栅格系统研究(2):蛋糕的切法

[出自]http://lifesinger.org/blog/2008/10/grid-system-2/首先澄清一个应用场景问题。研究(1)中指出,对于结构复杂的网站,不少设计师们喜欢采用960固定宽度布局。但要注意的是,960并不是万能钥匙,大部分网…

Vue3响应式原理

关注若川视野,回复"pdf" 领取资料,回复"加群",可加群长期交流学习本文结构- 关于Vue3- Vue2响应式原理回顾- Vue3响应式方案- Vue3响应式原理- 手写mini版Vue3响应式本文共计:2349字2图预计阅读时间&#xff…

python二值化 感兴趣区域_Python+OpenCV感兴趣区域ROI提取方法

方法一:使用轮廓步骤1"""src为原图"""ROI np.zeros(src.shape, np.uint8) #感兴趣区域ROIproimage src.copy() #复制原图"""提取轮廓"""proimagecv2.cvtColor(proimage,cv2.COLOR_BGR2GRAY) #转换成…

error while loading shared libraries: libmysqlclient.so.16: cannot open

转载链接:http://blog.sina.com.cn/s/blog_4ab24dd50100wnkv.html 问题解决办法: locate libmysqlclient.so.16 如我的mysql安装在/usr/local/mysql下,则结果为 /usr/local/mysql/lib/mysql/libmysqlclient.so.16 vi /etc/ld.so.conf 增…

编译cvaux错误的原因

平台vc6.0;在debug模式下编译cvaux时会提示三个错误:error C2039: foreground_regions : is not a member of CvFGDStatModelerror C2039: foreground_regions : is not a member of CvFGDStatModelerror C2039: foreground_regions : is not a member of CvGaussBG…

找准切入点,调试看源码,事半功倍

关注若川视野,回复"pdf" 领取资料,回复"加群",可加群长期交流学习最近写了很多源码分析相关的文章,React、Vue 都有,想把我阅读源码的一些心得分享给大家。React:React 架构的演变 - 从…

Android布局大全

Android的界面是有布局和组件协同完成的,布局好比是建筑里的框架,而组件则相当于建筑里的砖瓦。组件按照布局的要求依次排列,就组成了用户所看见的界面。 所有的布局方式都可以归类为ViewGroup的5个类别,即ViewGroup的5个直接子类…

java实现加减乘除运算符随机生成十道题并判断对错_2020年Java面试题(3年的工作总结),最全的知识点总结...

这份Java面试题整整花了三个月的时间来整理,都是自己再工作中总结出来,记住多少就写多少,希望这份资料可以帮助你们,文末有其余部分资料的领取方式.Redis12道面试题1.什么是Redis?答:Remote Dictionary Ser…

Linux MySQL 5.1.62 source install

源码包官网下载:http://dev.mysql.com/downloads/ MySQL手册官网下载:http://dev.mysql.com/doc/ 安装ncurses: 1、tar -zxvf ncurses-5.9.tar.gz 2、cd ncurses-5.9.tar.gz ; ./configure; make clean; make; make install 修改时区…

RDLC 示例 文章 1

GotReportViewer的实例,请在下面的连接中下载: http://files.cnblogs.com/waxdoll/RDLC.rar http://www.databasejournal.com/features/mssql/article.php/3605826/Mastering-OLAP-Reporting--Reporting-with-Analysis-Services-KPIs.htm MSSQL Server …

.NET 中的泛型 101

1.1.1 摘要 图1 C# 泛型介绍 在接触泛型之前,我们编程一般都是使用具体类型(char, int, string等)或自定义类型来定义我们变量,如果我们有一个功能很强的接口,而且我们想把它提取或重构成一个通用的接口,使…

年底了,给想进阶的的前端朋友一些福利

2020 年,很多朋友都经历了一段比较艰难的求职季。年末,“就业寒冬”迎来了一丝暖阳,很多中大型互联网公司扩大了未来一年的招聘需求。前不久,字节跳动放出了年末要招 1 万人的消息,腾讯校招规模也将扩张至 5000 人&…