注意: 这里是讲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. Python练习题 005:三个数字由大到小排序输出

    [Python练习题 005]输入三个整数x,y,z,请把这三个数由小到大输出. ----------------------------------------------------------- ...

  2. 《Java从入门到失业》第五章:继承与多态(5.1-5.7):继承

    5.继承与多态 5.1为什么要继承 最近我儿子迷上了一款吃鸡游戏<香肠派对>,无奈给他买了许多玩具枪,我数了下,有一把狙击枪AWM,一把步枪AK47,一把重机枪加特林(Gatling).假 ...

  3. 活字格外联数据库SQLServer和Mysql的经验(大多数经验也适合其它使用外联数据库的平台)

    来自学习和实操后的总结,有说得不对的,或者遗漏的,大家留言补充.希望这个贴子,能成为活字格老铁们使用外联库的一个指南.PS即使你不打算使用外联库,里面的一些方法,也值得看一看! 一.库表规划1.系统表 ...

  4. 面经手册 · 第13篇《除了JDK、CGLIB,还有3种类代理方式?面试又卡住!》

    作者:小傅哥 博客:https://bugstack.cn 沉淀.分享.成长,让自己和他人都能有所收获! 一.前言 编程学习,先铺宽度还是挖深度? 其实技术宽度与技术深度是相辅相成的,你能了解多少技术 ...

  5. selenium登录163邮箱,得到cookie,requests后续请求

    1.场景 很多时候登录操作是比较复杂的,因为存在各种反爆破操作,以及为了安全性提交数据都会存在加密.如果要完全模拟代码去实现登录操作是比较复杂,并且该网站后续更新了登录安全相关功能,那么登录的模拟操作 ...

  6. pytest文档55-plugins插件开发

    前言 前面一篇已经学会了使用hook函数改变pytest运行的结果,代码写在conftest.py文件,实际上就是本地的插件了. 当有一天你公司的小伙伴觉得你写的还不错,或者更多的小伙伴想要你这个功能 ...

  7. pytest文档41-参数化 ids 用例描述为中文时控制台输出unicode编码问题(pytest_collection_modifyitems)

    前言 使用 pytest.mark.parametrize 参数化的时候,加 ids 参数用例描述有中文时,在控制台输出会显示unicode编码,中文不能正常显示. 使用 pytest_collect ...

  8. 在VC6.0下运行C语言程序,以及编程入门必备的常识类小知识!

    今天给大家分享在VC6.0环境下编写C语言程序的基本步骤,为初学者打开学习C语言的第一道门.具体步骤如下(如果需要软件资源,可以留言): 1)新建工作区 依次点击 文件--新建--工作区 或是Ctrl ...

  9. elk-架构图

     

  10. Helium文档6-WebUI自动化-S用于通过id\name\classname\xpth定位元素

    前言 S方法可以灵活定位元素特别注意,比如to_left_of参数的使用是查找在某个元素左侧的元素,但是默认只会找第一个S方法可以灵活运用,特别是没有id,没有name,只有classname的情况, ...