注意: 这里是讲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\n",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\n",f(n));
(gdb) n
15 //因为前面设定返回15,所以这里输出15

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

gdb高级技巧的更多相关文章

  1. g++中宏NULL究竟是什么?

    NULL是个指针,还是个整数?0?或(void*)0?答案是和g++版本有关.g++ 4.6支持C++11,引入了nullptr,也许会发生变化. 可以写段简单代码求证一下: #include < ...

  2. GDB技巧整理

    https://blog.atime.me/note/gdb-tricks.html 整理常用的gdb技巧. 常用命令 常用的gdb命令... 启动gdb 直接运行 gdb --args prog a ...

  3. gdb调试若干问题

    1.若干命令速查 file <文件名>:加载被调试的可执行程序文件.因为一般都在被调试程序所在目录下执行GDB,因而文本名不需要带路径.示例:(gdb) file gdb-sample r ...

  4. 《软件调试的艺术》学习笔记——GDB使用技巧摘要

    <软件调试的艺术>学习笔记——GDB使用技巧摘要 <软件调试的艺术>,因为名是The Art of Debugging with GDB, DDD, and Eclipse. ...

  5. linux应用调试技术之GDB和GDBServer

    1.调试原理 GDB调试是应用程序在开发板上运行,然后在PC机上对开发板上得应用程序进行调试,PC机运行GDB,开发板上运行GDBServer.在应用程序调试的时候,pc机上的gdb向开发板上的GDB ...

  6. Javascript高级技巧

    上次整理了Ajax部分,这周看完了高级技巧部分,也整理下吧. 1.类型检测 使用Object.prototype.toString.call(obj)的方式. 因为无论typeof还是instance ...

  7. 新手如何在gdb中存活

    网络上已经有很多gdb调试的文章了,为什么我还要写这篇文章呢,因为本文是写给gdb新手的,目的就是通过一个简单的例子来让新手很快上手.一旦上手入门了,其他的问题就可以自己去搜索搞定了.右边是gdb的L ...

  8. GDB 多线程调试:只停止断点的线程,其他线程任然执行; 或只运行某些线程 其他线程中断

    多线程调试之痛 调试器(如VS2008和老版GDB)往往只支持all-stop模式,调试多线程程序时,如果某个线程断在一个断点上,你的调试器会让整个程序freeze,直到你continue这个线程,程 ...

  9. GDB调试命令

    1.查看源码: list [函数名][行数] 2.暂停程序 (1)设置断点: a.break + [源代码行号][源代码函数名][内存地址] b.break ... if condition   .. ...

随机推荐

  1. 我把这个贼好用的Excel导出工具开源了!!

    写在前面 不管是传统软件企业还是互联网企业,不管是管理软件还是面向C端的互联网应用.都不可避免的会涉及到报表操作,而对于报表业务来说,一个很重要的功能就是将数据导出到Excel.如果我们在业务代码中, ...

  2. antd pro table中的文件上传

    概述 示例代码 列表页面 form 页面 model.js service.js 总结 概述 项目中经常会遇到在表格中展示图片的需求(比如展示用户信息时, 有一列是用户的头像). antd pro t ...

  3. Redis 的完整安装过程

    Windos 版本安装 Redis 官方并不支持 Window 版本,但是微软公司在 Github 上维护了一个 Windows 版本的 Redis 项目,供 Windows 用户下载使用. 下载地址 ...

  4. ansible2.4安装和体验

    欢迎访问我的GitHub https://github.com/zq2599/blog_demos 内容:所有原创文章分类汇总及配套源码,涉及Java.Docker.Kubernetes.DevOPS ...

  5. 串口wifi

    串口wifi 串口WiFi ZLAN7146是一款wifi转串口的wifi串口服务器.该串口服务器可以方便地使得串口设备连接到WIFI无线网络,实现串口设备的无线化网络升级.RS232接口支持全双工. ...

  6. Shell Scripting 笔记

    Shell Scripting Tutorial Variables in the Bourne shell do not have to be declared, as they do in lan ...

  7. 【树形DP】CF 1293E Xenon's Attack on the Gangs

    题目大意 vjudge链接 给n个结点,n-1条无向边.即一棵树. 我们需要给这n-1条边赋上0~ n-2不重复的值. mex(u,v)表示从结点u到结点v经过的边权值中没有出现的最小非负整数. 计算 ...

  8. 【API管理 APIM】APIM中对后端API服务的DNS域名缓存问题

    问题描述 在使用API Management来进行API管理时,当我们后端的API DNS IP地址发生改变或者是API的域名发生改变后,通过APIM请求访问的还是是旧的域名或者IP地址,这是因API ...

  9. python 微信小程序自动化

    微信小程序自动化 https://www.cnblogs.com/yyoba/python27 - FautoTesthttps://www.cnblogs.com/yyoba/p/9973731.h ...

  10. linux 环境搭建Jenkins

    这里提供一个本地搭建Jenkins的方法,基于wins 的 https://blog.csdn.net/u011541946/article/month/2017/09/2   下面讲的是在服务器上操 ...