《GDB调试简易教程.ppt》由会员分享,可在线阅读,更多相关《GDB调试简易教程.ppt(47页珍藏版)》请在taowenge.com淘文阁网|工程机械CAD图纸|机械工程制图|CAD装配图下载|SolidWorks_CaTia_CAD_UG_PROE_设计图分享下载上搜索。
1、GDB调试方法与技巧调试方法与技巧刘鹏昊GDB简介:简介:调试器(例如GDB)能让你观察一个程序在执行时的内部活动,或者程序出错时发生了什么GDB主要能为你做四件事情:运行你的程序,设置所有的能影响程序运行的东西保证你的程序在指定的条件下停止当你程序停止时,让你检查发生了什么改变你的程序。那样你可以试着修正某个bug引起的问题,然后继续查找另一个bug启动:启动:最通常的命令就是使用一个参数:$(m68k-linux-)gdb你还可以同时为你的执行文件指定一个core文件:$gdbcore你也可以为你要执行的文件指定一个进程号:$gdb常用启动参数:常用启动参数:-symbols(-s)从中读
2、去符号-x执行gdb命令,在指定的文件中存放着一序列的gdb命令,就象一个批处理-directory(-d)指定路径。把加入到搜索源文件的路径中常用启动参数:常用启动参数:-quiet(-q)安静模式,不输出介绍和版权信息-x执行gdb命令,在指定的文件中存放着一序列的gdb命令,就象一个批处理-directory(-d)指定路径。把加入到搜索源文件的路径中结束:结束:quit直接退出gdbdetach放弃连接Shell命令:命令:shell启动一个shell执行,不用退出GDB就可以执行一个shell命令makemake-args使用make-args进行make相当于shellmakema
3、ke-argsGDB命令命令命令输入技巧:命令输入技巧:可以把一个gdb命令缩写成开头几个字母,如果这没有二意性你可以直接回车来运行。如果有不止一个选择的话,你还可以使用TAB键让gdb给你完成接下来的键入,或向你显示可选择的命令使用使用helphelpclass显示某一类命令的列表$helpstatusStatusinquiries.Listofcommands:show-Genericcommandforshowingthingssetwithsetinfo-GenericcommandforprintingstatusGDB命令命令(使用使用help)helpCOMMAND列出某个命令的
4、使用方法complete列出所有以ARGS开头的命令info(可以缩写为i)用来显示你程序的状态。比如,你可以使用infoargs列出你程序所接受的命令行参数。使用inforegisters列出寄存器的状态。show与info相反,show命令用来显示gdb自身的状态例如showversion显示版本号,showcopying显示版权信息GDB下运行程序下运行程序程序编译程序编译:当你在gdb下运行程序时,你必须先为gdb准备好带有调试信息的可执行文档。为了高效的调试一个程序,你需要使用编译器来产生附带调试信息的可执行代码这些调试信息存储在目标文件中;描述了变量数据类型和函数声明,在源文件代码
5、行和执行代码之间建立联系。为产生调试信息,当你使用编译器时指定-g选项,就可以为你的程序产生带有调试信息的可执行代码GDB下运行程序下运行程序Run命令命令:用于启动你的程序,使用命令前必须先指定你程序的名字(用gdb的命令行参数)或使用file命令,来指定文件名工作路径:工作路径:每次用run命令来运行程序时,程序将继承gdb的当前工作目录。而gdb的工作目录是从它的父进程继承而来的(一般是shell)。但你可以自己使用cd命令指定工作目录。gdb的工作目录就是它去寻找某些文件或信息的途径。cdDIRECTORY把gdb的工作目录设为DIRECTORYpwd打印输出当前目录GDB下运行程序下
6、运行程序调试一个已经运行的程序调试一个已经运行的程序:attach这个命令把一个已经运行的进程(在gdb外启动)连接入gdb,以便调试。PROCESS-ID是进程号。当然要使用attach命令的话,你的操作系统环境必须支持进程。另外你还要有向此进程发信号的权力。交叉调试环境:目标板:gdbserver:portattach调试端:targetremoteip:port停止调试:detachGDB下运行程序下运行程序调试一个多线程的程序调试一个多线程的程序:GDB会自动提示新线程创建thread各线程间进行切换infothreads查看已经存在的线程无论gdb何时中断了你的程序(因为一个断点或是
7、一个信号),它自动选择信号或断点发生的线程为当前线程。gdb将用一个格式为SwitchingtoSYSTAG的消息来向你报告。GDB下运行程序下运行程序调试多进程调试多进程:GDB对调试使用fork系统调用产生新进程的程序没有很多支持。当一个程序开始一个新进程时,GDB将继续对父进程进行调试,子进程将不受影响的运行。如果你在子进程可能会执行到的地方设了断点,那么子进程将收到SIGTRAP信号,如果子进程没有对这个信号进行处理的话那么缺省的处理就是使子进程终止。GDB下运行程序下运行程序调试子进程的技巧调试子进程的技巧:正常情况下,可以在子进程运行后正常情况下,可以在子进程运行后attach即可
8、即可如果需要调试子进程在启动过程中的问题,可以采取以下方法:如果需要调试子进程在启动过程中的问题,可以采取以下方法:父进程启动代码:pid=fork();if(0=pid)exec(“sh”“c”“child”);更改为pid=fork();if(0=pid)exec(“sh”“c”“gdbchild”);子进程的main函数中使用sleep进行延时,睡眠期间attach之断点断点断点的作用是当你程序运行到断点时,无论它在做什么都会被停止下来可以在行上,函数上,甚至在确切的地址上设置断点观察点是一种特殊的断点。它们在程序中某个表达式的值发生变化时起作用可以设置当程序被中断时显示的程序变量设置断
9、点设置断点(break point)使用break或简写成b来设置断点:breakfunction在某个函数上设置断点break+offsetbreakoffset当前行的前(后)offset行上设置断点breakfilename:linenum在文件的某行设置断点breakfilename:function在文件的某函数设置断点break*address在地址address上设置断点,可以在无调试信息的程序中设置断点设置断点设置断点(break point)break.ifCOND条件断点,当表达式COND非零时程序在断点停止例如break253ifi=10tbreak设置一个只会停止一次的
10、断点设置观察点设置观察点(watch point)watchEXPR为EXPR设置一个观察点,一旦EXPR被写入并发生变化,程序停止rwatchEXPREXPR被读取时,程序停止awatchEXPREXPR被读取或者被写入时,程序停止当程序运行到EXPR作用域以外的地方时,GDB将会自动删除此观察点,如果想继续观察,必须重新设置观察点。断点相关操作断点相关操作查看断点infobreakpointsinfowatchpointsinfobreakn删除断点cleardelete禁止、使能断点disablebreakpoints不带参数则禁止所有断点enabebreakpoints不带参数则使能所
11、有断点enablebreakpointsonce使能,并在程序停止后禁止enablebreakpointsdelete使能,并在程序停止后删除断点相关操作断点相关操作为断点设置条件conditionbNumEXPR为该断点设置条件conditionbNum取消该断点的条件忽略断点ignorebnumcount忽略该断点count次为断点设置命令列表mand-list.end断点被触发后可以执行指定的命令列表程序的恢复与单步调试程序的恢复与单步调试continue简写为c,恢复程序运行,直到下一个断点或程序结束step简写为s,单步调试,如果有函数调用,会进入该函数。进入函数的前提是,此函数被编
12、译有调试信息next简写为n,单步调试,如果有函数调用,不会进入该函数setstep-modesetstep-modeon打开step-mode模式,在进行单步调试时,不会因程序没有编译入调试信息而忽略“Stepin”。这个参数有很利于查看程序运行时的汇编代码setstep-modeoff关闭step-mode模式程序的恢复与单步调试程序的恢复与单步调试finish运行程序,直到当前函数完成并返回,打印函数返回时的堆栈地址和返回值及参数值等信息untillocation简写为u,继续运行程序直至跳出当前正在单步调试的循环体;加参数表示继续运行到代码的location处或者当前stackfram
13、e返回advancelocation继续运行直至location处处理信号处理信号infosignals/handle打印各种已经定义处理方法的信号列表handlesignalkeywords.定义各种信号的处理方法keywords:nostop当被调试的程序收到信号时,GDB不会停止程序的运行,但会打印信息通知用户收到这种信号stop当被调试的程序收到信号时,GDB会停止程序的运行print/noprint当被调试的程序收到信号时,GDB会/不会打印出一条信息passGDB不会拦截信号,留给应用程序处理nopass GDB拦截信号,不会让应用程序去处理多线程程序的停止和执行多线程程序的停止和
14、执行如果程序有多个线程,可以设置断点是否在所有的线程,或是在某个特定的线程上。break linespec thread threadno break linespec thread threadno if.linespec指定了断点设置的源程序的行号。threadno指定了线程的ID,注意,这个ID是GDB分配的,通过“infothreads”命令来查看正在运行程序中的线程信息。如果你不指定thread则表示断点设在所有线程上面。还可以为某线程指定断点条件。如:breakfrik.c:13thread28ifbartablim注意:当运行中的程序被当运行中的程序被GDB停止时,所有的运行线程
15、都会被停止。停止时,所有的运行线程都会被停止。这方便查看运行程序的总体情况。而在恢复程序运行时,所有的这方便查看运行程序的总体情况。而在恢复程序运行时,所有的线程也会被恢复运行,即使是在单步调试时线程也会被恢复运行,即使是在单步调试时查看程序堆栈查看程序堆栈backtrace可以简写为bt,打印当前的函数调用栈的所有信息frame n可以简写为f,切换到堆栈的第n层up/down在堆栈中向上/向下移动info frame打印更加详细的当前栈层的信息info args打印出当前函数的参数名及其值info locals打印出当前函数中所有局部变量及其值查看运行时的数据查看运行时的数据最常用的查看数
16、据方法最常用的查看数据方法print/FMT EXPR按照格式FMT打印表达式EXPR的值display/FMT EXPR自动按照格式FMT打印表达式EXPR的值例如:print/xi以十六进制格式打印i的值查看运行时的数据查看运行时的数据表达式:表达式:print和许多GDB的命令一样,可以接受一个表达式,GDB会根据当前的程序运行的数据来计算这个表达式,既然是表达式,那么就可以是当前程序运行中的const常量、变量、函数等内容。但是GDB不能使用调试程序中所定义的宏查看运行时的数据查看运行时的数据表达式:表达式:print和许多GDB的命令一样,可以接受一个表达式,GDB会根据当前的程序运
17、行的数据来计算这个表达式,既然是表达式,那么就可以是当前程序运行中的const常量、变量、函数等内容。但是GDB不能使用调试程序中所定义的宏在GDB中,可以随时查看以下三种变量的值:全局变量静态全局变量局部变量如果局部变量和全局变量发生冲突(也就是重名),一般情况下是局部变量会隐藏全局变量,也就是说,如果一个全局变量和一个函数中的局部变量同名时,如果当前停止点在函数中,用print显示出的变量的值会是函数中的局部变量的值。如果此时你想查看全局变量的值时,可以使用“:”操作符例如pf2.c:x或者pGetSize:x查看运行时的数据查看运行时的数据注意:注意:如果编译程序时开启了优化选项,那么在
18、用GDB调试被优化过的程序时,可能会发生某些变量不能访问,或是取值错误的情况。这个是很正常的,因为优化会对程序作一定的删改,调整程序的语句顺序,去除一些无意义的变量等,所以在GDB调试这种程序时,运行时的指令和源文件中指令有有不一样的可能,也就会出现预想不到的结果。这种情况下,需要在编译程序时关闭编译优化或者使用一种不同的调试信息格式。一般编译器都会支持几种这样的格式,如,GNU的C/C+编译器GCC,支持“-gstabs”选项,产生比COFF格式更高级的调试信息格式查看运行时的数据查看运行时的数据 数组或连续存储空间:数组或连续存储空间:调试程序过程中,可能会需要查看一段连续的内存空间的值。
19、比如数组的一段,或是动态分配的数据的大小。此时,可以使用GDB的“”操作符,“”的左边是第一个内存的地址的值,“”的右边则是要查看内存的长度例如,程序中有这样的语句:int*array=(int*)malloc(len*sizeof(int);于是,在GDB调试过程中,如下命令显示出这个动态数组的取值:p*arraylen的左边是数组的首地址的值,也就是变量array所指向的内容,右边则是数据的长度,其保存在变量len中,其输出结果,大约是下面这个样子的:(gdb)p*arraylen$1=2,4,6,8,10,12,14,16,18,20,22,24,26,28,30,32,34,36,38
20、,40如果是静态数组的话,可以直接用“print数组名”,就可以显示数组中所有数据的内容了查看运行时的数据查看运行时的数据输出格式:输出格式:x16进制d有符号整数u无符号整数o8进制t2进制a地址c字符常量f浮点数使用格式a可以查看地址在某函数内的位置,例如(gdb)p/a0 x54320$3=0 x54320查看运行时的数据查看运行时的数据查看内存:查看内存:x/nfuaddressn是一个正整数,表示显示的个数(每个显示值由u定义)f表示显示的格式,参见上面。如果地址所指的是字符串,那么格式可以是s,如果地址是指令地址,那么格式可以是i。u表示从当前地址往后每个请求的字节数,如果不指定的
21、话,GDB默认是4个bytes。u参数可以用下面的字符来代替,b表示单字节,h表示双字节,w表示四字节,g表示八字节。当我们指定了字节长度后,GDB会从指内存定的内存地址开始,读写指定字节,并把其当作一个值取出来。n/f/u三个参数可以一起使用。例如:命令:x/3xh0 x54320表示,从内存地址0 x54320读取内容,h表示以双字节为一个单位,3表示三个单位,x表示按十六进制显示。查看运行时的数据查看运行时的数据显示设置:显示设置:setprintaddresson/off打开关闭参数地址setprintarrayon/off打开后打印数组每个元素占一行,关闭后以逗号分隔各元素setpr
22、intelementsnumber设置打印数组的元素最大个数为numbersetprintnull-stopon/off打开时,打印字符串数组时,遇到null就不继续打印setprintprettyon/off打开时,使用print打印结构体会比较漂亮查看运行时的数据查看运行时的数据显示设置:显示设置:setprintunionon/off打开关闭是否显示联合体数据setprintobjecton/off在C+中,如果一个对象指针指向其派生类,如果打开这个选项,GDB会自动按照虚方法调用的规则显示输出,如果关闭这个选项的话,GDB就不管虚函数表了。这个选项默认是offsetprintstati
23、c-memberson/off是否打印对象中的静态成员setprintvtblon/off当此选项打开时,GDB将用比较规整的格式来显示虚函数表查看运行时的数据查看运行时的数据 历史数据:历史数据:当用GDB的print查看程序运行时的数据时,每一个print都会被GDB记录下来。GDB会以$1,$2,$3.这样的方式为每一个print命令编上号,所以可以使用这个编号访问以前的表达式,如$1。这个功能所带来的好处是,如果先前输入了一个比较长、复杂的表达式,如果还想再次查看这个表达式的值,就可以使用历史记录来访问,省去了重复输入查看运行时的数据查看运行时的数据GDB变量:变量:用户可以在GDB的
24、调试环境中定义自己的变量,用来保存一些调试程序中的运行数据。要定义一个GDB的变量很简单,只需使用GDB的set命令。GDB的环境变量和UNIX一样,也是以$起头。如:set$foo=*object_ptr一旦设置了环境变量,就可以在以后的使用中,直接对其賦值。环境变量没有类型,可以给环境变量定义任何的类型,包括结构体和数组。这是一个比较强大的功能,环境变量和程序变量的交互使用,将使得程序调试更为灵活便捷。例如:set$i=0printbar$i+-contents这样,当需要查看数组元素值时,就不用“printbar0-contents,printbar1-contents”地输入命令,只用
25、敲回车,重复执行上一条语句,环境变量会自动累加,从而完成逐个输出的功能。查看运行时的数据查看运行时的数据寄存器:寄存器:要查看寄存器的值,使用如下命令:info registers查看寄存器的情况(除了浮点寄存器)info all-registers查看所有寄存器的情况(包括浮点寄存器)info registers regname查看所指定的寄存器regname的情况。寄存器中放置了程序运行时的数据,比如程序当前运行的指令地址(ip),程序的当前堆栈地址(sp)等等。同样可以使用print命令来访问寄存器的情况,只需要在寄存器名字前加一个$符号就可以了。如:p/x$sp查看运行时的数据查看运行
26、时的数据寄存器:寄存器:$fp中存放的是调用者的栈帧,$fp+4存放的是当前函数的返回地址,因此通过不断的回溯$fp,即可以找到函数的调用关系。当GDB的BT命令不能正确显示堆栈时,加上使用命令infosymboladdr,可以自己倒推出堆栈。变更程序执行变更程序执行更改变量的值:更改变量的值:修改被调试程序运行时的变量值,在GDB中很容易实现,使用GDB的print命令即可完成,例如:(gdb)printx=4x=4这个表达式是C/C+的语法,意为把变量x的值修改为4,如果当前调试的语言是Pascal,那么可以使用Pascal的语法:x:=4某些时候,很有可能需要修改的变量和GDB中的参数冲
27、突,如setwidth=47可以使用setvarwidth=47明确告诉GDB是要修改程序的变量width变更程序执行变更程序执行跳转执行跳转执行:jumplinespec指定下一条语句的运行点。可以是文件的行号,可以是file:line格式,可以是+num这种偏移量格式。linespec标示着下一条运行语句从哪里开始变更程序执行变更程序执行向程序发送信号向程序发送信号:signalsignalname/signosignal参数是信号名字或者数字。UNIX的系统信号量通常从1到15,所以取值也在这个范围。single命令和shell的kill命令不同,系统的kill命令发信号给被调试程序时,
28、是由GDB截获的,而single命令所发出的信号则是直接发给被调试程序的变更程序执行变更程序执行强制程序返回强制程序返回:returnexpr使用return命令取消当前函数的执行,并立即返回,如果指定了expr,那么该表达式的值会被认作函数的返回值变更程序执行变更程序执行强制调用函数:强制调用函数:call expr 表达式中可以一是函数,以此达到强制调用函数的目的,并显示函数的返回值。如果函数返回值是void,那么就不显示。另一个相似的命令也可以完成这一功能print,print后面可以跟表达式,所以也可以用他来调用函数,print和call的不同是,如果函数返回void,call则不显示
29、,print则显示函数返回值,并把该值存入历史数据中远程调试远程调试gdbservergdbserver其实需要与GDB相同的操作系统功能。实际上,通过gdbserver来远程调试的系统都能在本地运行GDB!但gdbserver往往比较小,因此可以使用在新系统或较小的系统上。target machine:gdbserverargscomm制定通信方式,program和args制定要调试的程序例如gdbserver/dev/tty1notepad1.txt或者gdbserverhost:10000notepad1.txtGDB host machine:targetremote/dev/tty1targetremotetarget_ip:10000emacs下使用下使用GDB启动gdbM-xgdb/m68k-linux-gdb常用快捷键:C-hm描述emacsgdbmodeM-s相当于stepM-n相当于nextM-c相当于continueM-u相当于upM-d相当于downC-cC-f 相当于finishC-xspace在源文件中下断点结束结束本资料内容节选自DebuggingwithGDB更详尽内容请直接参阅该文档: