• GDB 基础


    编译程序, gcc 需要加上 -g,才能用 gdb 调试程序,然后把优化等级设为 -O0

    在 (gdb) 状态下直接回车,执行上一次命令

    (gdb) help   ;查看帮助文档

    (gdb) shell 命令  ;可以在 gdb 调试环境下执行 shell 命令

    程序执行控制

    $ gdb  ;进入 gdb 调试环境

    $ gdb 可执行文件   ;从可执行文件读取符号,还没有运行

    (gdb) add-symbol-file 可执行文件  ;从可执行文件读取符号

    (gdb) r  ;从头开始运行可执行程序

    (gdb) c  ;恢复程序运行,直到程序结束,或是下一个断点到来

    (gdb) n  ;step over,单步运行

    (gdb) step  ;step in,进入函数,单步运行

    (gdb) finish  ;运行程序,直到当前函数完成返回。并打印函数返回时的堆栈地址和返回值及参数值等信息

    (gdb) u 或 until  ;运行程序直到退出循环体

    (gdb) q  ;退出调试

    信息查看

    (gdb) l  ; 查看源代码,默认打印 10行

    (gdb) l 10  ;以第10行为中心,打印10行

    (gdb) l function  ;打印函数

    (gdb) p /x i  ;以16进制形式打印变量 i 的值

    (gdb) bt  ;查看调用的函数堆栈

    (gdb) info program  ;当进程被gdb停住时,可以使用info program 来查看程序的是否在运行,进程号,被暂停的原因

    (gdb) info line tst.c:func  ;查看 func 函数在运行时的内存地址

    (gdb) info line 145  ;查看 145 行代码在运行时的内存地址

    (gdb) run > file.log  ;运行程序, 并且把 printf 消息输入到文件 file.log 中

    (gdb) tty /dev/tty4  ;把 printf 消息输出到终端 tty4 

    停止点

    在gdb中,我们可以有以下几种暂停方式:断点(BreakPoint)、观察点(WatchPoint)、捕捉点(CatchPoint)、信号(Signals,如 ctrl+c)、线程停止(Thread Stops)。如果要恢复程序运行,可以使用c或是continue命令。

    断点

    (gdb) b 125  ; 在第 125 行处添加一个断点

    (gdb) b 函数名  ;在函数入口处设置一个断点

    (gdb) b filename:linenum  在源文件 filename 的 linenum 行处停住

    (gdb) break filename:function  在源文件filename的function函数的入口处停住。

    (gdb) break arg if condition  ;condition表示条件,在条件成立时停住。比如在循环体中,可以设置break 127 if i==100,表示当执行到 127 行时如果 i 等于 100 则停下。

    (gdb) info b  ;查看所有断点的信息

    (gdb) info b 断点号  ;查看某个断点信息

    观察点

    观察点一般来观察某个表达式(变量也是一种表达式)的值是否有变化了,如果有变化,马上停住程序

    观察点在程序运行的时候才能设置,并且重新运行需要重新设置观察点

    watch expr
    为表达式(变量)expr设置一个观察点。一旦表达式值有变化时,马上停住程序。

    rwatch expr
    当表达式(变量)expr被读时,停住程序。

    awatch expr
    当表达式(变量)的值被读或被写时,停住程序。

    info watchpoints
    查看观察点、断点和捕捉点信息,同info break 一样.

    捕捉点

    设置捕捉点来捕捉程序运行时的一些事件。如:载入共享库(动态链接库)或是C++的异常

    维护停止点

    GDB中的停止点一共是上述的三类。在GDB中,如果你觉得已定义好的停止点没有用了,你可以使用delete、clear、disable、enable这几个命令来进行维护。

    clear
    清除所有的已定义的停止点。

    clear function
    清除所有设置在函数上的停止点。

    clear linenum
    清除所有设置在指定行上的停止点。

    clear filename:linenum
    清除所有设置在指定文件:指定行上的停止点。

    delete [breakpoints] [range...]
    删除指定的断点,breakpoints为断点号。如果不指定断点号,则表示删除所有的断点。range 表示断点号的范围(如:3-7)。其简写命令为d。

    比删除更好的一种方法是disable停止点,disable了的停止点,GDB不会删除,当你还需要时,enable即可,就好像回收站一样。

    disable [breakpoints] [range...]
    disable所指定的停止点,breakpoints为停止点号。如果什么都不指定,表示disable所有的停止点。简写命令是dis.

    enable [breakpoints] [range...]
    enable所指定的停止点,breakpoints为停止点号。

    enable [breakpoints] once range...
    enable所指定的停止点一次,当程序停止后,该停止点马上被GDB自动disable。

    enable [breakpoints] delete range...
    enable所指定的停止点一次,当程序停止后,该停止点马上被GDB自动删除。

    程序变量

    在GDB中,你可以随时查看以下三种变量的值:

    1、全局变量(所有文件可见的)
    2、静态全局变量(当前文件可见的)
    3、局部变量(当前Scope可见的)
    如果你的局部变量和全局变量发生冲突(也就是重名),一般情况下是局部变量会隐藏全局变量,也就是说,如果一个全局变量和一个函数中的局部变量同名时,如果当前停止点在函数中,用print显示出的变量的值会是函数中的局部变量的值。如果此时你想查看全局变量的值时,你可以使用“::”操作符:
    file::variable
    function::variable
    可以通过这种形式指定你所想查看的变量,是哪个文件中的或是哪个函数中的。例如,查看文件f2.c中的全局变量x的值:
    gdb) p‘f2.c'::x
    当然,“::”操作符会和C++中的发生冲突,GDB能自动识别“::”是否C++的操作符,所以你不必担心在调试C++程序时会出现异常。

    另外,需要注意的是,如果你的程序编译时开启了优化选项,那么在用GDB调试被优化过的程序时,可能会发生某些变量不能访问,或是取值错误的情况。这个是很正常的,因为优化程序会删改你的程序,整理你程序的语句顺序,剔除一些无意义的变量等,所以在GDB调试这种程序时,运行时的指令和你所编写指令就有不一样,也就会出现你所想象不到的结果。对付这种情况时,需要在编译程序时关闭编译优化。一般来说,几乎所有的编译器都支持编译优化的开关,例如,GNU 的C/C++编译器GCC,你可以使用“-gstabs”选项来解决这个问题。关于编译器的参数,还请查看编译器的使用说明文档。

    数组

    有时候,你需要查看一段连续的内存空间的值。比如数组的一段,或是动态分配的数据的大小。你可以使用GDB的“@”操作符,“@”的左边是第一个内存的地址的值,“@”的右边则你你想查看内存的长度。例如,你的程序中有这样的语句:

    int *array = (int *) malloc (len * sizeof (int));
    于是,在GDB调试过程中,你可以以如下命令显示出这个动态数组的取值:
    *array@len 
    @的左边是数组的首地址的值,也就是变量array所指向的内容,右边则是数据的长度,其保存在变量len中,其输出结果,大约是下面这个样子的:
    (gdb) p *array@len
    $1 = {2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32, 34, 36, 38, 40}
    如果是静态数组的话,可以直接用print数组名,就可以显示数组中所有数据的内容了。

    自动显示

    可以设置一些自动显示的变量,当程序停住时,或是在你单步跟踪时,这些变量会自动显示。相关的GDB命令是display。

    display expr
    display/fmt expr
    display/fmt addr

    expr是一个表达式,fmt表示显示的格式,addr表示内存地址,当你用display设定好了一个或多个表达式后,只要你的程序被停下来,GDB会自动显示你所设置的这些表达式的值。

    格式i和s同样被display支持,一个非常有用的命令是:
    display/i $pc
    $pc是GDB的环境变量,表示指令的地址,/i则表示输出格式为机器指令码,也就是汇编。于是当程序停下后,就会出现源代码和机器指令码相对应的情形,这是一个很有意思的功能。

    下面是一些和display相关的GDB命令:

    undisplay dnums...
    delete display dnums...
    删除自动显示,dnums意为所设置好了的自动显式的编号。如果要同时删除几个,编号可以用空格分隔,如果要删除一个范围内的编号,可以用减号表示(如:2-5)
    disable display dnums...
    enable display dnums...
    disable和enalbe不删除自动显示的设置,而只是让其失效和恢复。
    info display
    查看display设置的自动显示的信息。GDB会打出一张表格,向你报告当然调试中设置了多少个自动显示设置,其中包括,设置的编号,表达式,是否enable。

    设置显示选项

    历史记录

    当用GDB的print查看程序运行时的数据时,每一个print都会被GDB记录下来。GDB会以$1, $2, $3 .....这样的方式为你每一个print命令编上号。于是,你可以使用这个编号访问以前的表达式,如$1。这个功能所带来的好处是,如果你先前输入了一个比较长的表达式,如果你还想查看这个表达式的值,你可以使用历史记录来访问,省去了重复输入。

    改变程序的执行

    一旦使用GDB挂上被调试程序,当程序运行起来后,可以根据自己的调试思路来动态地在GDB中更改当前被调试程序的运行线路或是其变量的值,这个强大的功能能够更好的调试程序,比如,可以在程序的一次运行中走遍程序的所有分支。

    1、修改变量值

    修改被调试程序运行时的变量值,在GDB中很容易实现,使用GDB的print命令即可完成。如:
    (gdb) print x=4
    x=4这个表达式是C/C++的语法,意为把变量x的值修改为4,如果你当前调试的语言是Pascal,那么你可以使用Pascal的语法:x:=4。

    在某些时候,很有可能你的变量和GDB中的参数冲突,如:

    (gdb) whatis width
    type = double
    (gdb) p width
    $4 = 13
    (gdb) set width=47
    Invalid syntax in expression.
    因为,set width是GDB的命令,所以,出现了“Invalid syntax in expression”的设置错误,此时,你可以使用set var命令来告诉GDB,width不是你GDB的参数,而是程序的变量名,如:
    (gdb) set var width=47
    另外,还可能有些情况,GDB并不报告这种错误,所以保险起见,在你改变程序变量取值时,最好都使用set var格式的GDB命令。

    2、跳转执行

    一般来说,被调试程序会按照程序代码的运行顺序依次执行。GDB提供了乱序执行的功能,也就是说,GDB可以修改程序的执行顺序,可以让程序执行随意跳跃。这个功能可以由GDB的jump命令来完:
    jump linespec
    指定下一条语句的运行点。可以是文件的行号,可以是file:line格式,可以是+num这种偏移量格式。表示下一条运行语句从哪里开始。
    jump *address
    这里的是代码行的内存地址。
    注意,jump命令不会改变当前的程序栈中的内容,所以,当你从一个函数跳到另一个函数时,当函数运行完返回时进行弹栈操作时必然会发生错误,可能结果还是非常奇怪的,甚至于产生程序Core Dump。所以最好是同一个函数中进行跳转。

    熟悉汇编的人都知道,程序运行时,eip寄存器用于保存当前代码所在的内存地址。所以,jump命令也就是改变了这个寄存器中的值。于是,你可以使用“set $pc”来更改跳转执行的地址。如:

    set $pc = 0x485

    3、产生信号量

    使用singal命令,可以产生一个信号量给被调试的程序。如:中断信号Ctrl+C。这非常方便于程序的调试,可以在程序运行的任意位置设置断点,并在该断点用GDB产生一个信号量,这种精确地在某处产生信号非常有利程序的调试。

    语法是:
    signal signal
    UNIX的系统信号量通常从1到15。所以取值也在这个范围。

    single命令和shell的kill命令不同,系统的kill命令发信号给被调试程序时,是由GDB截获的,而single命令所发出一信号则是直接发给被调试程序的。

    4、强制函数返回

    如果你的调试断点在某个函数中,并还有语句没有执行完。你可以使用return命令强制函数忽略还没有执行的语句并返回。
    return
    return expression

    使用return命令取消当前函数的执行,并立即返回,如果指定了,那么该表达式的值会被认作函数的返回值。

    5、强制调用函数

    call expr
    表达式中可以一是函数,以此达到强制调用函数的目的。并显示函数的返回值,如果函数返回值是void,那么就不显示。

    print expr
    另一个相似的命令也可以完成这一功能——print,print后面可以跟表达式,所以也可以用他来调用函数,print和call的不同是,如果函数返回void,call则不显示,print则显示函数返回值,并把该值存入历史数据中。

    在不同语言下使用 GDB

    GDB支持下列语言:C, C++, Fortran, PASCAL, Java, Chill, assembly, 和 Modula-2。一般说来,GDB会根据你所调试的程序来确定当然的调试语言,比如:发现文件名后缀为“.c”的,GDB会认为是C程序。文件名后缀为 “.C, .cc, .cp, .cpp, .cxx, .c++”的,GDB会认为是C++程序。而后缀是“.f, .F”的,GDB会认为是Fortran程序,还有,后缀为如果是“.s, .S”的会认为是汇编语言。
    也就是说,GDB会根据你所调试的程序的语言,来设置自己的语言环境,并让GDB的命令跟着语言环境的改变而改变。比如一些GDB命令需要用到表达式或变量时,这些表达式或变量的语法,完全是根据当前的语言环境而改变的。例如C/C++中对指针的语法是*p,而在Modula-2中则是p^。并且,如果你当前的程序是由几种不同语言一同编译成的,那到在调试过程中,GDB也能根据不同的语言自动地切换语言环境。这种跟着语言环境而改变的功能,真是体贴开发人员的一种设计。

    下面是几个相关于GDB语言环境的命令:

    show language
    查看当前的语言环境。如果GDB不能识为你所调试的编程语言,那么,C语言被认为是默认的环境。

    info frame
    查看当前函数的程序语言。

    info source
    查看当前文件的程序语言。

    如果GDB没有检测出当前的程序语言,那么你也可以手动设置当前的程序语言。使用set language命令即可做到。

    当set language命令后什么也不跟的话,你可以查看GDB所支持的语言种类,于是你可以在set language后跟上被列出来的程序语言名,来设置当前的语言环境

  • 相关阅读:
    OC-KVO简介
    注册审核
    应用权限
    关于函数执行的一点知识
    设置权限
    文件操作实例:文件管理器(网页版)
    文件操作
    正则表达式
    全局变量和递归
    案例:简单留言板
  • 原文地址:https://www.cnblogs.com/god-of-death/p/14954622.html
Copyright © 2020-2023  润新知