peakzhang 发表于 2008-7-15 23:24:33

Windows程序调试(1)

Windows程序调试一:便于调试的C++代码
1:C++的强制类型转换
static_cast: 不能在指针类型与非指针类型之间进行转换,也不能消除类型中的const和volatile属性。但它能在编译时刻验证被转换的变量与目标类型之间是否相容。
dynamic_cast: 在运行时刻对强制类型转换进行检查。当指针无效时,返回0。若是无效的引用强制类型转换就发生bad_cast异常
const_cast: 消除了类型中的const 和 volatile属性
reinterpret_cast: 能转换不相容的数据类型,特别是指针和非指针类型。如一个指针传递给多态LPARAM参数。
尽量使用C++的类型转换,有个很大的好处就是:你可以使用“Find in Files”进行相似搜索,来检验类似的错误。

二:在Windows中调试
1.         事后调试
优先选择的版本:
       调试版本、带有调试符号的发布版本、使用Dr.Watson日志文件进行事后调试
2.在Visual C++的调试窗口中输入:@ERR 或者@ERR,hr来监视GetLasstError的值
3.DLL重新定位技术和重定位工具、绑定工具的使用
       当程序使用多个DLL时,有可能某些DLL无法加载到最佳基地址,这会引起性能严重下降。
       Vc\Bin\ 下,在Build后使用,假如程序包含如下文件:myapp.exe mydll1.dll mydll2.dll mydll3.dll ,则在工程中建立如下文本文件Rebase.txt,内容是:
Myapp.exe
Mydll1.dll
Mydll2.dll
Mydll3.dll
然后使用如下命令:
Rebase –b 400000 –R c:\xxxxx\debug\ -G Rebase.txt
也可以在工程设置对话框里面进行如下设置:
Post_build Step标签:Post_build_Command选项中加入上述命令
注意不要重新定位第三方的DLL,特别是Windows系统的DLL。
然后再使用绑定工具进行处理vc\common\tools\下
4.使用映射文件调试
创建:
       工程设置对话框,Link标签,Debug类打开Generate mapfile选项。还需要在Project Options对话框中加入:/MAPINFO:LINES
这样可以输出程序代码地址和源代码行号的映射。如果需要引出序号,输入
/MAPINFO:EXPORTS
把全部模块的映射文件归档,有利于事后调试。
发生崩溃后,使用文本编辑器检查MAP文件,首先查看最佳装载地址,这是映射文件会假定的装载模块的地址。如果你不将你的程序重定位,并且模块产生了虚地址空间冲突,所有的虚地址(准确的说是RVA+Base值)必须要调整成使用实际基地址。
接下来的一步是找到与崩溃地址匹配的最好的函数。公共函数在静态函数的前面列举,所以要找到匹配函数,两个列表你都需要检查。这两个函数列表中, 第三列包含Rva+Base的值(例如,0x00401044),这是相对虚地址(例如0x00000044),加上假定的基地址(0x00400000),再加上PE文件头大小(例如0x00001000)。匹配的最好的函数在崩溃地址上,或者在更低的地址上。
最后一步是找到匹配的最好的源文件行号。映射文件按照相对虚地址列举了行号,所以你不得不做反向工作。即从崩溃地址开始(例如0x00401044),减去实际基地址(0x00400000)和PE文件头(0x00001000),使用剩下的值(0x00000044)。现在检查RVA,直到找到了最好的匹配,它恰好是RVA或者更低。
5.使用PDB文件调试
       装载程序工程,debug(F11或者Step Into)
       显示反汇编窗口
       在EDIT菜单中选择Go To命令,Go to What 里面选择Address选项。
       在Enter Address expression 对话框中输入导致崩溃的地址。确定地址以0x开始。
       在反汇编窗口找到崩溃地址,单击GOTO 按钮。
       在上下文菜单中使用Go To Source命令,从反汇编窗口中找到源码位置。

6.使用Dr.Watson调试
输入命令:Drwtsn32.exe
或者在附件的系统信息里面找到。
一定要按照调试符号(Win2000和NT4),后者在VC++的程序组中有符号安装程序。
在安装了一个服务组件后,记住要更新系统符号,否则很有可能发生不匹配的情况。
可以从服务组件光盘上的\Support\Debug\拷贝文件来更新。

三:使用VC++调试器进行调试
1.调试发布版本 - 黄金定律:最好为你的可执行程序创建调试符号,并且将得到的PDB文件存档,即使程序属于发布版本。
发布版本也可以创建带有调试符号的版本。
A: 选择需要设置的版本:DEBUG/RELEASE,对整个工程进行设置
B: 选择C/C++标签,Category选择General,如果是_DEBUG版本,在下面的Debug info里面选择Program Database for Edit and Continue,如果是发布版本,选择Program Database。这是与优化兼容的问题。
C: Link标签中选择Debug类,选中Debug info 和 Microsoft format选项。记住不要选中Separate types选项,这样才能使得所有的调试信息合并到一个文件。如果需要事后的调试信息映射文件,选中Generate mapfile.
D:对于发布版本,选中Link标签,在Project options对话框的最后加上:/OPT:REF
这个选项使得不被引用的函数和数据不会出现在可执行文件中,避免了尺寸的无谓增大。调试版本不要使用这个选项,它会关闭增量连接(incremental linking)
E:Rebuild All 整个工程
如果发现带有调试符号的可执行文件比不带的大很多,很可能没有加入/OPT:REF选项。

对于这种调试,可能使用反汇编窗口进行断点设置、寻找源码更为方便,如变量调试
软件测试中,最好Debug和Release版本都要测试,因为前者的错误很容易定位,而后者不是这样。

2.使用调试窗口的小技巧
观察窗口(Watch):可以直接把变量拖入,可以在value中修改变量值。
      可以根据标签进行变量修改,例如Watch1窗口中输入nFlags=2,在Watch2窗口中输入nFlags=3,这样,切换标签窗口时候,变量的值就会自动修改。
但这种方式要注意的是,它们会被继续求值,从而可能产生奇怪的结果,如果不用,请删除。
对于常数的输入,如果打开观察窗口的Hexadecimal display,则表达式中的数字都会被看做16进制例如myVal + 16 -> myVal + 0x16,如果需要使用10进制,请加入0n前缀
如myVal + 0n16。这一点对变量窗口一样适用。
      求值规则:
               只有活动标签的表达式求值
               调试器暂停时、活动标签被更改时
               从上向下进行
变量窗口,可以在value中修改变量值。
内存窗口,可以直接把变量拖入,注意起始地址

--------------------------------
author: winston
第一部分
页: [1]
查看完整版本: Windows程序调试(1)