作为程序员,平时最担心见到的事情就是程序发生了崩溃,无论是指针越界还是非法操作,都将给我们的应用系统造成巨大的损失。但在一个大型系统的测试过程中,初期出现程序崩溃似乎成了不可避免的事。其实测试中出现程序崩溃并不可怕,反而是测试的成功。我们更为关心的是程序中的哪一行导致了系统崩溃,这样我们才能有针对性的进行改正。
  在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

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

  1. 使用MAP文件快速定位程序崩溃代码行

    作为程序员,平时最担心见到的事情就是程序发生了崩溃,无论是指针越界还是非法操作,都将给我们的应用系统 造成巨大的损失.但在一个大型系统的测试过程中,初期出现程序崩溃似乎成了不可避免的事.其实测试中出现 ...

  2. VS2010下使用dmp文件和pdb文件定位程序异常代码行号的注意事项

    2018-01-12 创建人:Ruo_Xiao 2018-01-15 修改人:Ruo_Xiao 添加为dump.exe和pdb版本保持一致的原因. 一.minidump文件 崩溃转储是创建一个应用程序 ...

  3. vs2010利用map,cod文件定位崩溃代码行

     利用map,cod文件定位崩溃代码行 利用vs2010 新建一个空的控制台项目,添加文件gtg.cpp,内容如下 void crash() { inti=1; intj=0; i/=j; } i ...

  4. 【iOS】iOS 调试快速定位程序在哪崩溃

    iOS 开发过程中经常遇到程序崩溃.快速定位程序在哪崩溃的步骤如下: 1. 2. 3. 这样设置后,程序崩溃时会定位到崩溃的语句,如下: 原文链接:iOS开发何如在调试的时候轻松找到程序在哪里崩溃 转 ...

  5. 如何快速定位程序Core?

    导读:程序core是指应用程序无法保持正常running状态而发生的崩溃行为.程序core时会生成相关的core-dump文件,是程序崩溃时程序状态的数据备份.core-dump文件中包含内存.处理器 ...

  6. 安卓逆向_7 --- 六种快速定位关键 Smali 代码的方法 ( 去掉 RE 广告 )

    哔哩哔哩:https://www.bilibili.com/video/BV1UE411A7rW?p=34 具体用法,看视频教程( 去掉 RE 的 结束广告 ) 6 种定位关键代码的方法,当然还有其他 ...

  7. CrashFinder,找到崩溃代码行

    1.CrashFinder需要PDB才可以工作: 2.对于Release版本程序,需要设置产生program database 和generate debuginfo,才可以使用crash finde ...

  8. 其中一个页签慢_Word中如何快速定位到页、行、表格、公式,查找与替换方法...

    如果一个文档有几百页甚至上千页,要通过拖动滑块定位到某页将是十分不易的事,拉多了又过了,拉少了又离得太远.如果用 Word 2016 提供的定位功能,定位到某页将变得十分容易的事,并且速度相当快,瞬间 ...

  9. 软件崩溃时 将堆栈信息写入dump文件, 并使用VS2010定位程序崩溃位置

    Windows下有三种生成dump文件的方式: 1.通过任务管理器和注册表:2.WinDbg抓取:3.程序中加入存储Dump的代码 具体生成方法参看:Windows下dump文件生成与分析 本文详细介 ...

最新文章

  1. 2019秋招面试常考题目
  2. ajax 加载 提示 锁屏,js锁屏解屏通过对$.ajax进行封装实现
  3. STM32中EXTI和NVIC的关系
  4. 视频需求超平常数 10 倍,却节省了 60% 的 IT 成本投入是一种什么样的体验?
  5. 全球及中国硝基复合肥市场产销状况与未来运营前景分析报告2022版
  6. Hazelcast入门指南第2部分
  7. python编程制作接金币游戏_pygame学习笔记(6):完成一个简单的游戏
  8. 互联网人理想假期VS现实假期
  9. Android分享功能的一点总结
  10. 【云周刊】第141期:阿里正式发布《Java开发手册》终极版!绝对珍藏!
  11. react-tv-focusable
  12. Unity-UI-实现文本框内容自动滚动
  13. 短视频剪辑入门技巧,简单却重要
  14. 【经典箴言 || 人生感悟 】
  15. Qlikview---日期字段
  16. Stream流中常用的方法
  17. 数据分析-1.数据分析介绍
  18. 网页成品——手表商城网站模板源码(17页) web期末作业设计网页_手表商城网页设计作业成品
  19. nrm : 无法加载文件 c:\users\甜点\appdata\roaming\npm\nrm.ps1,因为在此系统上禁止运行脚本。
  20. 20000字详解大厂实时数仓建设(好文收藏)

热门文章

  1. 算法 --- 翻转二叉树
  2. Python 字符串的内置函数
  3. Linux运维系统工程师系列---13
  4. An Introduction to Our Code Breaking Team
  5. C#:WinForm无边框窗体移动方法、模仿鼠标单击标题栏移动窗体位置
  6. ITU-RBT.656视频标准接口
  7. 如何选择一款优秀的儿童读写台灯?
  8. 【python数字信号处理】——Z变换
  9. 学习笔记(40):Python实战编程-文本
  10. C++总结笔记(十二)—— 智能指针