這篇文章只介紹了一些有關Visual Studio的基本調試技巧,但還有其他一些同樣有用的技巧。我整理了一些Visual Studio(至少在VS 2008下)原生開發的除錯技巧。 (如果你是工作在託管程式碼下,調試器會有更多的特性,在CodeProject中有介紹它們的文章),以下是我的整理的一些技巧:
異常中斷| Break on Exception
Watch視窗中的偽變數| Pseudo-variables in Watch Windows
符號越界後檢視堆物件|
查看陣列的值
避免進入不必要的函數
從程式碼啟動偵錯器| Launch the debugger from code
在Outputpute從程式碼啟動偵錯器| Launch the debugger from code
在Outputputeput 洩漏
調試發行版| Debug the Release Build
遠端調試
技巧1: 異常中斷
在處理被調用之前,異常發生時可以啟動調試器進行中斷,立即調試程序。操作呼叫堆疊便於你去查找異常發生的根本原因。
Vistual Studio允許你去指定想要中斷的異常類型或特殊異常。選擇選單Debug>Exceptions彈出對話框,你可以指定原生的(或託管的)異常,除了調試器自帶的一些預設異常,你還可以添加自己的自訂異常。
下面是一個std::exception 異常拋出時調試器中斷的例子。
技巧2:Watch視窗中的偽變量
Watch視窗或QuickWatch對話框提供一些特定的(調試器可識別的)變量,稱為偽變量。文件包含以下內容:
$tid—–目前執行緒的執行緒ID
$pid——進程ID
$cmdline———-啟動程式的命令列字串
$user———-正在執行程式的帳戶資訊
$registername—–顯示暫存器registername 的內容
不管怎麼樣,關於最後一個錯誤的偽變數是非常有用的:
$err——–顯示最後一個錯誤的錯誤碼
$err,hr—顯示最後一個錯誤的錯誤訊息
技巧3:符合越界後查看堆對象
有時候,在調試符號越界後,你還想查看對象的值,這個時候,watch窗口中的變量是被禁用的,不能再查看(也不能更新),儘管物件仍然存在。你如果知道物件的位址,可以繼續充分地觀察它。你可以將位址轉換為該物件類型的指針,放在watch窗中。
下面的範例中,當單步跳出do_foo()之後,_foo不能再被存取。但是,將它的位址轉換為foo*後,就可以繼續觀察這個物件。
技巧4:查看數組的值
如果你在操作一個很大的數組(我們假設至少有幾百個元素吧,但是可能更少),在Watch窗口中展開數組,查找一些特定範圍內的元素很麻煩,因為你要不停地滾動.如果數組是分配在堆上的話,你甚至不能在watch窗口中展開數組元素.對此,有一個解決辦法。你可以使用(array+
如果你的數組是在堆上,你可以在watch窗口中將它展開,但是要查看某個特定範圍的值,用法稍有不同:((T*) array +
如果你在用MFC,並使用其中的'array'容器,像 CArray, CDWordArray,CStringArray等等。你當然可以使用同樣的過濾方法。除此之外,你必須查看array的m_pData成員,它是保存資料的真實快取。
技巧5:避免進入不必要的函數
很多時候,你在調試代碼時可能會進入到你想跳過的函數,像構造函數,賦值操作或者其他的。其中最困擾我的是CString建構函數。下面是一個例子,當你準備單步執行take_a_string()函數時,首先進入到CString的建構子。
void take_a_string(CString const &text){}void test_string(){ take_a_string(_T("sample"));}
幸運的是可以告訴調試器
幸運的是可以告訴偵錯程式。實現它的方法也已經改變了,回到使用VS6的日子,通常是透過autoexp.dat檔案來指定的。 Vistual Studio 2002改成了使用註冊表設定。想要跳過一些函數,你需要在註冊表裡添加一些值(詳情如下):實際位置取決於你使用的Vistual Studio版本和作業系統平台(x86或x64,因為註冊表只能在64位的Windows下瀏覽)值的名字是數字,代表規則的優先權;數字越大,優先權越高。值資料是一個正規表示式的REG_SZ值,用於指定怎樣過濾和執行。
為了避免進入任何CString方法,我添加了下面的規則:
有了這個,即使你強制進入上例中的take_a_string(),調試器也會跳過CString的構造函數。 技巧6:從程式碼啟動偵錯器Launch the debugger from code你可能很少需要將偵錯器附加到程式中,但你不能在Attach視窗這樣做(可能因為中斷太快而沒有捕捉到) ,你也不能一開始就在調試器中啟動程式。你可以在程式中產生中斷給偵錯器一個機會透過呼叫內部的_degbugbreak()來附加。void break_for_debugging() { __debugbreak(); }實際上還有其他的方法來完成,例如觸發中斷3,但這僅僅適用於x86平台(C++64位元不再支援ASM)。另外還有DebugBreak()函數,但它的使用不太簡便,所以這裡推薦使用內部方法。
__asm int 3;
程式運行內部方法時會停止運行,這時你就有機會將偵錯器附加到該進程。
技巧7:在output視窗列印
🎜透過呼叫DebugOutputString可以在調試器的output視窗顯示一段特定的文字。如果沒有附加的偵錯器,該函數什麼都不做。 🎜🎜🎜🎜技巧8:隔离内存泄漏
内存泄漏是在原生开发中的一个很重要的问题,要检测内存泄漏是一个很严峻的挑战,尤其是在大型项目中。Vistual Studio可以提供检测内存泄漏的报告,还有其他的一些应用程序(免费的或商业的)也可以帮助你检测内存泄漏.有些情况下,在一些内存分配最终会导致泄漏时,可以使用调试器去中断。但是你必须找到可再现的分配编号(尽管没那么容易)。如果能做到这一点,执行程序时调试器才会中断。
我们来看下面的代码,分配了8个字节,却一直没释放分配的内存。Visual Studio提供了造成内存泄漏的对象的报告,多运行几次,会发现一直是同一个分配编号(341)。
void leak_some_memory() { char* buffer = new char[8]; } Dumping objects -> d:\marius\vc++\debuggingdemos\debuggingdemos.cpp(103) : {341} normal block at 0x00F71F38, 8 bytes long. Data: < > CD CD CD CD CD CD CD CD
在一个特定的(可复现的)位置中断的步骤如下:
确定你有足够的关于内存泄漏的报告模式(参考 使用CRT库检测内存泄漏)
多次运行程序直到你能在程序运行结束后的内存泄漏报告里找到一个可复现的分配编号,例如上个例子中的(341)
在程序一开始的地方设置一个断点以便你能够尽早地进行中断。
当最初的中断发生时,watch窗口的Name栏里会显示:{,,msvcr90d.dll}_crtBreakAlloc,在Value栏里写入你想要查找的位置编号
继续调试(F5)
程序执行到指定位置会停止,你可以使用调用栈被指引找到被该位置触发的那段代码。
遵循这些步骤, 在上个例子中,使用分配的编号(341)就可以识别内存泄漏的起因。
技巧9:调试发行版
调试和发布是两个不同的目的。调试配置是用于开发的,而发布配置,顾名思义,是用来作为程序的最终版本,因为它必须严格遵循发布的质量要求,该配置包含优化部分和调试版本的中断调试的设置。而且,有时候,要像调试调试版本一样去调试发行版。要做到这一点,你需要在配置里做一些改变。但是这种情况下,你就不再是在调试发行版,而是调试和发行的混合版
你还应该做一些事儿,以下是必须要做的:
配置C/C++ >General>Debug Information Format 应该为 “Program Database(/Zi)”
配置C/C++ >Optimization>Optimization 应该为”Disabld(/Od)”
配置Linker>Debugging>Generate Debug Info 应该为”Yes/(DEBUG)”
如图所示:
技巧10:远程调试
另一个重要的调试就是远程调试,这是一个更大的话题,多次被提到,这里我只做一下简单的概括:
你需要在远程机器上安装远程调试监控
远程调试监控必须以管理员身份运行,并且用户必须属于管理员组
在你运行监控时,会开启一个新的服务,该服务的名字必须用Visual Studio的Attach to Progress窗口的Qualifier组合框的值。
远程和本地机器上的防火墙必须允许Visual Studio和远程调试监控之间能够通信
想要调试,PDB文件是关键;为了能够让VisualStudio自动加载它们,必须满足以下条件:
1)本地的PDB文件必须可用(在远程机器的相同路径下放置一个对应的模块)。
2) 远程机器上的托管PDB文化必须可用。
结束语
Ivan Shcherbakov那篇文章和我这篇文章提到的调试技巧,在大多数的调试问题中都是必不可少的。
更多分享Visual Studio原生开发的10个调试技巧相关文章请关注PHP中文网!