VC++调试技巧

来源:互联网 发布:网络割接实施方案 编辑:程序博客网 时间:2024/04/29 07:40

Inside VC debug

本文内容来源于我在2004年给所在部门的新员工做的一次内部培训。在对新员工的培训过程中,发现对于新员工来说,在进入工作岗位后,关注编程的技巧比较多,而对于VC/Windows环境下的程序调试,以及相关工具的使用掌握的不好。从我自己学习的过程来看,调试和工具的使用主要靠长期的积累和摸索,相关的资料非常少,部分调试技术,对于工作多年的老员工来说,可能也从来没有接触过。下面我就将自己在日常工作中使用的一些调试经验作一些介绍,希望对大家有所帮助。

一、      调试

1.          基本技巧

1)      断点的设置与跟踪

a)     普通断点设置  F9

b)     断点进入条件(Ctrl+B

 

 

 

 

 

 

2)      跟踪

 

Step Into  进入函数、模块调试

Step Over 不进入函数调试

Step Out  从函数中返回

Run to Cursor  运行到鼠标所在行

 

 

 

 

 

 

 

 

 

 

 

 

 

 

3)      察看数据

a)       Watch

 

可以自由添加需要查看的变量

b)      Call Stack

 

运行堆栈,对出错(红叉)时的错误定位非常有效

c)      Memory

 

 

内存查看器。主要用于查看内存块内的数据。图中查看了b变量在内存中的值(第一个字节0A为变量b的值,即10)

d)      Variables

 

                     变量查看器。自动显示当前代码用到的变量的值。

e)       Registers

 

寄存器查看器。用于查看寄存器内的数据值。用于底层代码调试。

 

f)       Disassembly

查看汇编代码。

下面为函数aa()的汇编代码

8:    void aa()

9:    {

00401000   push        ebp

00401001   mov         ebp,esp

00401003   sub         esp,48h

00401006   push        ebx

00401007   push        esi

00401008   push        edi

00401009   lea         edi,[ebp-48h]

0040100C   mov         ecx,12h

00401011   mov         eax,0CCCCCCCCh

00401016   rep stos    dword ptr [edi]

10:       for(int i=0;i<100000;i++)

00401018   mov         dword ptr [ebp-4],0

0040101F   jmp         aa+2Ah (0040102a)

00401021   mov         eax,dword ptr [ebp-4]

00401024   add         eax,1

00401027   mov         dword ptr [ebp-4],eax

0040102A   cmp         dword ptr [ebp-4],186A0h

00401031   jge         aa+3Bh (0040103b)

11:       {

12:           int b=i;

00401033   mov         ecx,dword ptr [ebp-4]

00401036   mov         dword ptr [b],ecx

13:       }

00401039   jmp         aa+21h (00401021)

14:   }

4)      高级技巧

a)       伪寄存器

可以直接在Watch窗口中查看,特别是前2个,可以大大减少GetLastError的使用,免于调试过程中反复改动代码。

@hr    最后出错编号。等于GetLastError()

@hr,err  最后出错编号的文字描述

@EIP      指令寄存器

@寄存器名称              对应的寄存器

b)      逆向运行

X86 CPU运行时,当前运行代码所对应的地址保存于IP寄存器,32CPU保存在EIP,所以,我们可以理论上可以依靠修改EIP的值来让我们的代码跳转到任意代码行(考虑到函数调用时的参数需要压栈,如果要跨函数恢复到完全一样运行环境,比较困难,所以在本函数体内跳转比较合适,超出函数范围就不建议使用本技巧了)

实例:

程序运行情况如下图。

1. 当前运行到第23行代码a=2

2. 我们可以在编辑区和Watch@EIP伪寄存器中看到23行代码对应的内存地址是0x401096

3. 现在我们想让代码跳转到0x0040108F,让程序再运行一次22行的代码。那我们只需要将@EIP值改为0x0040108F

4.在下图中,我们可以看到代码被反向运行了。此技巧主要用于反复运行同一行代码或反复调用同一个函数。

 

 

c)      性能测试(Profilling)

