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