您的位置:首页 > 其它

VC中的一些调试技巧

2006-04-27 00:53 417 查看
毕业快一年,做了2个项目,都是在别人的代码上作开发,苦不堪言:bug实在是太多。这一年中有大半的时间是在改别人的bug,也积累了一些经验,和大家分享。我的方法大多数都来自《Windows程序调试》(英文名DebuggingWindowsPrograms)。那本书里讲了很多方法,我只挑对我自己帮助最大的:
1.调试内存破坏。
这种bug的表现就是不定时,不定地方的崩溃。这种bug我一共碰到2次,每一次都花了很长时间,尤其是第二次,花了大家三天时间。其原因是堆(heap)被破坏掉了。
我的方法是这样的:在可能出现问题的地方加上对堆的检查,用如下代码:
//Getthecurrentstateoftheflag

//andstoreitinatemporaryvariable

inttmpFlag=_CrtSetDbgFlag(_CRTDBG_REPORT_FLAG);

//TurnOn(OR)-Keepfreedmemoryblocksinthe

//heap’slinkedlistandmarkthemasfreed

tmpFlag|=__CRTDBG_CHECK_ALWAYS_DF;

//Setthenewstatefortheflag

_CrtSetDbgFlag(tmpFlag);

int*nn=newint;

deletenn;

//TurnOff(AND)-prevent_CrtCheckMemoryfrom

//beingcalledateveryallocationrequest,Itwillcausemuchtime

tmpFlag&=~_CRTDBG_CHECK_ALWAYS_DF;

//Setthenewstatefortheflag

_CrtSetDbgFlag(tmpFlag);

如果之前堆已经坏掉了,那么程序(Debug版)就会在分配内存的地方中断,在这儿是[code]int*nn=newint;
[/code]
第一次破坏堆的代码是这样:

typedefstructaa

{

inta;

}AA;

AAs
;

inti=0;

for(i=0;i<n;i++)

{



for(i=0;i<n;i++){…}



s[i].a=0;

}

内外都使用i做循环变量,就这样把堆破坏了。

[code]第二次的代码比较隐蔽,我先是使用map文件找到了崩溃的地方,但是一看是一个window的API,就放过了,后来还是再用上面的方法,又定位到那个API:
GetFileAttributesEx(szFile,GetFileExInfoStandard,&attributes);[/code]
发现szFile这个参数有问题,是一个不合法的文件名,然后再调用这个API之前作文件名的合法检查,就没事了。呵呵,真是这个API干的,看来我们还是不能让Microsoft什么都做。[code]
[/code]
2.查找memoryleak

可以通过内存分配号来查找memoryleak,方法是这样的(我以前写的+msdn,就不翻译了):
YoucanseetheinformationbelowinyourVCoutputwindowiftheapphavememoryleaks[code]
[/code]
Detectedmemoryleaks!

Dumpingobjects->

{18}normalblockat0x00780E80,64byteslong.

[code]
Data:<>CDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCD
[/code]
Objectdumpcomplete.


IfyoucannotseethedumpinformationinVC,youcanaddthecodetothefilecrtdbg.h:

#ifdef_DEBUG
#define_CRTDBG_MAP_ALLOC
#define_INC_MALLOC
#include<stdlib.h>
//customfunctionsdeclaration(ATL+BETAversionproblems)
extern"C"
{
void*__cdecl_alloca(size_t);
#definealloca_alloca
}

#endif

Asyoucansee,_CrtDumpMemoryLeaksgivesyoumuchmoreusefulinformationwhen_CRTDBG_MAP_ALLOCisdefined.Without_CRTDBG_MAP_ALLOCdefined,thedisplayshowsyou:

thememoryallocationnumber(insidethecurlybraces).

thetypeofblock(normal,client,orCRT).

thememorylocationinhexadecimalform.

thesizeoftheblockinbytes.

thecontentsofthefirst16bytes(alsoinhexadecimal).

Youcanrunyourprogramtwiceinthesameway,thenyoucanfindthatthememoryallocationnumberoftheleakedmemoryisalwaysthesame,soyoucanusethememoryallocationnumbertofindthememoryleak;directlytosay,youcanbreaktheprogrambymemoryallocationnumber.
ThebelowistakenfromMSDN(DetectingandIsolatingMemoryLeaksUsingMicrosoftVisualC++)

SettingaBreakpointonaMemoryAllocationNumber