可以统计每个函数在运行期间被调用了多少次,占用了多少CPU百分比。

 

但是此功能在VC6中不是很稳定,经常无法运行。另外,BoundsCheck的最新版本中,增加了Performance测试,可以进行函数和代码行级的性能统计。所以推荐使用BoundsCheck来进行测试。(7.0以上版本才包含此功能,但是由于版权问题,不能大范围使用,可以使用专用电脑来进行性能统计测试)

 

 

d)      远程调试(Debugger remote connection)

 

需要设置:

1.对方IP

 

2.附加DLL

 

要求2台调试机器的操作系统(包括补丁)版本完全一样。否则无法调试。一般用于调试图形程序等调试器(VC)会对代码运行产生影响的程序。

2.          错误定位

1)      基本技巧

开发机:运行碰到错误。

调试运行。碰到红叉后根据Stack信息定位到调试代码具体位置。

2)      高级技巧

测试机、用户机器:运行错误。

有代码行数时:直接调出源代码查看。

只有出错内存地址 可以使用.map文件定位错误函数即行数。在大型程序中,往往不能提供完备的.map文件,无法定位到代码行数或函数。这个时候可以使用ProcessInfo工具查看程序模块映射关系来推断出错模块。

 

例如:在ATNotes.exe运行过程中出错,出错地址为 7C920100,则根据上图,可以发现出错地址位于ntdll.dll模块中,因为ntdll.dll的基地址(BaseAddr)等于7C920000,模块所占内存大小为94000,说明7C920000~7D9B400ntdll.dll的代码段。所以可以定位此模块出错。

只有出错框,没有显示是哪个程序出错。有时候会有多个程序同时运行,当出错时,如果出错框没有显示程序名称,很难定位到底哪个程序出错了。

 

使用Spy++查看对话框信息,获得进程PID

 

打开进程管理器,查看进程和PID对应关系

 

可以发现出错对话框属于TestESP(8B0 = 2224)

 

 

3.          工具

1)      BoundsCheck

主要提供详细的调试信息。常用来检查内存、资源泄漏。

2)      Process Explorer

主要用来查看进程详细信息,如进程加载了哪些DLL等。

3)      VC Tools

VC所带的工具,常用的有:

SPY++:查看窗口信息

Dependency:查看DLL接口

OleView:查看COM组件信息

TestContainer:用于调试控件

ErrorLookup: 用于查看出错代码对应的错误说明

4)      DebugView

用于查看TRACE输出的调试信息,目前被软件开发部门大量使用。

 

5)      SoftIce/Ollydbg

2个工具功能非常强大,在调试中,主要用于已经在运行的程序调试。可以在指定的函数入口设置断点(包括系统API)。当函数被调用时,就可以进入断点开始汇编级的跟踪。

 

举例:在一次程序故障中,发现某台计算机中的程序出现运行不正常情况,但是出现概率极小,同时,在不稳定时,不允许退出程序进行代码调试,最后,经过分析,在开发机上大致定位了出错可能的范围,然后在出错机器上使用Ollydbg,定位到了可能出错的代码区,加入断点,跟踪运行期的汇编代码,最后查明了故障原因。

 

 

OllyDbg

 

本节涉及到的工具可以在我的ftp下载:ftp://10.10.20.60/tools

二、      单元测试

1.      DLLOCX模块介绍

1)      DLL的类型:

常规DLL:生成时自动生成CwinApp类,帮助管理DLL

扩展DLL:没有生成CwinAppDLL入口主要依靠DLLMainDLL_PROCESS_ATTACHDLL_PROCESS_DETACH消息

 

 

2)      DLL的常用加载方法:

静态加载

       VC工程设置加载(命令行加载)

 

       代码加载

#pragma comment (lib,"vfprojectd.lib")

 

动态加载

调用ci_r_IsServer()函数

HINSTANCE  hDLLHandle = AfxLoadLibrary (“vfprojectd.dll”)

if(hDLLHandle)

