| |
||
| ::首页 >> 文档中心 >> 一般文档 >> 编译与调试技巧 | ||
| |
仅通过崩溃地址找出源代码的出错行 作者:老罗 提交者:eastvc 发布日期:2003-10-23 9:16:11 原文出处:http://www.luocong.com/articles/show_article.asp?Article_ID=29 作为程序员,我们平时最担心见到的事情是什么?是内存泄漏?是界面不好看?……错啦!我相信我的看法是不会有人反对的——那就是,程序发生了崩溃! “该程序执行了非法操作,即将关闭。请与你的软件供应商联系。”,呵呵,这句 M$ 的“名言”,恐怕就是程序员最担心见到的东西了。有的时候,自己的程序在自己的机器上运行得好好的,但是到了别人的机器上就崩溃了;有时自己在编写和测试的过程中就莫名其妙地遇到了非法操作,但是却无法确定到底是源代码中的哪行引起的……是不是很痛苦呢?不要紧,本文可以帮助你走出这种困境,甚至你从此之后可以自豪地要求用户把崩溃地址告诉你,然后你就可以精确地定位到源代码中出错的那行了。(很神奇吧?呵呵。) 首先我必须强调的是,本方法可以在目前市面上任意一款编译器上面使用。但是我只熟悉 M$ 的 VC 和 MASM ,因此后面的部分只介绍如何在这两个编译器中实现,请读者自行融会贯通,掌握在别的编译器上使用的方法。 Well,废话说完了,让我们开始! :) 首先必须生成程序的 MAP 文件。什么是 MAP 文件?简单地讲, MAP 文件是程序的全局符号、源文件和代码行号信息的唯一的文本表示方法,它可以在任何地方、任何时候使用,不需要有额外的程序进行支持。而且,这是唯一能找出程序崩溃的地方的救星。 好吧,既然 MAP 文件如此神奇,那么我们应该如何生成它呢?在 VC 中,我们可以按下 Alt+F7 ,打开“Project Settings”选项页,选择 C/C++ 选项卡,并在最下面的 Project Options 里面输入:/Zd ,然后要选择 Link 选项卡,在最下面的 Project Options 里面输入: /mapinfo:lines 和 /map:PROJECT_NAME.map 。最后按下 F7 来编译生成 EXE 可执行文件和 MAP 文件。 在 MASM 中,我们要设置编译和连接参数,我通常是这样做的: rc %1.rc 把它保存成 makem.bat ,就可以在命令行输入 makem filename 来编译生成 EXE 可执行文件和 MAP 文件了。 在此我先解释一下加入的参数的含义: /Zd 表示在编译的时候生成行信息 OK,通过上面的步骤,我们已经得到了 MAP 文件,那么我们该如何利用它呢? 让我们从简单的实例入手,请打开你的 VC ,新建这样一个文件: 01 //**************************************************************** 很显然本程序有“除0错误”,在 Debug 方式下编译的话,运行时肯定会产生“非法操作”。好,让我们运行它,果然,“非法操作”对话框出现了,这时我们点击“详细信息”按钮,记录下产生崩溃的地址——在我的机器上是 0x0040104a 。 再看看它的 MAP 文件:(由于文件内容太长,中间没用的部分我进行了省略) CrashDemo Timestamp is 3e430a76 (Fri Feb 07 09:23:02 2003) Preferred load address is 00400000 Start Length Name Class Address Publics by Value Rva+Base Lib:Object 0001:00000020 ?Crash@@YAXXZ 00401020 f CrashDemo.obj entry point at 0001:000000f0
13 0001:00000020 14 0001:00000038 15 0001:0000003f 16 0001:00000046 如果仔细浏览 Rva+Base 这栏,你会发现第一个比崩溃地址 0x0040104a 大的函数地址是 0x00401070 ,所以在 0x00401070 这个地址之前的那个入口就是产生崩溃的函数,也就是这行: 0001:00000020 ?Crash@@YAXXZ 00401020 f CrashDemo.obj 因此,发生崩溃的函数就是 ?Crash@@YAXXZ ,所有以问号开头的函数名称都是 C++ 修饰的名称。在我们的源程序中,也就是 Crash() 这个子函数。 OK,现在我们轻而易举地便知道了发生崩溃的函数名称,你是不是很兴奋呢?呵呵,先别忙,接下来,更厉害的招数要出场了。 请注意 MAP 文件的最后部分——代码行信息(Line numbers information),它是以这样的形式显示的: 13 0001:00000020 第一个数字代表在源代码中的代码行号,第二个数是该代码行在所属的代码段中的偏移量。 如果要查找代码行号,需要使用下面的公式做一些十六进制的减法运算: 崩溃行偏移 = 崩溃地址(Crash Address) - 基地址(ImageBase Address) - 0x1000 为什么要这样做呢?细心的朋友可能会留意到 Rva+Base 这栏了,我们得到的崩溃地址都是由 偏移地址(Rva)+ 基地址(Base) 得来的,所以在计算行号的时候要把基地址减去,一般情况下,基地址的值是 0x00400000 。另外,由于一般的 PE 文件的代码段都是从 0x1000 偏移开始的,所以也必须减去 0x1000 。 好了,明白了这点,我们就可以来进行小学减法计算了: 崩溃行偏移 = 0x0040104a - 0x00400000 - 0x1000 = 0x4a 如果浏览 MAP 文件的代码行信息,会看到不超过计算结果,但却最接近的数是 CrashDemo.cpp 文件中的: 16 0001:00000046 也就是在源代码中的第 16 行,让我们来看看源代码: 16 i /= j; 哈!!!果然就是第 16 行啊! 兴奋吗?我也一样! :) 方法已经介绍完了,从今以后,我们就可以精确地定位到源代码中的崩溃行,而且只要编译器可以生成 MAP 文件(包括 VC、MASM、VB、BCB、Delphi……),本方法都是适用的。我们时常抱怨 M$ 的产品如何如何差,但其实 M$ 还是有意无意间提供了很多有价值的信息给我们的,只是我们往往不懂得怎么利用而已……相信这样一来,你就可以更为从容地面对“非法操作”提示了。你甚至可以要求用户提供崩溃的地址,然后就可以坐在家中舒舒服服地找到出错的那行,并进行修正。 是不是很爽呢? :) |
改为:\n ,即可! 还有,srcStr = str_end +1 ;也是用来使指针加1 ,可是在windows就是不行啊!必须要 ++srcStr ;才可以! 但是在unix上是可以的! why?? ( czyfseal 发表于 2004-2-18 20:55:00) 对于这种情况,我这个程序可以触发,当然我是知道在那里出问题了! 但是,如何通过map文件来查找呢? 程序如下: #include <iostream.h> #include <string.h> char* my_strpbrk(char* char* ); void main() { char* srcStr = "This is \0 a test and \0 i will test !" ; char* chrSec = "\0" ; my_strpbrk(srcStr , chrSec ) ; } char* my_strpbrk(char* srcStr , char* chrSec) { char* str_end ; str_end = strpbrk(srcStr , chrSec) ; while (*srcStr != NULL) { if(str_end) { //在unix上可以使用 srcStr = str_end +1 ; cout << "1 str_end is : "<< str_end << endl ; cout << "*srcStr is : " << *srcStr << endl ; } else { cout << "2 str_end is : " << str_end ; } ++ srcStr ; }; return str_end ; } 恳请各位大侠赐教! ( czyfseal 发表于 2004-2-18 20:53:00) "0x00404240" 指令引用得"0x00000000"内存。 该内存不能为"read" 这个该怎么查找呢!谢谢! ( czyfseal 发表于 2004-2-18 20:49:00) 原文: http://www.codeproject.com/debug/mapfile.asp ( taishan_sword 发表于 2003-12-11 9:37:00) ( pathfinder 发表于 2003-10-28 15:12:00) 只是有一种情况搞不定: { int *pp; *pp=222; } release版本下出错: “"0x6bc8b527"指令引用的"0x00000516"内存。该内存不能为"read"。” 注:指令及内存地址因机器而大同小异 ( ywqz 发表于 2003-10-28 13:00:00) 是弄多了,就出来了吗 太高深了 ( DzqSuper 发表于 2003-10-24 21:52:00) ....................................................... More... |