淘先锋技术网

首页 1 2 3 4 5 6 7

一.启动调试

在CmakeLists.txt中设置gdb指令:

SET(CMAKE_BUILD_TYPE "Debug")
SET(CMAKE_CXX_FLAGS_DEBUG "$ENV{CXXFLAGS} -O0 -Wall -g2 -ggdb")
SET(CMAKE_CXX_FLAGS_RELEASE "$ENV{CXXFLAGS} -O3 -Wall")

调试方式运行程序:

  1. 调试启动无参程序:

gdb helloworld

2.调试启动带参程序:

gdb helloworld
run 参数

gdb helloworld
set args 参数
run

调试已运行程序:

使用ps命令找到进程id:

ps -ef|grep 进程名

获取进程id后,使用attach方式调试进程:

gdb
(gdb)attach 进程id

二、断点设置

1.info breakpoints查看已设置的断点:

2.根据行号设置断点

b 9 #break 可简写为b

b test.c:9

则运行到第9行的时候会断住

3.根据函数名设置断点:将断点设置在函数处

b printNum

4.根据条件设置断点:

假设程序某处崩溃,怀疑崩溃的原因是某个地方出现了非期望的值,那么可以在这里使用条件设置断点

break test.c:23 if b==0

当在b等于0时,程序将会在第23行断住

它和condition有着类似的作用,假设上面的断点号为1,那么相当于:

condition 1 b==0

5.根据规则设置断点:

例如 rbreak printNum* 意思是对所有以printNum开头的函数都设置了断点

#用法:rbreak file:regex
rbreak .
rbreak test.c:.  #对test.c中所有函数设置断点
rbreak test.c:^print  #对以print开头的函数设置断点

6.设置临时断点

假设想要在某处的断点只生效一次,那么可以设置临时断点

tbreak test.c:10  #在第10行设置临时断点

7.跳过多次设置断点

ignore 1 30  #1为要忽略的断点号,30是需要跳过的次数

8.根据表达式值变化产生断点

有时需要观察某个值或表达式的变化,则可以借助watch命令:

watch a

此时,让程序继续运行,如果a的值发生变化,则会打印相关的内容

rwatch和watch均可以设置观察点,前者是当变量值被读时断住,后者是被读或者被改写时断住

9.禁用或启动断点

disable #禁用所有断点
disable bnum #禁用标号为bnum的断点
enable #启用所有断点
enable bnum #启用标号为bnum的断点
enable delete bnum #启动标号为bnum的断点,并且再次之后删除该断点

10.断点清除

clear #删除当前行所有breakpoints
clear function #删除函数名为function处的断点
clear filename:function #删除文件filename中函数function处的断点
clear lineNum #删除行号为lineNum处的断点
clear filename: lineNum #删除文件filename中行号为lineNum处的断点
delete #删除所有breakpoints,watchpoints和catchpoints
delete bnum #删除断点为bnum的断点

三、变量查看

1.普通变量查看

打印基本类型变量,数组,字符数组

使用print(可简写为p)打印变量内容

例如: (gdb) p a

当多个函数或者多个文件会有同一个变量名时,可以在前面加上函数名或者文件名来区分

(gdb) p 'test.c'::a
(gdb) p 'main'::b

2.打印指针指向内容

(gdb) p d #打印指针的地址
(gdb) p *d #打印指针指向的内容

仅使用*只能打印出第一个值,如果想打印多个值,后面跟上@并加上要打印的长度,或者@后跟上变量值

(gdb) p *d@10 或 (gdb) p *d@a

此外,$可表示上一个变量,假设此时有一个链表ListNode,它有next成员代表下一个节点,则使用下面方式可不断打印链表内容:

(gdb) p *ListNode
(gdb) p *$.next

如果想要查看前面数组的内容,可以将下标一个一个累加,还可以定义一个类似UNIX环境变量:

例如:

(gdb) set $index=0
(gdb) p b[$index++]

3.按照特定格式打印变量

常见的格式控制字符:

x 按十六进制格式显示变量。

d 按十进制格式显示变量。

u 按十六进制格式显示无符号整型。

o 按八进制格式显示变量。

t 按二进制格式显示变量。

a 按十六进制格式显示变量。

c 按字符格式显示变量。

f 按浮点数格式显示变量。

例如: (gdb) p/x c #按照16进制打印

4.查看内存内容

examine(简写为x)可以用来查看内存地址中的值

语法为:x/[n][f][u] addr

其中,n表示要显示的内存单元数,默认是1

f表示要打印的格式

u表示要打印的单元长度

addr 内存地址

单元长度类型常见的有:

b 字节

h 半字,即双字节

w 字,即四字节

g 八字节

