zoukankan      html  css  js  c++  java
  • gdb高级技巧

    注意: 这里是讲gdb的高级技巧。如果没有接触过gdb,请看这篇:点这里

    gdb是一个功能极其强大的命令行调试器。其实,除了我们常用的 file b s n q disp p 等命令,也有很多高级技巧。虽然有的功能是为系统级调试提供的,但还是有方便之处。

    接下来,我将介绍一些高级技巧,希望可以帮助大家。

    (温馨提醒:多用help命令!请提前用 -g 参数编译)

    GDB版本:9.1;系统版本:Arch Linux 245-1

    示例代码:(以下示例均以此代码为准)

    #include <iostream>
    #include <cstdio>
    using namespace std;
    int f(int n){
        if(n==0) return 1;
        return f(n-1)*n;
    }
    
    int main(){
        int n;
        scanf("%d",&n);
        printf("%d
    ",f(n));
        return 0;
    }
    

    1. backtrace

    backtrace(简写为bt)可以让你查看栈帧信息。这对调试递归的函数很有帮助。

    配套命令:

    up/down [num] 往栈顶/栈底移动num帧。num默认为1。
    frame [num] 切换到第num帧。frame简写为f。

    Example:

    当抵达某个断点停下后,输入bt,类似于这样:

    (gdb) bt
    #0  f (n=3) at example.cpp:6
    #1  0x000055555555519e in f (n=4) at example.cpp:6
    #2  0x000055555555519e in f (n=5) at example.cpp:6
    #3  0x000055555555519e in f (n=6) at example.cpp:6
    #4  0x000055555555519e in f (n=7) at example.cpp:6
    #5  0x000055555555519e in f (n=8) at example.cpp:6
    #6  0x000055555555519e in f (n=9) at example.cpp:6
    #7  0x000055555555519e in f (n=10) at example.cpp:6
    #8  0x00005555555551dd in main () at example.cpp:12
    (gdb)
    

    几个示例:

    (gdb) up 1
    #1  0x000055555555519e in f (n=4) at example.cpp:6
    6           return f(n-1)*n;
    (gdb) down 1
    #0  f (n=3) at example.cpp:6
    6           return f(n-1)*n;
    (gdb) frame 5 //前面bt输出的结果中,第一项是序号,frame即切换到对应序号的帧
    #5  0x000055555555519e in f (n=8) at example.cpp:6
    6           return f(n-1)*n;
    (gdb) 
    

    2. commands

    commands(简写为comm)可以在触发某个(或多个)断点的时候运行指定gdb命令。

    用法:commands [断点编号1] [断点编号2] ...

    之后,它会让你逐行输入要指定的gdb命令。

    效果吗...在到你指定的断点时,他都会逐行运行你之前输入的命令。

    Example:

    (gdb) b 6
    Breakpoint 1 at 0x1191: file example.cpp, line 6.
    (gdb) comm 1 
    Type commands for breakpoint(s) 1, one per line.
    End with a line saying just "end".
    >p "Command test" //指定到达1号断点时打印这段字符串
    >end //以end结束
    (gdb) r
    Starting program: /run/media/acceptedzhs/SimpleDisk/编程/洛谷/example 
    10
    
    Breakpoint 1, f (n=10) at example.cpp:6
    6           return f(n-1)*n;
    $1 = "Command test"
    (gdb) 
    

    顺便提一句,怎么查看断点编号?运行 info b 即可。

    输出类似这样:(num是编号)

    Num     Type           Disp Enb Address            What
    1       breakpoint     keep y   0x000000000000129e in pre() at UVA10140 Prime Distance.cpp:12
    2       breakpoint     keep y   0x00000000000012c7 in pre() at UVA10140 Prime Distance.cpp:13
    3       breakpoint     keep y   0x00000000000012de in pre() at UVA10140 Prime Distance.cpp:14
    

    3. ignore

    用法:ignore [断点编号] [num]。ignore可缩写为ig。

    效果:在前num次触发指定断点时都不停止(即到了第num+1次触发断点才停下)

    这在调一些循环结构的代码时比较有用。

    Example:

    (gdb) b 6
    Breakpoint 1 at 0x1191: file example.cpp, line 6.
    (gdb) ig 1 4
    Will ignore next 4 crossings of breakpoint 1.
    (gdb) r
    Starting program: /run/media/acceptedzhs/SimpleDisk/编程/洛谷/example 
    10
    
    Breakpoint 1, f (n=6) at example.cpp:6
    6           return f(n-1)*n;
    (gdb) //前面4次经过断点,分别为f(10)、f(9)、f(8)、f(7),都跳过了
    //因此f(6)才触发
    

    4. condition

    用法:condition [断点编号] [条件]。condition可缩写为cond。

    效果:触发断点时,只有指定的条件为真时才停下。

    Example:

    (gdb) b 6
    Breakpoint 1 at 0x1191: file example.cpp, line 6.
    (gdb) cond 1 n==5 //只有n等于5时才触发断点
    (gdb) r
    Starting program: /run/media/acceptedzhs/SimpleDisk/编程/洛谷/example 
    10
    
    Breakpoint 1, f (n=5) at example.cpp:6
    6           return f(n-1)*n;
    (gdb) 
    

    5. 各种breakpoint

    什么?断点还有类型?这里介绍下:

    • break(简写b)是我们最熟悉的。
    • tbreak(简写tb):临时断点,也就是触发一次后自动消失。与break用法相同。
    • hbreak(简写hb):硬件断点。对我们来说没什么用。
    • rbreak(简写rb):根据正则表达式设置断点。用法:rbreak [正则表达式]

    说明一下rbreak。举个例子,我程序里有两个函数,dfs1与dfs2。如果我运行 rbreak dfs* ,由于dfs1与dfs2均匹配,所以这两个函数均会被加上断点。

    Example1:(tbreak)(看到没,第一次触发在f函数处的断点,继续运行便不会再触发该断点了)

    (gdb) tb f
    Temporary breakpoint 1 at 0x1184: file example.cpp, line 5.
    (gdb) r
    Starting program: /run/media/acceptedzhs/SimpleDisk/编程/洛谷/example 
    10 //这里是输入,10!=3628800
    
    Temporary breakpoint 1, f (n=10) at example.cpp:5
    5           if(n==0) return 1; //临时断点,只停了一次
    (gdb) c
    Continuing. //继续,就不会再停了
    3628800
    [Inferior 1 (process 31859) exited normally]
    (gdb) 
    

    Example2:(rbreak)(main符合mai*的条件,因此被加了断点)

    (gdb) rb mai*
    Breakpoint 1 at 0x11ac: file example.cpp, line 9.
    int main();
    (gdb) 
    

    6. print/display命令输出格式(可以用简写)

    用法:print/[format] [变量1] [变量2] ...

    当然,如果是display命令,则要换成display/[format] [变量1] [变量2] ...

    其中,format是一个小写字母,指定打印变量值的格式。

    format字母 对应格式
    x 按十六进制格式显示变量
    d 按十进制格式显示变量
    u 按十进制格式显示无符号整型
    o 按八进制格式显示变量
    t 按二进制格式显示变量
    a 按十六进制格式显示变量
    c 按字符格式显示变量
    f 按浮点数格式显示变量

    比如说,调试状压DP的程序时,就可以 p/t [变量名] 来以二进制形式查看变量了。disp同理。

    Example:

    (gdb) p/x 100 
    $1 = 0x64
    (gdb) p/d 100
    $2 = 100
    (gdb) p/u 100
    $3 = 100
    (gdb) p/o 100
    $4 = 0144
    (gdb) p/t 100
    $5 = 1100100
    (gdb) p/a 100
    $6 = 0x64
    (gdb) p/c 100
    $7 = 100 'd'
    (gdb) p/f 100
    $8 = 1.40129846e-43 //浮点数格式不一样
    (gdb) 
    

    7. save

    其实断点是可以保存的!比如说,我临时要重启一下,又不想丢失当前调试的断点信息。那么,我们可以将当前的断点信息保存到一个文件里,到时候再导入。

    用法:save breakpoints [文件名]

    效果:将当前所有的断点信息保存到一个指定的文件里。

    有人有疑问了,怎么导入断点信息呢?那就是source命令!

    用法:source [文件名]

    效果:从指定的文件里导入断点信息。

    Example:

    (gdb) b 5
    Breakpoint 1 at 0x1184: file example.cpp, line 5.
    (gdb) b 6
    Breakpoint 2 at 0x1191: file example.cpp, line 6.
    (gdb) b 7
    Breakpoint 3 at 0x11a2: file example.cpp, line 7.
    (gdb) save breakpoints 123 //保存断点信息
    Saved to file '123'.
    (gdb) q
    
    ---中间退出gdb,再重新进---
    
    (gdb) source 123 //从这个文件中引入断点信息
    Breakpoint 1 at 0x1184: file example.cpp, line 5.
    Breakpoint 2 at 0x1191: file example.cpp, line 6.
    Breakpoint 3 at 0x11a2: file example.cpp, line 7.
    (gdb) 
    

    8. call

    用法:call [调用语句]

    效果:调用指定函数。

    比如说,我的程序有一个min函数,我就可以通过 call min(a,b) 来获取变量a、b的最小值了。

    Example:

    (gdb) call f(10) //也就是10!
    $1 = 3628800
    (gdb) 
    

    9. finish(缩写fin)

    用法:无参数。

    效果:继续运行,直到当前函数返回。

    Example:

    Breakpoint 1, main() at example.cpp:10
    10    int n;
    (gdb) fin
    Run till exit from #0 main() at example.cpp:9 //main函数执行完毕,返回了0
    
    [Inferior 1 (process 32243) exited normally]
    (gdb) 
    

    10. watchpoint

    其实,断点还有一种特殊的类型——watchpoint。(简写为wa——似乎不太吉祥)

    用法:watch/rwatch/awatch [变量名]

    作用:监视指定变量。

    • watch(简写wa):当指定变量被写时停下。
    • rwatch(简写rwa):当指定变量被读时停下。
    • awatch(简写awa):当指定变量被读/写时停下。

    Example:

    (gdb) wa n
    Hardware watchpoint 2: n
    (gdb) c
    Continuing.
    10
    
    Hardware watchpoint 2: n
    
    Old value = 32767 //此处由于scanf读入修改了n的值,因此停下
    New value = 10
    0x00007ffff7ac43a9 in __vfscanf_internal () from /usr/lib/libc.so.6
    (gdb) 
    

    11. checkpoint

    有时候,我们要复现某个bug,这个时候,我们可以创建一个快照,即checkpoint。

    命令:checkpoint(可简写为ch)

    用法:无参数。

    效果:创建一个快照,包含当前调试的所有信息。同时会输出这个checkpoint的信息,就像这样:

    checkpoint 1: fork returned pid 25776.
    

    其中,数字1便是这个checkpoint的编号。

    那么,如何回滚到以前的快照呢?那就是restart命令啦!

    用法:restart [checkpoint编号]

    效果:回退到指定checkpoint的快照。

    Example:

    Breakpoint 1, f (n=10) at example.cpp:5
    5           if(n==0) return 1;
    (gdb) ch //创建了编号为1的快照
    checkpoint 1: fork returned pid 32213.
    (gdb) c
    Continuing.
    
    Breakpoint 1, f (n=9) at example.cpp:5
    5           if(n==0) return 1;
    (gdb) restart 1 //恢复到编号为1的快照
    Switching to process 32213
    #0  f (n=10) at example.cpp:5
    5           if(n==0) return 1;
    (gdb) 
    

    12. jump

    用法:jump [num]

    作用:强制使跳转至第num行。(中间的行都跳过了)

    注意,这个不能跨函数跳转,否则会出错。

    Example:

    (gdb) b 10
    Breakpoint 1 at 0x11bb: file example.cpp, line 11.
    (gdb) r
    Starting program: /run/media/acceptedzhs/SimpleDisk/编程/洛谷/example 
    
    Breakpoint 1, main () at example.cpp:11
    11          scanf("%d",&n);
    (gdb) jump 13
    Continuing at 0x5555555551f0.
    [Inferior 1 (process 32243) exited normally] //跳到了第13行,main函数已经结束了,因此直接退出程序
    (gdb) 
    

    13. return

    用法:return [argu]

    作用:强制使当前函数退出,并返回argu值。(如果该函数本来就没有返回值,则argu可以省略)

    Breakpoint 1, f (n=10) at example.cpp:5
    5           if(n==0) return 1;
    (gdb) return 15
    Make f(int) return now? (y or n) y
    #0  0x00005555555551dd in main () at example.cpp:12
    12          printf("%d
    ",f(n));
    (gdb) n
    15  //因为前面设定返回15,所以这里输出15
    

    蒟蒻写博客不易,恳请大佬点个赞!

  • 相关阅读:
    C语言实现二叉堆BuildHeap操作
    Java大浮点数
    线索二叉树
    二叉树的层次遍历
    CS231n Lecture3-Loss Functions and Optimization学习笔记
    Python函数式编程(高阶函数、map/reduce、filter、sorted、匿名函数、返回函数)-3
    CS231n Lecture2-Image Classification学习笔记
    Python高级特性(生成器、迭代器、列表生成式)-2
    Python函数(定义、参数、递归)-1
    Numpy中的广播(Broadcast)讲解简单易懂,困扰已久,终于想通了
  • 原文地址:https://www.cnblogs.com/acceptedzhs/p/12562880.html
Copyright © 2011-2022 走看看