{

              typedef BOOL (*CI_R_ISSERVER)(void);

              CI_R_ISSERVER ci_r_IsServer = (CI_R_ISSERVER)GetProcAddress(hTempDll,"ci_r_IsServer");

              if(ci_r_IsServer)

              {

                     m_bSelfDS = ci_r_IsServer();

              }

              FreeLibrary(hDLLHandle);

}

 

3)      OCX常用调试方法:

Ocx主要使用TestContainerVB调试,不建议使用VC

2.      DLL单元测试举例

模块名称:VFProjectD.dll

头文件:OtherUseDll.h

库文件:VFProjectD.lib

加载方式:静态加载

 

 

 

VFProject共有10个接口函数。

对一个接口的测试,需要包含:边界测试、中值测试、随机数测试、非法数据测试

/*******************************************************************

*

* 函数名称:  CTestVFProjectDlg::OnTest1()

*     : test FileDlg()

*     : 何军[2006-4-20 13:15:32]

*

*     : [void] -

*

* 函数参数 :

*

*******************************************************************/

void CTestVFProjectDlg::OnTest1()

{

       // open

       CString pathn,filen;

       FileDlg(TRUE,"pic","pic",pathn,filen);

       Trace("Test1.1 Open Dialog:pathname=%s,filename=%s/n",pathn,filen);

 

       // save

       FileDlg(FALSE,"pic","pic",pathn,filen);

       Trace("Test1.2 Save Dialog:pathname=%s,filename=%s/n",pathn,filen);

 

       // 非法参数

       FileDlg(TRUE,"","",pathn,filen);

       Trace("Test1.3 Save Dialog:pathname=%s,filename=%s/n",pathn,filen);

 

       FileDlg(FALSE,"","",pathn,filen);

       Trace("Test1.4 Save Dialog:pathname=%s,filename=%s/n",pathn,filen);

}

 

 

测试结果

一些补充:

Go命令(F5):向下执行程序直到断点,或结束;
Restart命令(Ctrl+Shift+F5):重新开始调试,直到第一个断点或结束;
Stop Debugging(Shift+F5):结束调试工作,返回原工作环境;
Break:停止调试;

Step Into命令(F11):单步向下运行,该命令可以深入到Call调用的函数里,
执行每一步汇编指令,此时可以打开Registers窗口,观察各寄存器的变化;

Step Over命令(F10): 单步状态结束;

Step Out(Shift+F11):从Call函数中跳出来;

Run to Cursor(Ctrl+F10):执行到光标处;

Step Into Specific Function :单步执行特殊的功能;
Exception命令:将列出异常中断程序执行的情况,可以进行修改;
Threads命令:将列出被调试程序中的线程信息,包括线程的标号、名称、地址、
优先级等。
Show Next Statement命令:将当前正在调试的语句标识出来
Quick Watch:快速观察的功能。

原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 快递派件不成功怎么办 韵达快递不派件怎么办 中通快递不派件怎么办 理财回执单丢失怎么办 理财回单丢了怎么办 杭州市老年优待卡怎么办 网络连接不到服务器怎么办 即时库存有负数怎么办 电脑软件被拦截怎么办 超市无条码商品怎么办 场外期权有诈骗怎么办 ip地址访问受限怎么办 电脑ip地址受限怎么办 百度云资源打不开怎么办 百度网盘看文件字太小怎么办 密码输入三次错误怎么办 notes邮箱满了怎么办 小米8买不到怎么办 小米付款不发货怎么办 小米金融还款中怎么办 股票遇到闪崩怎么办 微信插件没有怎么办 excel打印太小怎么办 工地临时人员死亡怎么办 哺乳期乳腺增生疼怎么办 哺乳期有乳腺增生怎么办 哺乳期得了乳腺增生怎么办 中等教育认证花名册丢失怎么办 哺乳期囊性结节怎么办 乳腺增生堵奶怎么办 月子期乳房增生怎么办 母乳期乳腺增生怎么办 上市公司破产了股票怎么办 iptv错误码30022怎么办 电信iptv不清晰怎么办 电信iptv卡顿怎么办 pr滚动字幕闪烁怎么办 电视车表盘看不清怎么办 图片字看不清楚怎么办 字太潦草看不清怎么办 微信图片看不清怎么办