例如:

(gdb) x/4tb &e
0x7fffffffdbd4:    00000000    00000000    00001000    01000001

5.自动显示变量内容

(gdb) display e #程序断住时自动显示该变量的值。每次程序断住时,就会打印e的值

要查看哪些变量被设置了display,可以使用:

(gdb) info display

如果想要清除自动显示可以使用: delete display num #num为前面变量前的编号,不带num清除所有

或者:disable display num #num为前面变量前的编号,不带num清除所有

6.查看寄存器内容

(gdb) info registers

四、单步调试

1.单步调试-next

next命令(可简写为n)用于在程序断住后,继续执行下一条语句,假设已经启动调试,并在第12行停住,如果要继续执行,则使用n执行下一条语句,如果后面跟上数字num,则表示执行该命令num次,就达到继续执行n行的效果了

next命令不会进入单个函数的内部

2.单步进入-step

如果想要跟踪函数内部,可以使用step命令(可简写为s),它可以单步跟踪到函数内部,但前提是该函数有调试信息并且有源码信息

如果通过step命令尝试进入函数,但是没有该函数源码,需要跳过该函数执行,可使用finish命令,继续后面的执行

step-mode:用来设置当遇到没有调试信息的函数,step命令是否跳过该函数而继续执行,默认情况下是跳过的,即为off

(gdb)show step-mode
(gdb)set step-mode on
(gdb)set step-mode off

stepi(可简写为si)命令为每次执行一条机器指令

3.继续执行到下一个断点-continue

我们可能打了多出断点或者断点打在循环内,想要跳过这个断点,甚至跳过多次断点继续执行,可以使用continue命令(可简写为c)或者fg,它会继续执行,直到再次遇到断点

(gdb)fg #继续运行,直到下一次断住
(gdb)c 3 #跳过三次断点

4.继续运行到指定位置-until

使用until命令(可简写为u)在指定的行停住

(gdb)u 29 #运行到29行停住,利用的是临时断点

5.跳过执行-skip

skip命令可以在step时跳过一些不想关注的函数或者某个文件的代码

(gdb)skip function ad #step时跳过add函数

五、源码查看

我们在调试过程中难免要对照源码进行查看,如果已经开始了调试,而查看源码或者编辑源码却要另外打开一个窗口,那未免显得太麻烦。下面介绍如何在GDB调试模式下查看源码或对源码进行编辑。

1.直接打印源码

list命令(可简写为l),用来打印源码

(gdb) l

直接输入l可从第一行开始显示源码,继续输入l,可列出后面的源码。后面也可以跟上+或者-,分别表示要列出上一次列出的源码的后面部分和前面部分

2.列出指定行附近源码

l 后可以跟行号,表示要列出附近的源码

3.列出指定函数附近的源码

l后面跟函数名

(gdb) l printNum

4.设置源码一次列出行数

通过listsize属性来设置一次性列出的源码行数

(gdb) set listsize 20 #设置每次列出20行

5.列出指定行之间的源码

list first,last (两者之一可以省略一个)

(gdb) l 3,15 #列出3到15行之间的源码

6.列出指定文件的源码

l location 其中国location可以是文件名加行号或函数名

(gdb) l test.c:1  #查看指定文件的指定行数
(gdb) l test.c:printNum #查看指定文件的指定函数
(gdb) l test.c:1,test.c:3 #查看test.c文件的1到3行

7.指定源码路径

若源码移动了目录,则使用list命令查看不了源码,可以使用dir命令指定源码路径

(gdb) dir ./temp

若编译好的程序文件放到另一台机器上进行调试,或者源码文件全都移动到了其它路径下,可以使用set substitute-path from to将原来的路径替换为新的路径

借助readelf命令得到原来的源码路径:

$ readelf main -p .debug_str  #main为将要调试的程序名
(gdb) set substitute-path 原路径 新路径

8.编辑源码

直接在gdb模式下编辑源码,默认使用的编辑器是/bin/ex,

使用其它编辑器,通过下面的方式进行设置:

$EDITOR=/usr/bin/vim
$export EDITOR

借助whereis或者which命令查看编辑器的位置

使用命令edit location在gdb调试模式下进行编辑源码

(gdb)edit 3 #编辑第三行
(gdb)edit test.c:5 #编辑test.c第5行

编辑完保存后,需要重新编译程序

(gdb)shell 编译语句

注意:为了在gdb调试模式下执行shell命令,需要在命令之前加上shell,表明这是一条shell命令,这样就能在不用退出gdb调试模式的情况下编译程序了

9.另一种模式

启动时,带上tui(Text User Interface)参数,它会将调试在多个文本窗口呈现

gdb main -tui