VC++調試技巧

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:快速觀察的功能。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章