程序调试技术ppt课件_第1页
程序调试技术ppt课件_第2页
程序调试技术ppt课件_第3页
程序调试技术ppt课件_第4页
程序调试技术ppt课件_第5页
已阅读5页,还剩18页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

C/C+程序调试技术,东方网力技术培训,1,内容提要,中断和异常调试断点常见调试器功能源代码级别的主动调试手段C+异常和win32的SEH常见程序问题调试,性能分析和优化C/C+语言的一些陷阱一些常见平台差异实例分析阅读程序的技巧几点建议,2,中断和异常,所谓中断是指CPU对系统发生的某个事件做出的一种反应,CPU暂停正在执行的程序,保留现场后转入去执行相应的中断处理程序,执行完中断处理程序后再返回中断现场继续执行被打断的程序。中断可分为三类:1、第一类是由CPU外部引起的,称作中断,如I/O中断、时钟中断、控制台中断(重启动中断,关机中断,)等。2、第二类是CPU的内部事件,称作异常,如CPU故障、程序故障(非法操作码、地址越界、浮点数溢出、除0错误等)。3、第三类是由程序为了使用某些系统服务而主动引发,称作陷入(也叫软中断),如现在x86CPUint3指令,dos下著名的int13、int21等。程序调试断点就是通过int3指令实现的。4、x86CPU的单步中断特性(TRAPFLAG被设置后,执行每条指令后都会发生此中断)。程序的指令级别的单步执行应该就是用单步中断实现的。中断向量表IDT,即中断处理程序的入口地址表。第三类软中断事件(异常)处理过程,以win32平台处理int3指令为例:1、保留现场,进程/线程被挂起,进入操作系统的处理程序(执行系统int3的中断处理程序,下面称为系统)。2、发生中断的进程如果处于被调试状态,则系统把int3事件通知给调试进程,尝试由调试进程处理int3事件。3、尝试让进程自己处理int3事件(参考C+的异常以及WindowsStructuredExceptionHandling知识)。4、如果2、3情况都没处理int3事件,则系统弹出异常对话框,通知用户进程发生了异常(此时用户可以使用调试器再来处理int3事件转入2)。第一类中断一般直接由系统处理,然后可能再分发给需要处理的用户进程。第二类中断一般处理顺序为1-3-2-4.i,3,调试断点,调试断点一般是通过int3指令实现的。调试器设置断点原理(以VC调试器为例):调试器首先找到被调试进程需要设置断点的指令地址(调试版本根据源代码设置的断点也会被转化为实际的指令地址),然后把该地址的1byte数据记录到一张对应表里,接着把这1byte改写为0 xCC(即int3指令码)。这样当程序被调试运行的时候,在断点位置的指令其实就是int3指令,参照上一节的int3中断事件处理过程,就可以明白调试器捕获断点的工作机理。取消断点时则把对应表里记录的1byte回写到被调试进程。常用跟踪相关动作都是通过断点方式实现的对于stepto、stepover、stepin、stepout等调试器都是通过在要运行的下一个地址处先设置一个临时断点,然后调试运行程序来实现的。其它断点实现类似。程序主动调试断点,ASSERT宏,ASSERT(false)即等效为一条int3指令。理解和使用条件断点、单次断点、固定次数断点等。怎么在动态库(静态load和动态load)里设置断点,VC的AdditionalDLLs选项。怎样在模板代码、内联函数、静态库代码里设置断点。(调试器问题,怎样在不能设置断点的代码位置设置断点)。带调试信息模块和不带调试信息模块共存情况的调试方法,如VB、浏览器、MediaPlayer使用我们需要调试的.ocx、.dll文件等。,4,常见调试器功能-具体参考VC,gdb等调试器的用户手则,查看和修改变量,监视变量查看和修改内存,监视内存查看和修改寄存器,监视寄存器全局变量写监视CallStack(调用堆栈)的查看更改指令指针寄存器EIP,实现调试时强行跳转(VC的SetNextStatement命令同此)查看源码对应的汇编指令/机器码,5,源代码级别的主动调试手段,编译时刻防御性编程-C+契约(contract)1、静态assert(编译时刻断言)-STATIC_ASSERT(),must_have_base()2、一些有用的静态判断:boolIS_INT_TYPE(T)、IS_SIGNED_TYPE(T)、GET_INT_MAX_VALUE(T),见npdebug.h“调试时刻防御性编程宏ASSERT()、VERIFY()、TRACE()等。MFC的AfxIsValidAddress()、AfxIsValidString()等。1、程序应该大量使用ASSERT()宏,保证ASSERT()覆盖没有正常处理的所有程序逻辑分支。2、所有没有完成的函数和逻辑分支应该写上ASSERT(false)以防止以后遗忘。运行/发布时刻防御性编程即程序的各种边界、容错、健壮性处理等。,6,C+异常和win32的SEH,什么情况下建议使用异常a、当使用第三方提供的库,调用该库接口的代码需要放在异常块里面(对于第三方库内部有独立线程或独立进程时,目前我还没想到好的办法增强程序健壮性)。b、构造函数可能失败的情况必须使用异常。c、在使用异常可以大大简化程序逻辑的地方也可以使用异常。d、内存分配可能失败的地方。异常不可能全面代替错误处理。不可使用异常来做一般的逻辑控制。宏NP_BEGIN_CATCH_ALL()和NP_END_CATCH_ALL(),7,常见程序问题调试,内存泄漏内存溢出/越界多线程死锁发布版本的调试分析只在发布版本才会出现的问题多平台调试,8,内存泄漏,尽量减少对new和delete,malloc和free的使用,尽量使用C+的自动对象,如std:string,std:vector,classCAutoPtr,classCAutoObj等。检查低级错误,通查程序里面的new、delete、malloc、free等内存操作,delete和delete是否混用如果对象有引用计数,查看计数是否有问题。可以使用调试器分析是谁在申请内存而没释放,VC里面可以直接在C/C+运行库源代码里面设断点,其它平台通过重载全局的new、delete或者使用hook技术钩住malloc、free后,再在重载/钩子函数里设断点。用VC自带的内存检测机制(调试运行程序,正常退出后检查内存信息)。使用VisualLeakDetector。其它方法,如打印、程序折半法等。,9,内存溢出/越界,得到写越界/出错的内存地址(分为全局heap内存和函数局部stack内存),并监控该内存的内容,接着单步执行程序,找到引起该内存变化的语句,此语句就是导致内存越界的直接原因,然后再深入分析,找出真正bug。,10,多线程死锁,理解程序发生死锁的机理。建议程序里面的线程同步对象全部使用npsync.h、ILocker.h、NPRWLock.h等代码库里面的函数,structtagOSMutex:lockedThreadID,即专为解决死锁而设计。程序发生死锁后利用调试器的线程切换和堆栈查看能力配合lockedThreadID信息,一般来说可以很快找到死锁原因。如果死锁实在不能避免,建议改造程序逻辑,使用TryLock、SendTimeout等方式。,11,发布版本的调试,使用map文件。使用手工插入软断点int3,直接查看汇编指令。查看程序CPU、内存、各种句柄使用情况(windows的任务管理器,linuxtop命令)。原始方式:打印,printf()、OutputDebugString(),12,分析只在发布版本才会出现的问题A,首先需要理解发布版本和调试版本的不同。发布版本没有任何调试相关代码,检查是否有错用VERIFY为ASSERT的地方。发布版本一般的内部不会有初始化动作,而在调试版本,编译器为了便于调试,一般会对内存做初始化。如VC在调试版本会用0 xCC初始化所有自动变量,用0 xCD填充new出来的内存,用0 xDD填充delete的内存,用0 xFD填充受保护的内存(动态分配内存的前后地址),以上值都是比较大的奇数,这样便于查错。,13,分析只在发布版本才会出现的问题B,发布版本会优化掉一些不必要的操作和变量。如优化掉一些局部变量就会引发一些只会在发布版本发生的错误,如:【理解x86体系的CPU的堆栈地址是递减的,著名的c语言buffer溢出攻击即是基于此理】inta;charch4;/*/变量a没有使用或者只是在调试版本使用,当对ch发生向后越界操作时(小于4bytes的越界),在调试版本因为有变量a,不会产生错误,但发布版本inta可能被优化掉,则会引发堆栈错误。检查有使用#ifdef_DEUBG的地方是否会导致调试版本和发布版本有逻辑差异。也有可能因为使用系统库的不同,如MFC库,引发一些差异性错误。,14,多平台调试,若程序不是特别平台相关,应尽量让程序可以在多个平台下编译运行,比如在linux平台不易查的问题,可以到win32平台下来查。尽量使用标准C库、stl以及codelib里面的跨平台库。,15,性能分析和优化A,利用x86的RDTSC指令进行精确的时间统计(在多核CPU系统下使用该指令需要小心)。对程序进行时间复杂度统计。利用编译器提供的统计功能,如gcc的GPROF(参考Makefile)。使用调试器配合软断点(int3)查看发布版本的汇编代码,了解代码在发布版本里对应的实际执行指令。优化,找到关键问题所在,记得二八原则,即程序80%的时间在执行20%的代码。写一个模块(函数、类)的时候,时刻想到是可读性重要,还是性能重要。分清楚什么时候该用ASSERT,什么时候该用错误处理逻辑,常见的做法是在最底层函数使用ASSERT声明所有的非法情况,在上层函数使用错误逻辑处理,这样既保证了正确性,也获得了发布版本的效率。例:在TYPE*CAutoObj:operator-()对对象是否合法的ASSERT检测,为了效率,此处显然不应该用错误处理(非法时返回NULL)。,16,性能分析和优化B,没必要在某些问题上耗费我们的时间,现代编译器对于有些优化比你做的更好,如:a/2-a1,没必要把整数乘法变成位移,破坏了程序可读性,这件事情编译器会帮你做。时刻谨记编译时刻常量(C+摸板元编程)是不会耗费任何执行时刻开销的,比如定义#defineXXX(12)就比#defineXXX0 x8可读性好。尽量减少大数据拷贝动作,在读磁盘和内存缓存之间做权衡。减少网络访问次数和传输的数据量。怎么节约内存和避免内存碎片【在服务器程序和内存受限系统中这是个重要问题】1、内存池、重载new和delete,classSameSizeMemMgr。2、在堆栈够用的情况下尽量使用堆栈内存,即尽量使用局部对象这也有利于编译器优化。如当一个数值的长度是常数编译时刻确定的数,则一般使用局部数组。推荐大家尽量使用classCSmartBuf和classCSmartArray来定义局部数组对象。3、尽量减少new和delete的使用,建议使用自动对象包容摸板classCAutoObj。,17,C/C+语言的一些陷阱A,整数1、回环问题,怎样用tick统计时间长度;2、扩展问题,如16位扩展到32;3、位移问题,如:int3233不是我们想象的0,而是与int321等价,因为c/c+编译器为了效率直接使用了硬件移位,而很多硬件指令的位移就是这么做的。另:在32位平台下VC和gcc实现的INT64对于int6465结果为0而不是等价于int641。宏,别忘了在宏里面加括号,如:#defineXXX(a)a10则必须写为#defineXXX(a)(a)10)。宏不是函数。宏不是类型定义。C+的自动类型转换,如果不想被隐含使用,就在构造函数前面加上explicit,structAA(inti);structAA(inti,intb=0);是需要加explicit的两种形式。小心使用operatorTYPE()。对VC6for语句bug的修正#defineforif(0)(void)0);elsefor见msc_opt.h“。intelCPU浮点数和mmx问题,emms指令。比sizeof(ar)/sizeof(ar0)更好的数组长度计算子ARRAY_LEN,见“nprbase.h”。,18,C/C+语言的一些陷阱B,关于0的特殊用法,在C+里,0可以是int类型,也可以是任何指针类型,如NULL一般定义如下#defineNULL0,就会导致如:intindex=NULL;这样就可能有潜在逻辑错误的代码被编译过(正确应该是intindex=-1)。bool和BOOL问题,sizeof(bool)和sizeof(BOOL)可能不等,BOOL和int可能是同一个类型。std:string作为printf()的可变参数(C语言可变参函数语法陷阱)会导致程序崩溃。语言库提供的某些随机函数可能是非均匀分布的,如:rand()%100并不是在0,100)之间均匀分布,在需均匀分布的时候请使用RandNum()和RSort_()“QSort.h“,这些函数保证了伪随机数的均匀分布性。,19,一些常见平台差异,Win321、写socket绑定事件问题。2、WaitForMultipleObjects()事件数量限制。3、stlbug,见“npr_std_mender.h”。4、fopen(,“w”)文本模式问题,请使用“npfdk.h”。Linux1、线程没有优先级别,创建线程默认堆栈,20,实例分析,21,阅读程序的技巧,从功能、界面、通信协议及命令码入手查找关键代码(

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论