[转]两个经典的windbg调试案例,值得学习。
?
1. 調試Bug的神兵利器:通過WinDbg條件斷點收集Log
原文地址:http://blogs.msdn.com/yizhang/archive/2009/03/30/bug-windbg-log.aspx
調試Bug的神兵利器:通過WinDbg條件斷點收集Log
前段時間花了幾天一直在用WinDbg調試一個比較棘手的Bug。這個Bug是C# Team那邊發現的,他們的Testcase跑大概10分鐘左右會出一個在CLR內部的ASSERT。比較難調試的主要原因在于ASSERT表明一個全局的數據結構出現了問題,本來不應該用完的數組卻已經用完了(因為按照設計,這個數組是邊使用邊清理的,是不會用完的)。初步想到的有下面幾種方案來調試:
1. 設置數據斷點
2. 一步一步調試
3. 添加Log代碼
設置數據斷點的主要問題是不太好確定到底是因為什么原因導致的數據結構問題,而且因為是數組被用完,很難將是到底是哪一個數組元素的加入導致了數組被全部占用,因此無法通過設置數據斷點的方法來調試。一步一步的調試顯然也沒法解決問題,因為這個Testcase本身要跑十分鐘,可以想象單步調試運行十分鐘的程序會花費多長時間。因此兩個方案都被我否決。添加Log代碼其實是可以的,只是需要修改代碼,每次修改之后需要重新編譯代碼,然后需要在目標機器上安裝,而且C#使用的CLR的Branch并非我們正在開發的Branch,需要重新下載源代碼,相對比較麻煩。最后為了解決這個問題,我采取的方法是使用WinDbg的條件斷點+Log的方式。大致的方法如下:
第一步:在一個或者多個可疑處設置斷點
bu address “command”
bu是WinDbg中的設置Unresolved Breakpoints命令,用起來比較方便,我比較喜歡用。address就是你所要斷的代碼地址,可以是函數開始,也可以是某一行。Command非常重要,它表示了WinDbg在每次斷到address的時候都要執行的命令,不同命令用分號隔開,如:
.echo [Function A]; dv this; kb; g
這幾條命令意思是:打印[Function A],打印this指針的值,打印當前調用棧,然后繼續執行。大家可以根據實際情況添加一些其他命令打印一些自己所需要的信息。通過上面這套命令打印的內容大致如下:
[FunctionA]
this = 0xABCDEFG
module!FuncA
module!FuncB
module!FuncC
…
可以看出,這條斷點如果反復被斷,那么在WinDbg的命令窗口中便會把每次斷點被Hit的相關信息通過剛才定義的命令打印出來。如果定義了很多這樣的斷點,那么在命令窗口中就會把整個程序執行的情況打印出來,起到Log的作用,而且可以顯示調用棧等信息,比一般的Log要強大許多。
第二步:設置Log
缺省情況下,WinDbg的Buffer大小是有限的,如果程序運行時間比較長,那么Buffer可能會不夠,我們通過條件斷點打出的信息會被截斷。幸好,WinDbg提供了將命令窗口的內容輸出到Log中的功能。選擇Edit->Open/Close Log File菜單項,WinDbg會顯示如下對話框:
在這個對話框里面輸入你想要保存的Log文件名即可。如果是添加新的內容而不是覆蓋原有的,則勾上Append。
第三步:分析Log
當獲得了Log信息之后,下一步就需要分析Log的內容了,這是一件需要耐心、對數據的敏感、以及一點點運氣的事情。分析的時候可能發現Log的信息不足,這時就需要添加新的斷點或者修改打印的信息,重新收集Log,再加以分析,直到Log信息足夠為止。這時WinDbg設置條件斷點的優勢就出來了,因為不需要修改代碼,編譯代碼,部署代碼這樣的一個過程,而是只需要鍵入不同的命令而已。經過幾次調整斷點位置和打印的信息并重新收集Log,我最終通過分析發現這個Bug是只有可能在特定情況下RCW沒有被GC,并且創建線程退出的時候才會出現,具體的內容因為涉及到.NET 4.0中還沒有發布的新功能,這里就不多說了。可以看到,如果采用常規的方法,對于這種在特定的條件下才會重現的問題是很難發現的。
總之,使用WinDbg來設置條件斷點,打印相關信息,并且輸出到Log文件是一種非常強大的調試方法,可以調試一些非常復雜的Bug,而且具有不需要修改代碼的靈活性,可以自由定義自己想需要打印的信息和斷點設置的位置,主要的缺點是方法稍顯復雜,不過如果適應了之后還是很方便的。我強烈推薦大家在遇到比較復雜的Bug的時候,可以嘗試使用一下這種方法,可能具有意想不到的效果哦。
?
?
如果一個程序跑10000次只失敗一次,你會怎么調試?
原址:http://blogs.msdn.com/yizhang/archive/2009/08/28/9887951.aspx
CLR小組中存在著大量的回歸測試,這些回歸測試會定期執行來發現CLR中的Bug,Developer在Checkin之前,也需要執行這些測試的一部分(大概是10小時左右,如果全部跑的話估計要好幾天)。這些測試對于保證CLR的質量是至關重要的。有時候,這些測試會偶爾失敗,比如跑100次失敗大概一到兩次,有些極端的例子甚至是10000次才失敗一次。像這種問題通常是很難調試的。在前面調試Bug的神兵利器:通過WinDbg條件斷點收集Log這篇文章中,我講到了如何通過條件斷點收集各種信息來判斷Bug究竟出在哪里。但是,這個方法還是不太管用,因為它不能夠反復執行某個程序。下面我要講一種技巧可以用來調試類似這樣的問題,這種技巧主要適用于下面幾種情況:
#2和#4決定了一步步調試基本上是不可能的。#1和#3則意味著我們必須得使用條件斷點來收集信息來判斷代碼的錯誤,因為直接調試出錯的位置是不可行的。下面了我來講一下如何用CDB(其實就是WinDbg的無UI版本,WinDbg=CDB+UI)來做到:
我們先假設我們需要調試的程序叫做Hello.exe,每次出問題的現象是,調用某個函數Hello!Func()的時候,其參數arg為NULL。Arg這個變量是由某個全局變量g_arg傳入而來。我們可以通過硬件的數據斷點來查看每次將g_arg賦值為NULL的情況(當然了,賦值為NULL并不代表是錯誤,只有傳入Hello!Func的時候為NULL才是錯誤)。程序一般要跑10000次才可能發現問題。使用下面的命令行可以做到反復收集Func1(Func2、Func3因為類似,這里就不列出了)執行時候的g_arg的值并放入Log文件中,并且如果發現調用Hello!Func的時候arg參數為NULL,則停止程序:
for /L %i in (1, 1, 10000) DO CDB.exe -c "bu Hello!Func \".echo Inside Hello!Func; dv; .if (poi(arg)!=0) { g } \"; ba w4 Hello!g_arg \“.if (poi(Hello!g_arg)==0) { .echo g_arg changes to NULL; kb; }\”; g" -G -logo debug.log Hello.exe
我們來簡單分析一下:
除了用-c指定初始的命令之外,也可以使用-cf來指定一個文件包含任意條CDB命令,如果CDB命令較多,可以采用這種方法。
本文說道的方法是比較有效的,我自己曾經使用過這種方法解決過不少比較棘手的問題。如果碰到了此種需要運行10000次才能重現問題的Bug,不妨試一下本文的方法。
轉載于:https://www.cnblogs.com/hhuai/archive/2010/03/01/1675383.html
總結
以上是生活随笔為你收集整理的[转]两个经典的windbg调试案例,值得学习。的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: c# 类的基本知识,未完,待续
- 下一篇: gridview DataFormatS