一、GDB概述
全称GNU symbolic debugger, 简称GDB调试器,是Linux平台下一款最常用的程序调试器。
GBD是GNU开源组织发布的一个强大的UNIX下的程序调试工具。非图形界面方式。
一般来说,GDB主要帮忙完成一下四个方面的功能:
- 启动程序,可以按照自己定义的要求随心所欲的运行程序。
- 可以让被调试的程序在你所指定的调试的断点处停住。(断点可以是条件表达式)
- 当程序被停住时,可以检查此时你的程序中所发生的事情。
- 动态的改变你程序的执行环境。
GDB编译命令:g++ -g tst.c -o tst (注意如果要使用GDB进行调试,需要加上-g,表示把调试信息加到可执行文件中,否则看不到程序的函数名和变量名)
二、常用命令
命令 | 简写形式 | 说明 |
---|---|---|
backtrace | bt、where | 查看当前线程的调用堆栈 |
break | b | 设置断点 |
tbreak | tb | 设置临时断点,临时断点只生效一次 |
info break | 显示所有断点 | |
continue | c、cont | 让暂停的程序继续运行 |
delete | d | 删除断点 |
finish | fi | 结束当前调用函数,到上一层函数调用处 |
return | return | 结束当前调用函数并返回指定值,到上一层函数调用处 |
jump | j | 将当前程序执行流跳转到指定行或地址 |
disassemble | dis | 查看汇编代码 |
set args | 设置程序启动命令行参数 | |
show args | 查看设置的命令行参数 | |
watch | watch | 监视某一个变量或内存地址的值是否发生变化 |
display | 监视的变量或者内存地址,当程序中断后自动输出监控的变量或内存地址 | |
dir | dir | 重定向源码文件的位置 |
info breakpoints | 显示断点信息 | |
next | n | 执行到下一行 |
p | 打印或修改变量或寄存器值 | |
ptype | ptype | 查看变量类型 |
run | r | 运行程序 |
step | s | 如果有调用函数,进入调用的函数内部,相当于step into |
thread | thread | 切换到指定线程 |
x | 显示内存内容 | |
until | u | 运行到指定行停下来 |
directory | dir | 插入目录 |
enable | enable | 启用某个断点 |
disable | disable | 禁用某个断点 |
down | do | 在当前调用的栈帧中选择要显示的栈帧 |
edit | e | 编辑文件或者函数 |
frame | f | 切换到当前调用线程的指定堆栈 |
forward-search | fo | 向前搜索 |
generate-core-file | gcore | 生成内核转存储 |
help | h | 显示帮助一览 |
info | i | 显示信息,查看断点/线程等信息 |
list | l | 显示源码 |
nexti | ni | 执行下一行(以汇编代码为单位) |
print-object | po | 显示目标信息 |
sharelibrary | share | 加载共享的符号 |
stepi | si | 执行下一行 |
三、如何使得程序能被GDB调试
写完代码后,gcc编译代码(如果gcc编译的时候没有加上-g参数,那么就不会保留调试参数,就不能用gdb调试)
创建了test.c源文件
执行编译命令:gcc -g -o test test.c
-g选项的作用是在可执行文件中加入源代码的信息,比如可执行文件中第几条机器指令对应源代码的第几行,但并不是把整个源文件嵌入到可执行文件中,所以在调试时必须保证gdb能找到源文件。
四、启动核心转储功能
在 Linux 操作系统中,当程序执行发生异常崩溃时,系统可以将发生崩溃时的内存数据、调用堆栈情况等信息自动记录下载,并存储到一个文件中,该文件通常称为 core 文件,Linux 系统所具备的这种功能又称为核心转储(core dump)。幸运的是,GDB 对 core 文件的分析和调试提供有非常强大的功能支持,当程序发生异常崩溃时,通过 GDB 调试产生的 core 文件,往往可以更快速的解决问题。
-
查看是否启动core dump功能
ulimit -a
woke@woke-virtual-machine:~/GDBTest$ ulimit -a real-time non-blocking time (microseconds, -R) unlimited core file size (blocks, -c) 0 data seg size (kbytes, -d) unlimited scheduling priority (-e) 0 file size (blocks, -f) unlimited pending signals (-i) 53100 max locked memory (kbytes, -l) 1708744 max memory size (kbytes, -m) unlimited open files (-n) 1024 pipe size (512 bytes, -p) 8 POSIX message queues (bytes, -q) 819200 real-time priority (-r) 0 stack size (kbytes, -s) 8192 cpu time (seconds, -t) unlimited max user processes (-u) 53100 virtual memory (kbytes, -v) unlimited file locks (-x) unlimited # 如果 core file size(core 文件大小)对应的值为 0,表示当前系统未开启 core dump 功能
-
启动 core dump
ulimit -c unlimited
woke@woke-virtual-machine:~/GDBTest$ ulimit -c unlimited woke@woke-virtual-machine:~/GDBTest$ ulimit -a real-time non-blocking time (microseconds, -R) unlimited core file size (blocks, -c) unlimited data seg size (kbytes, -d) unlimited scheduling priority (-e) 0 file size (blocks, -f) unlimited pending signals (-i) 53100 max locked memory (kbytes, -l) 1708744 max memory size (kbytes, -m) unlimited open files (-n) 1024 pipe size (512 bytes, -p) 8 POSIX message queues (bytes, -q) 819200 real-time priority (-r) 0 stack size (kbytes, -s) 8192 cpu time (seconds, -t) unlimited max user processes (-u) 53100 virtual memory (kbytes, -v) unlimited file locks (-x) unlimited # unlimited 表示不限制 core 文件的大小
-
转换core dump时core文件的存放位置
echo "/home/woke/GDBTest/core_%e.%p.%t" | sudo tee /proc/sys/kernel/core_pattern # 其中/home/woke/GDBTest为自己指定的位置,之后执行该目录下的可执行文件时发生core dump就会将core文件存在该目录下。
五、启动GDB
- gdb < program >
- gdb < program > core;gdb同时调试一个运行程序和core文件,core是程序非法执行后core dump后产生的文件。
- gdb < program > PID;如果程序是服务程序,那么可以指定这个服务程序运行时的进程ID。该程序应该PATH环境变量中搜索得到。
调试已经运行的程序有两种方法:
4. 在Linux下用ps查看已经运行的程序的PID,然后用gdb PID 的格式把gdb挂接到正在运行的程序。
5. 先用gdb 关联上源代码,执行gdb,在gdb中用attach命令来挂接进程的PID,并用detach来取消挂接的进程。
其中 < program > 是可执行程序的名称,不是源代码文件的名称。
可以通过gdb --help 命令(这是没有进入gdb调试环境的)查看gdb命令的一些参数选项,用来定制gdb的行为。
进入gdb调试环境后,可以使用help命令查看在gdb调试环境中可以使用哪些命令,gdb把命令分为很多类别,可以使用help 来查看具体类别下的每一条命令。help 查看具体命令的帮助文档。
gdb调试环境中可以执行Linux下的shell命令,在gdb调试环境中通过shell 来执行。gdb中的shell命令。
gdb中还有一个make命令,用来在gdb调试环境中通过make < make-args >来重新build自己的程序。
六、通过命令行给main函数传参
有些程序在启动的时候需要在命令行传递参数,这些参数传递给main函数,要调试这类程序,这些参数必须在应用程序启动之前通过调试程序gdb进程传递进去。也就是gdb启动后,通过gdb中设置参数的命令set args … ,查看设置的命令行参数命令是show args。
# 非gdb调试命令行传参
# argc 参数总个数,argv[0] == ./app, argv[1] == "11" argv[2] == "22" ... argv[5] == "55"
$ ./app 11 22 33 44 55 # 这是数据传递给main函数
# 使用 gdb 调试
$ gdb app
GNU gdb (Ubuntu 7.11.1-0ubuntu1~16.5) 7.11.1
Copyright (C) 2016 Free Software Foundation, Inc.
# 通过gdb给应用程序设置命令行参数
(gdb) set args 11 22 33 44 55
# 查看设置的命令行参数
(gdb) show args
Argument list to give program being debugged when it is started is "11 22 33 44 55".
七、启动执行程序
gdb中启动要调试的执行程序有两种方式,一种是run命令,一种是start命令。整个gdb调试过程中,启动执行程序的命令只能使用一次。
run: 可以缩写为 r, 如果程序中设置了断点会停在第一个断点的位置, 如果没有设置断点, 程序就执行完了。
start: 启动程序, 最终会阻塞在main函数的第一行,等待输入后续其它 gdb 指令。
如果想让程序start之后继续执行或者在断点处继续执行,可以使用continue命令,简写c。
八、查看源代码
给调试的程序打断点是调试之前必须做的一项工作。为了方便定位源代码的位置,gdb提供了查看代码的命令,这样就可以轻松定位要调试的代码行的位置了。
查看代码的命令叫做list可以缩写为 l, 通过这个命令我们可以查看项目中任意一个文件中的内容,并且还可以通过文件行号,函数名等方式查看。
# 当前文件
## 一个项目中一般是有很多源文件的, 默认情况下通过list查看到代码信息位于程序入口函数main对应的的那个文件中。因此如果不进行文件切换main函数所在的文件就是当前文件, 如果进行了文件切换, 切换到哪个文件哪个文件就是当前文件。查看文件内容的方式如下:
# 使用 list 和使用 l 都可以
# 从第一行开始显示
(gdb) list
# 列值这行号对应的上下文代码, 默认情况下只显示10行内容
(gdb) list 行号
# 显示这个函数的上下文内容, 默认显示10行
(gdb) list 函数名
## 通过list去查看文件代码, 默认只显示10行, 如果还想继续查看后边的内容, 可以继续执行list命令, 也可以直接回车(再次执行上一次执行的那个gdb命令)。
# 切换文件
## 在查看文件内容的时候,很多情况下需要进行文件切换,我们只需要在list命令后边将要查看的文件名指定出来就可以了,切换命令执行完毕之后,这个文件就变成了当前文件。文件切换方式如下:
# 切换到指定的文件,并列出这行号对应的上下文代码, 默认情况下只显示10行内容
(gdb) l 文件名:行号
# 切换到指定的文件,并显示这个函数的上下文内容, 默认显示10行
(gdb) l 文件名:函数名
# 设置显示行数
## 默认通过list只能一次查看10行代码, 如果想显示更多, 可以通过set listsize设置, 同样如果想查看当前显示的行数可以通过 show listsize查看, 这里的listsize可以简写为 list。具体语法格式如下:
# 以下两个命令中的 listsize 都可以写成 list
(gdb) set listsize 行数
# 查看当前list一次显示的行数
(gdb) show listsize
九、设置断点
想要通过gdb调试某一行或者得到某个变量在运行状态下的实际值,就需要在在这一行设置断点,程序运行到断点的位置就会阻塞,我们就可以通过gdb的调试命令得到我们想要的信息了。
设置断点的命令叫做break可以缩写为b。
断点的设置有两种方式一种是常规断点,程序只要运行到这个位置就会被阻塞,还有一种叫条件断点,只有指定的条件被满足了程序才会在断点处阻塞。
调试程序的断点可以设置到某个具体的行, 也可以设置到某个函数上,具体的设置方式如下:
-
根据行号设置断点 break == b
(gdb) b 5
(gdb) b test.c:5 // 文件名:行号
-
设置普通断点到某个非当前文件上
(gdb) b 文件名:行号
(gdb) b 文件名:函数名 // 停止在函数的第一行 -
根据函数名设置断点
(gdb) b main // 函数名
-
根据条件设置断点
(gdb) b test.c:10 if a == 1
-
根据偏移量设置断点
(gdb) b +12
-
根据地址设置断点
(gdb) b *0x40059b
-
设置临时断点(临时断点只生效一次)
(gdb) tbreak test.c:12
break +offset
break -offset
在当前行号的前面或者后面的offset行停住。offset为自然数。
break filename:linenum 在源文件filename的linenum行处停住
break filename:function 在源文件filename的function函数处停住
break *address 在程序运行的内存地址处停住
break 命令没有参数时,表示在下一条指令处停住
break … if …可以是上述参数,condition表示条件,在条件成立时停住。
info breakpoints [n] 、info break [n] 查看断点,n表示断点号。
十、查看断点
通过info break命令查看设置的断点信息,info简写i。
(gdb) i b
Num Type Disp Enb Address What
1 breakpoint keep y 0x0000000000400cb5 in main() at test.cpp:12
2 breakpoint keep y 0x0000000000400cbd in main() at test.cpp:13
3 breakpoint keep y 0x0000000000400cec in main() at test.cpp:18
4 breakpoint keep y 0x00000000004009a5 in insertionSort(int*, int)
at insert.cpp:8
5 breakpoint keep y 0x0000000000400cdd in main() at test.cpp:16
6 breakpoint keep y 0x00000000004009e5 in insertionSort(int*, int)
at insert.cpp:16
# 在显示的断点信息中有一些属性需要在其他操作中被使用, 介绍如下:
## Num: 断点的编号, 删除断点或者设置断点状态的时候都需要使用
## Enb: 当前断点的状态, y表示断点可用, n表示断点不可用
## What: 描述断点被设置在了哪个文件的哪一行或者哪个函数上
十一、删除断点
删除命令是 delete 断点编号, 这个delete可以简写为del也可以再简写为d。
删除断点的方式有两种: 删除(一个或者多个)指定断点或者删除一个连续的断点区间,具体操作如下:
# delete == del == d
# 需要 info b 查看断点的信息, 第一列就是编号
(gdb) d 断点的编号1 [断点编号2 ...]
# 举例:
(gdb) d 1 # 删除第1个断点
(gdb) d 2 4 6 # 删除第2,4,6个断点
# 删除一个范围, 断点编号 num1 - numN 是一个连续区间
(gdb) d num1-numN
# 举例, 删除第1到第5个断点
(gdb) d 1-5
-
清除断点
(gdb) delete 4 # 清楚某个断点
(gdb) delete # 清除所有断点
(gdb) clear # 清除当前行断点
十二、设置断点状态
如果某个断点只是临时不需要了,我们可以将其设置为不可用状态, 设置命令为disable 断点编号,当需要的时候再将其设置回可用状态,设置命令为 enable 断点编号。
# 让断点失效之后, gdb调试过程中程序是不会停在这个位置的
# disable == dis
# 设置某一个或者某几个断点无效
(gdb) dis 断点1的编号 [断点2的编号 ...]
# 设置某个区间断点无效
(gdb) dis 断点1编号-断点n编号
# enable == ena
# 设置某一个或者某几个断点有效
(gdb) ena 断点1的编号 [断点2的编号 ...]
# 设置某个区间断点有效
(gdb) ena 断点1编号-断点n编号
# 查看断点信息
(gdb) i b
Num Type Disp Enb Address What
2 breakpoint keep y 0x0000000000400cce in main() at test.cpp:14
4 breakpoint keep y 0x0000000000400cdd in main() at test.cpp:16
5 breakpoint keep y 0x0000000000400d46 in main() at test.cpp:23
6 breakpoint keep y 0x0000000000400d4e in main() at test.cpp:25
7 breakpoint keep y 0x0000000000400d6e in main() at test.cpp:28
8 breakpoint keep y 0x0000000000400d7d in main() at test.cpp:30
# 设置第2, 第4 个断点无效
(gdb) dis 2 4
# 查看断点信息
(gdb) i b
Num Type Disp Enb Address What
2 breakpoint keep n 0x0000000000400cce in main() at test.cpp:14
4 breakpoint keep n 0x0000000000400cdd in main() at test.cpp:16
5 breakpoint keep y 0x0000000000400d46 in main() at test.cpp:23
6 breakpoint keep y 0x0000000000400d4e in main() at test.cpp:25
7 breakpoint keep y 0x0000000000400d6e in main() at test.cpp:28
8 breakpoint keep y 0x0000000000400d7d in main() at test.cpp:30
# 设置 第5,6,7,8个 断点无效
(gdb) dis 5-8
# 查看断点信息
(gdb) i b
Num Type Disp Enb Address What
2 breakpoint keep n 0x0000000000400cce in main() at test.cpp:14
4 breakpoint keep n 0x0000000000400cdd in main() at test.cpp:16
5 breakpoint keep n 0x0000000000400d46 in main() at test.cpp:23
6 breakpoint keep n 0x0000000000400d4e in main() at test.cpp:25
7 breakpoint keep n 0x0000000000400d6e in main() at test.cpp:28
8 breakpoint keep n 0x0000000000400d7d in main() at test.cpp:30
# 设置第2, 第4个断点有效
(gdb) ena 2 4
# 查看断点信息
(gdb) i b
Num Type Disp Enb Address What
2 breakpoint keep y 0x0000000000400cce in main() at test.cpp:14
4 breakpoint keep y 0x0000000000400cdd in main() at test.cpp:16
5 breakpoint keep n 0x0000000000400d46 in main() at test.cpp:23
6 breakpoint keep n 0x0000000000400d4e in main() at test.cpp:25
7 breakpoint keep n 0x0000000000400d6e in main() at test.cpp:28
8 breakpoint keep n 0x0000000000400d7d in main() at test.cpp:30
# 设置第5,6,7个断点有效
(gdb) ena 5-7
# 查看断点信息
(gdb) i b
Num Type Disp Enb Address What
2 breakpoint keep y 0x0000000000400cce in main() at test.cpp:14
4 breakpoint keep y 0x0000000000400cdd in main() at test.cpp:16
5 breakpoint keep y 0x0000000000400d46 in main() at test.cpp:23
6 breakpoint keep y 0x0000000000400d4e in main() at test.cpp:25
7 breakpoint keep y 0x0000000000400d6e in main() at test.cpp:28
8 breakpoint keep n 0x0000000000400d7d in main() at test.cpp:30
十三、设置观察点
观察点一般用来观察某个表达式(变量也是表达式)的值是否发生变化,如果有变化,立马停住程序。
设置观察点的方法:
命令 | 用途 |
---|---|
watch < expr > | 为表达式expr设置一个观察点,一旦表达式的值发生变化,马上停住程序 |
rwatch < expr > | 当表达式被读时,停住程序 |
awatch < expr > | 当表达式的值被读或者被写时,停住程序 |
info watchpoints | 列出当前设置的所有观察点 |
十四、设置捕捉点
设置捕捉点来捕捉程序运行时的一些事件。
设置捕捉点格式: catch < event >
当event发生时,停住程序。event可以是下面的内容:
十五、维护停止点
以上的三种点,就是gdb中的三类停止点。如果觉得定义好的停止点没用了,可以使用delete、clear、disable、enable这几个命令来维护。
命令 | 用途 |
---|---|
clear | 清除所有的已定义的停止点 |
clear < function > ; clear < filename:function > | 清除所有设置在函数上的停止点 |
clear < linenum > ; clear < filename : linenum > | 清楚所有设置在指定行上的停止点 |
delete [ breakpoints ] [range…] | 删除指定断点,breakpoints为断点号,如果不指定断点号表示删除所有断点。range表示断点号范围,如:3-7 |
disable [ breakpoints ] [range…] | 比删除更好的一种方式是disable停止点,disable之后,gdb不会删除,当还需要,enable即可。简写dis |
enable [ breakpoints ] [range…] | enbale所指定的停止点 |
enable [ breakpoints ] once range … | enable 指定停止点一次,当程序停止后,该停止点马上被gdb自动disable |
enable [ breakpoints ] delete range … | enable 指定停止点一次,当程序停止后,该停止点马上被gdb自动删除 |
十六、停止条件维护
设置条件后,当条件成立时,程序自动停止。使用if关键字,为断点设置条件。条件设置好后,使用condition命令来修改断点条件。(只有break、watch支持if,catch不支持if)
命令 | 用途 |
---|---|
condition < bnum > < exp > | 修改断点号为bnum的停止条件为exp |
condition < bnum > | 清楚断点号bnum的停止条件 |
十七、断点菜单
可能会出现同一个名字的函数多次,这时,break < function > 不能告诉gdb要停在哪个函数的入口。gdb会列出一个断点菜单供你选择所需要的断点,你只需要输入菜单列表编号就可以。
以上gdb列出了所有after的重载函数,选择一下列表编号就好了。0表示放弃设置断点,1表示所有函数都设置断点。
十八、查看栈信息
程序被停住,当调用了函数时,函数的地址、函数参数、函数内的局部变量都会被压入栈中。
查看函数调用栈信息的命令:backtrace or bt
命令 | 用途 |
---|---|
backtrace < n > | n是一个整数表示只打印栈顶上的n层的栈信息 |
backtrace < -n > | -n表示一个负整数,表示只打印栈底n层栈信息 |
如果要切换当前的栈层,一般来说,程序停止时最顶层就是当前栈,如果要查看栈下面层的信息,首先要做切换当前栈。
命令 | 用途 |
---|---|
frame < n > | n是一个整数,是栈中层的编号,0表示栈顶,1表示栈的第二层 |
up < n > | 表示向栈的上面移动n层,可以不打n,表示向上移动一层 |
down < n > | 向下移动 |
十九、运行
- 运行 r
- 继续单步调试 n
- 继续执行到下一个断点 c
- 查看源码和行号 l #不断输入 l 并回车会不断显示源码
以下命令都是在程序运行起来才起作用
-
打印变量
p a
-
打印指针
p p # 第二个p是一个指针
-
打印main函数中的变量a
p ‘main’::a
-
打印指针指向的内容,@后面跟的是打印的长度
p *p@3 #如果p指向数组,则打印数组前三个元素。
-
设置变量打印
set $index = 0
p p[$index] # p是一个指向数组的指针,其实就是下标取数组元素
-
设置打印格式
- x 按十六进制格式显示变量
- d 按十进制格式显示变量
- u 按十六进制格式显示无符号整型
- o 按八进制格式显示变量
- t 按二进制格式显示变量
- a 按十六进制格式显示变量
- c 按字符格式显示变量
- f 按浮点数格式显示变量
(gdb) p/x a(按十六进制格式显示变量)
(gdb) p/x a
$7 = 0x1
二十、调试
如果调试的程序被断点阻塞了又想让程序继续执行,这时候就可以使用continue命令。程序会继续运行, 直到遇到下一个有效的断点。continue可以缩写为 c。
20.1 打印信息
当程序被某个断点阻塞之后, 可以通过一些命令打印变量的名字或者变量的类型,并且还可以跟踪打印某个变量的值。
# 打印变量的值
## 在gdb调试的时候如果需要打印变量的值, 使用的命令是 print, 可缩写为 p。如果打印的变量是整数还可以指定输出的整数的格式, 格式化输出的整数对应的字符表如下:
# 格式化字符(/fmt) 说明
# /x 以十六进制的形式打印出整数。
# /d 以有符号、十进制的形式打印出整数。
# /u 以无符号、十进制的形式打印出整数。
# /o 以八进制的形式打印出整数。
# /t 以二进制的形式打印出整数。
# /f 以浮点数的形式打印变量或表达式的值。
# /c 以字符形式打印变量或表达式的值。
# print == p
(gdb) p 变量名
# 如果变量是一个整形, 默认对应的值是以10进制格式输出, 其他格式请参考上表
(gdb) p/fmt 变量名
# 举例
(gdb) p i # 10进制
$5 = 3
(gdb) p/x i # 16进制
$6 = 0x3
(gdb) p/o i # 8进制
$7 = 03
# 打印变量类型
## 在调试过程中需要查看某个变量的类型, 可以使用命令ptype, 语法格式如下:
(gdb) ptype 变量名
# 打印变量类型
(gdb) ptype i
type = int
(gdb) ptype array[i]
type = int
(gdb) ptype array
type = int [12]
# 自动打印信息
## 设置变量名自动显示
# 和 print 命令一样,display 命令也用于调试阶段查看某个变量或表达式的值,它们的区别是,使用 display 命令查看变量或表达式的值,每当程序暂停执行(例如单步执行)时,GDB 调试器都会自动帮我们打印出来,而 print 命令则不会。因此,当我们想频繁查看某个变量或表达式的值从而观察它的变化情况时,使用 display 命令可以一劳永逸。display 命令没有缩写形式,常用的语法格式如下 2 种:
# 在变量的有效取值范围内, 自动打印变量的值(设置一次, 以后就会自动显示)
(gdb) display 变量名
# 以指定的整形格式打印变量的值, 关于 fmt 的取值, 请参考 print 命令
(gdb) display/fmt 变量名
## 查看自动显示列表
## 对于使用 display 命令查看的目标变量或表达式,都会被记录在一张列表(称为自动显示列表)中。通过执行info dispaly命令,可以打印出这张表:
# info == i
(gdb) info display
Auto-display expressions now in effect:
Num Enb Expression
1: y i
2: y array[i]
3: y /x array[i]
## 在展示出的信息中, 每个列的含义如下:
# Num : 变量或表达式的编号,GDB 调试器为每个变量或表达式都分配有唯一的编号
# Enb : 表示当前变量(表达式)是处于激活状态还是禁用状态,如果处于激活状态(用 y 表示),则每次程序停止执行,该变量的值都会被打印出来;反之,如果处于禁用状态(用 n 表示),则该变量(表达式)的值不会被打印。
# Expression :被自动打印值的变量或表达式的名字。
## 取消自动显示
## 对于不需要再打印值的变量或表达式,可以将其删除或者禁用。
## 删除自动显示列表中的变量或表达式
# 命令中的 num 是通过 info display 得到的编号, 编号可以是一个或者多个
(gdb) undisplay num [num1 ...] # 删除 是删除
# num1 - numN 表示一个范围
(gdb) undisplay num1-numN
(gdb) delete display num [num1 ...] # 删除
(gdb) delete display num1-numN
# 查看显示列表
(gdb) info display
Auto-display expressions now in effect:
Num Enb Expression
1: y i
2: y array[i]
3: y /x array[i]
# 删除变量显示, 需要使用 info display 得到的变量/表达式编号
(gdb) undisplay 1 2
# 查看显示列表, 只剩下一个了
(gdb) i display
Auto-display expressions now in effect:
Num Enb Expression
3: y /x array[i]
## 如果不想删除自动显示的变量, 也可以禁用自动显示列表中处于激活状态下的变量或表达式、
# 命令中的 num 是通过 info display 得到的编号, 编号可以是一个或者多个
(gdb) disable display num [num1 ...]
# num1 - numN 表示一个范围
(gdb) disable display num1-numN
## 当需要启用自动显示列表中被禁用的变量或表达式时, 可以使用下边的命令
# 命令中的 num 是通过 info display 得到的编号, 编号可以是一个或者多个
(gdb) enable display num [num1 ...]
# num1 - numN 表示一个范围
(gdb) disable display num1-numN
20.2 单步调试
当程序阻塞到某个断点上之后, 可以通过以下命令对程序进行单步调试:
# step命令
## step命令可以缩写为s, 命令被执行一次代码被向下执行一行,如果这一行是一个函数调用,那么程序会进入到函数体内部。
# 从当前代码行位置, 一次调试当前行下的每一行代码
# step == s
# 如果这一行是函数调用, 执行这个命令, 就可以进入到函数体的内部
(gdb) step
# finish命令
## 如果通过 s 单步调试进入到函数内部, 想要跳出这个函数体, 可以执行finish命令。如果想要跳出函数体必须要保证函数体内不能有有效断点,否则无法跳出。
# 如果通过 s 单步调试进入到函数内部, 想要跳出这个函数体
(gdb) finish
# next命令
## next命令和step命令功能是相似的,只是在使用next调试程序的时候不会进入到函数体内部,next可以缩写为 n
# next == n
# 如果这一行是函数调用, 执行这个命令, 不会进入到函数体的内部
(gdb) next
# util命令
## 通过 until 命令可以直接跳出某个循环体,这样就能提高调试效率了。如果想直接从循环体中跳出, 必须要满足以下的条件,否则命令不会生效:
### 1. 要跳出的循环体内部不能有有效的断点
### 2. 必须要在循环体的开始/结束行执行该命令
(gdb) until
20.3 设置变量值
在调试程序的时候, 我们需要在某个变量等于某个特殊值的时候查看程序的运行状态, 但是通过程序运行让变量等于这个值又非常困难, 这种情况下就可以在 gdb 中直接对这个变量进行值的设置, 或者是在单步调试的时候通过设置循环因子的值直接跳出某个循环, 值设置的命令格式为: set var 变量名=值
# 可以在循环中使用, 直接设置循环因子的值
# 假设某个变量的值在程序中==90的概率是5%, 这时候可以直接通过命令将这个变量值设置为90
(gdb) set var 变量名=值
二十一、信号
GDB中handle命令捕获指定的信号,并停止程序。
命令 | 用途 |
---|---|
handle < signale > < keywords > | signal表示信号,也可以是一个信号范围:SIGIO-SIGKILL |
keywords的取值:
二十二、退出GDB
quit 简写q
学习资料
张小方 https://zhuanlan.zhihu.com/p/548379635
爱编程的大丙 https://subingwen.cn/linux/gdb/
参考:https://blog.youkuaiyun.com/lovely_dzh/article/details/109160337