Thefilenameandlinenumberinthememoryleakreporttellyouwhereleakedmemoryisallocated,butknowingwherethememoryisallocatedisnotalwayssufficienttoidentifytheproblem.Oftenanallocationwillbecalledmanytimesduringarunoftheprogram,butitmayleakmemoryonlyoncertaincalls.Toidentifytheproblem,youmustknownotonlywheretheleakedmemoryisallocatedbutalsotheconditionsunderwhichtheleakoccurs.Thepieceofinformationthatmakesitpossibleforyoutodothisisthememoryallocationnumber.Thisisthenumberthatappearsincurlybraces,afterthefilenameandlinenumberwhenthosearedisplayed.Forexample,inthefollowingoutput,“18”isthememoryallocationnumber.Itmeanstheleakedmemoryisthe18thblockofmemoryallocatedinyourprogram.

Detectedmemoryleaks!

Dumpingobjects->

C:/PROGRAMFILES/VISUALSTUDIO/MyProjects/leaktest/leaktest.cpp(20):{18}

normalblockat0x00780E80,64byteslong.

[code]
Data:<>CDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCD
[/code]
Objectdumpcomplete.

TheCRTlibrarycountsallmemoryblocksallocatedduringarunoftheprogram,includingmemoryallocatedbytheCRTlibraryitselforbyotherlibraries,suchasMFC.Therefore,anobjectwithallocationnumbernwillbethenthobjectallocatedinyourprogrambutmaynotbethenthobjectallocatedbyyourcode.(Inmostcases,itwillnotbe.)

Youcanusetheallocationnumbertosetabreakpointatthelocationwherememoryisallocated.Todothis,setalocationbreakpointnearthestartofyourprogram.Whenyourprogrambreaksatthatpoint,youcansetsuchamemoryallocationbreakpointfromtheQuickWatchdialogboxortheWatchwindow.IntheWatchwindow,forexample,typethefollowingexpressionintheNamecolumn:

_crtBreakAlloc

Ifyouareusingthemultithreadeddynamic-linklibrary(DLL)versionoftheCRTlibrary(the/MDoption),youmustincludethecontextoperator,asshownhere:

{,,msvcrtd.dll}_crtBreakAlloc

Now,pressRETURN.ThedebuggerevaluatesthecallandplacestheresultintheValuecolumn.Thisvaluewillbe–1ifyouhavenotsetanybreakpointsonmemoryallocations.ReplacethevalueintheValuecolumnwiththeallocationnumberofthememoryallocationwhereyouwanttobreak—forexample,18tobreakattheallocationshownintheoutputearlier.

Afteryousetbreakpointsonthememoryallocationsinwhichyouareinterested,youcancontinuedebugging.Becarefultoruntheprogramunderthesameconditionsasthepreviousrunsotheallocationorderdoesnotchange.Whenyourprogrambreaksatthespecifiedmemoryallocation,youcanlookattheCallStackwindowandotherdebuggerinformationtodeterminetheconditionsunderwhichthememorywasallocated.Ifnecessary,youcancontinueexecutionoftheprogramfromthatpointtoseewhathappenstotheobjectandperhapsdeterminewhyitisnotproperlydeallocated.(SettingaDatabreakpointontheobjectmaybehelpful.)

Althoughitisusuallyeasiertosetmemoryallocationbreakpointsinthedebugger,youcansettheminyourcode,ifyouprefer.Tosetamemoryallocationbreakpointinyourcode,addalinelikethis(forthe18thmemoryallocation):

_crtBreakAlloc=18;

Asanalternative,youcanusethe_CrtSetBreakAllocfunction,whichhasthesameeffect:

_CrtSetBreakAlloc(18);


AstoyourAPP,youmustrenamethefilemsvcrtd.dlltolettheAPPusethedllfileinthefolderSystem32.Youshouldadd
{,,msvcrtd.dll}_crtBreakAlloc
totheWatch.You’duseStepIntoandsetthebreakpoint.



3.跨进程调试。
比如两个程序APP1和APP2,由APP1调起APP2,这时候你想调试APP2.可以这样做,在APP2的initinstance()中合适的地方加上一句ASSERT(FALSE);让APP2停下来,然后用VCAttach过去,就可以到APP2对应的cpp里去设断点了。

《Windows程序调试》还讲了很多方法:比如设高级断点,远程调试,map文件等等,都是非常有用的方法,我在这儿都列出来,未免太罗嗦了。对于原理方面的东西,希望大家多去看书和msdn.再就是大家一定要写出高质量的代码,多加一句检查,可能会省去别人三天的时间。
有不妥当的地方,希望大家指正。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: