[debug]调试Release版本应用程序

引言

如果在您的开发过程中遇到了常见的错误,或许您的 Release 版本不能正常运行而 Debug 版本运行无误,那么我推荐您阅读本文:因为并非如您想象的那样, Release 版本可以保证您的应用程序可以象 Debug 版本一样运行。

如果您在开发阶段完成之后或者在开发进行一段时间之内从来没有进行过 Release 版本测试,然而当您测试的时候却发现问题,那么请看我们的调试规则 1:

规则 1 : 经常性对开发软件进行 Debug Release 版本的常规测试 .

测试 Release 版本的时间间隔越长,排除问题的难度越大,至少对 Release 版本进行每周 1 次的测试,可以使您在紧凑的开发周期内节省潜在的排故时间 .

不要随意删除 Release 版本需要的代码

这点看起来似乎再明显不过,但却是开发人员无意中经常犯的错误,原因在于编译器编译 Release 版本时候会主动排除在代码中存在的宏,例如 ASSERT TRACE Release 版本会自动排除,这样导致的问题是您在这些宏当中运行的代码也被随之删除,这是非常危险的事情 J , 例如:

      ASSERT ( m_ImageList . Create ( MAKEINTRESOURCE ( IDB_IMAGES ), 16, 1, RGB (255,255,255)));

这样的代码在 Debug 模式不会出错,图像列表也自动创建了,然而在 Release 版本呢?后继使用 m_ImageList 对象只会造成程序的 Crash! ,因此 ASSERT 宏中尽量使用逻辑运算符作为验证。

规则 2 : 不要将代码放置在仅在某种编译选项中执行的地方,对于使用 _DEBUG 等编译选项宏内部的代码必须不影响整个程序的使用 .

规则 3 : 不要使用规则 2 作为评判标准来删除 ASSERT 宏, ASSERT 宏是个有用的工具,但容易使用错误 .

使 Debug 编译模式接近 Release 模式

如果您的 Release 版本存在的问题是由代码被编译器自动排除造成的,那么通过这个方法您的问题可能会重现 .

一些问题的产生可能是由于不同编译选项之间预定义符号造成的,因此您可以更改编译模式下的预定义符号,从而使您的 Debug 模式接近 Release 模式,观察错误是否产生,更改编译预定义符号方法如下 :

  • Alt-F7 打开项目设置,在 C++/C 页面,选择 "General" 类别,更改 "_DEBUG " 符号为 "NDEBUG ".
  • C++/C 页面 , 选择 "Preprocessor" 类别,添加预定义符号 "_DEBUG " "Undefined Symbols" .
  • 使用 "Rebuild All" 重新编译

如果通过上面设置,您在 Release 编译模式下面的问题在 Debug 模式下重现,那么请您依据以下步骤对您的代码进行修改 :

  • 查找 ASSERT 排除其中的所有重要执行语句,或者将 ASSERT 修改为 VERIFY .
  • 检查 "#ifdef _DEBUG " 内所有代码,排除 Release 模式使用的代码 .
  • 查找 TRACE 排除其中的所有重要执行语句 . TRACE ASSERT 一样,仅在 Debug 模式下编译 .

如果通过上面修改更正了您在 Debug 模式下的问题,那么您可以重新编译 Release 模式,非常有可能您可以解决先前存在的问题! .

错误的假定造成编译模式错误

您是否经常性的假定您的变量或者对象被初试化成某个指定的值 ( 可能 0) ?您是否假定你所有关联到的资源在应用程序中都存在?这些也是 Debug Release 模式下不同问题产生的原因 .

规则 4 : 除非您在代码中对变量进行初始化,否则不能作出如上假定 . 包括全局变量,自动变量,申请对象和 new 对象 .

这种情况还常常发生在内存顺序的问题,记得原来使用结构体的时候为了使用方便,比较两个结构体对象使用 memcmp ,在 Debug 版本工作正常,而 Release 版本计算出错误的解,看来的确不能进行错误的假定!

规则 5 : 确保删除资源的所有引用都被删除,例如 resource.h 中的定义 .

软件开发中,不同编译版本对变量和内存的初始化是不同的 . 如果您假定变量初始化为 0, 那么在 Win9x 系统的 Release 模式下,会出现异常现象。因此对所有变量,内存显式清 0 是较为安全的做法 .

如果您引用了已经被删除的资源,您的 Debug 版本可以正常工作,但是 Release 版本可能会 crash.

您是否相信编译器 ?

编译器警告级别和编译噪音有着相当大的关系 .

通过提高编译器警告级别可增加程序隐藏问题暴露的机会 . 通常设置警告级别在 "Level 3" 或者 "Level 4". 编译并解决所有警告,这是发布 Release 版本应用程序的一个很好的建议 . 这能暴露会使您的应用程序出现问题的很多初始化问题和其它潜在的错误 .

规则 6 : 开始项目之前先将编译警告级别设置在 "Level 3" 或者 "Level 4" ,登记代码之前确保消灭所有警告! .

总结报告

编译模式下的调试

曾经不止一次的听到一些 VC 开发者说 Release 模式下面不能进行调试,幸运的是:通过相应设置,可以在 Release 模式进行调试,因此那只不过是一个以讹传讹的荒谬说法而已 .

规则 7 : 当前面所有的方法都无效的时候,在 Release 模式下面进行调试 .

Release 模式可以进行调试,第一步是打开符号表 :

  • Alt-F7 打开项目设置,在 C++/C 页面,选择 "General" 类,修改 Debug Info setting "Program Database".
  • "Link" 页面,选择 "Generate Debug Info".
  • "Rebuild All"

这些设置将允许您在 Release 模式下保留符号表,您也可以同时考虑以下设置 :

  • 调试 Release 版本应用程序,您可以关闭优化选项 .
  • 如果在 Release 模式下面不能设置断点,添加指令 "__asm {int 3}" 可以是您的应用程序在改行停止 ( 确定在发布应用程序时候排除这些代码 ).

Release 模式进行调试的几个限制 .

  • 最大的问题在于您不能跟踪到 MFC 函数内部,原因在于 Release 版本的 MFC 动态链接库不包含调试信息和符号表 .
  • 同上,想要调试调用的 dll ,您必须给它们全部加上调试信息和符号表 .

编译器生成了错误的代码 ?

或许有的时候您会发现 VC++ 编译器生成了 问题代码 ,然而坦率的讲,人们通常抱怨的太早 . 您可以在 Release 模式下面关闭优化选项来进行测试 .

如果这个操作解决了您的问题,或许您的编码习惯存在问题 . 信不信由你 , 极其可能在您的编码中存在模棱两可的求解或者看起来似乎正确,某些条件下也是正确的情况 . 举个例子,下面的代码在 Debug 模式似乎一切 正常 ,而在 Release 模式下面却会出错!

#include <stdio.h>  

  

int   * func1() 

{ 

int    retval =  5   ; 

return    &retval; 

} 

  

int    main( int    argc,  char   * argv[]) 

{ 

printf( "%d/n"   , *func1()); 

return     0   ; 

} 

我相信大多数程序员尤其是初学者容易遇到此类情况的 .

规则 8 : 如果关闭 Release 模式的优化选项可以使您的应用程序运行正常,而打开优化选项则出现问题的化,原因多半在于您的不良编码习惯造成的 . 这意味着必须仔细检查您的代码,清理出那些错误的假设,悬空指针等等 . 等同的这告诉您,在 Debug 模式和关闭优化选项的 Release 模式下您的应用程序工作正常全是因为系统隐含的运气,您必须着手更正存在隐患的代码,否则在日后可能会造成巨大的损失 .

规则 9 : 如果您已经彻底检查了您的代码,并且没有发现问题,那么您最好逐个打开优化选项将产生错误的原因限制在某个范围之内 .

BTW- 以上问题代码由 C++ 编译器自动检出 . 如果您已经遵循 规则 6 您或许在前面环节中已经解决了这些问题 .

凭我的开发经验,编译器极少会产生错误的代码(当然要注意接口程序边界对齐的问题) . 通常在使用模板类时候 VC6 编译器或许会产生断言 ASSERT 错误,这种情况您只需更新补丁即可解决 .

最后的思考

在日常编码中只需稍微增加一点严格的检测,便能有效的避免新的 Debug -v- Release 模式问题的产生,以下是我的一些经验 .

1.      取出 (check out) 需要修改的代码 .

2.      修改代码,排除所有警告,编译 Debug Release 版本 .

3.      详细测试新代码,即单步调试新代码段之后进入工作代码,确保代码无误 .

4.      更正所有问题 .

5.      确认无误之后将新代码登记入库 (check in).

6.      对登记入库的代码进行全新的编译,确保新登记代码与其它代码融合 .

7.      重新详细测试代码 .

8.      更正新问题 ( 或许可以发现登记入库代码存在的问题 )

严格按照以上步骤,您在设计开发过程中即可解决大量问题,避免在最后发布应用程序时候产生新的难以定位的问题 .

后记

本文是在我的开发历程中遇到 Release 版本应用程序发布,产生错误的时候苦苦求索得到的一些经验,原文来自于 codeproject ,经过本人润色,改写成为适合国内开发者的文章,希望能对大家有用,谢谢!

 

原帖:http://blog.csdn.net/chinacodec/archive/2009/01/26/3853473.aspx

你可能感兴趣的:(优化,测试,mfc,编译器,imagelist,preprocessor)