GDB之(1)基础入门指令参数介绍
Author:Once Day Date: 2022年7月29日/2024年2月26日
漫漫长路,才刚刚开始…
全系列文章请查看专栏: Linux实践记录_Once-Day的博客-CSDN博客
推荐参考文档:
- GDB: The GNU Project Debugger (sourceware.org)
- GDB Documentation (sourceware.org)
- 用GDB调试程序 _CSDN博客 _陈皓
- linux下gdb调试方法与技巧整理-CSDN博客_花开蝶自来
文章目录
- GDB之(1)基础入门指令参数介绍
- 1.引言
- 2. 程序运行时,可设置的环境和变量
- 2.1 程序运行参数
- 2.2 运行环境
- 2.3 工作目录
- 2.4 程序的输入输出
- 3.暂停程序
- 3.1 设置断点
- 3.2 设置观察点
- 3.3 设置捕捉点
- 4.清除停止点
- 5. 恢复程序运行和单步调试
- 6.信号处理
- 7. 多线程
- 8. 查看栈信息
- 9.显示源代码
- 9.1 源码搜索
- 9.2 指定源文件的路径
- 9.3 查看源代码在内存中的地址
- 10. 查看运行时数据
- 10.1 使用examine(x)查看内存地址的值
- 10.2 自动显示
- 11.设置显示(print)选项
- 11.2 print历史记录
- 11.3 GDB环境变量
- 11.4 查看寄存器
- 12. 查看内存映射信息
- 12.1 查看虚拟地址映射情况
- 12.2 查看动态库加载情况
- 12.3 加载特定动态库的符号表
1.引言
使用gcc编译c/c++时,需要使用-g
来生成调试信息。
启动gdb有以下几种,program即需要调试的执行文件:
-
gdb <program>
-
gdb <program> core
,配合coredump进行调试。 -
gdb <program> <PID>
,指定服务程序的运行时的进程ID,gdb会自动attach上去。
启动时可以加入一些常用的参数:
-
-s
,从指定文件读取符号表 -
-se
,从指定文件读取符号表信息,并把他用在可执行文件中 -
-c
,调试时,coredump的core文件 -
-d
,加入一个源文件的搜索路径,默认搜索路劲是环境变量中PATH所定义的路径
gdb环境之下可以运行shell命令以及make命令:
(gdb) shell ls
(gdb) make [args]
2. 程序运行时,可设置的环境和变量
2.1 程序运行参数
set设置程序开始运行时的输入参数,show展示目前的参数列表。
(gdb) set args 15 "145" 155 145
(gdb) show args
Argument list ... is "15 "145" 155 145".
用于main(int,char*)的参数输入。
2.2 运行环境
path设定程序的运行路径,show查看程序的运行路径,set environment 设置环境变量,show environment查看环境变量。
(gdb) path .
Executable and object file path: /home/onceday/new:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin:/snap/bin
(gdb) show path
Executable and object file path: /home/onceday/new:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin:/snap/bin
(gdb) set environment ONCE=day
(gdb) show environment ONCE
ONCE = day
环境变量用于main(argc,argv,env)中的env。
2.3 工作目录
cd切换目录, pwd显示当前目录。
2.4 程序的输入输出
info terminal
显示你程序用到的终端模式
run > outfile
使用重定向控制程序输出
tty /dev/ttyb
可以指写输入输出的终端设备
3.暂停程序
当程序暂停时,可用info program来查看程序信息。
有如下几种暂停方式:
-
断点(Breakpoint)
-
观察点(WatchPoint)
-
捕捉点(CatchPoint)
-
信号(Signals)
-
线程停止(Thread Stops)
可使用c
或者continue
命令恢复程序运行。
3.1 设置断点
-
break <function>
,在进入指定函数时停住, 可以增加类名和类型名 -
break <linenum>
,在指定行号停住 -
break +offset
/break -offset
,在当前行号的前面或后面offset行停住 -
break filename:linenum
,在源文件filename的linenum行处停住 -
break filename:function
,在源文件filename的function函数的入口处停住 -
break *address
,在程序运行的内存地址处停住 -
break
,在下一条指令处停住 -
break ... if <condition>
,...
表示上述的参数,这个可以在条件成立时停住。
3.2 设置观察点
观察点一般是来观察某个表达式(变量也是一种表达式)的值是否有变化,如果有变化,马上停止程序。
-
watch <expr>
,为表达式(变量),设置观察点,一旦表达式值有变化,马上停止程序。 -
rwatch <expr>
,当表达式(变量),被读时,停住程序。 -
awatch <expr>
,当表达式(变量)的值被读或者写的时候,停住程序。 -
info watchpoints
,列出当前所设置了的所有观察点。
3.3 设置捕捉点
可以设置捕捉点来捕捉程序运行时的一些事情,如:载入共享库(动态链接库)或是C++的异常。
catch <event>
catch assert -- Catch failed Ada assertions, when raised.
catch catch -- Catch an exception, when caught.
catch exception -- Catch Ada exceptions, when raised.
catch exec -- Catch calls to exec.
catch fork -- Catch calls to fork.
catch handlers -- Catch Ada exceptions, when handled.
catch load -- Catch loads of shared libraries.
catch rethrow -- Catch an exception, when rethrown.
catch signal -- Catch signals by their names and/or numbers.
catch syscall -- Catch system calls by their names, groups and/or numbers.
catch throw -- Catch an exception, when thrown.
catch unload -- Catch unloads of shared libraries.
catch vfork -- Catch calls to vfork.
4.清除停止点
清除停止点:
-
clear
,清除所有的已定义的停止点。 -
clear <[filename:]function>
,清除所有设置在函数上的停止点。 -
clear <[filename:]function>
,清除所有设置在指定行上的停止点。 -
delete breakpoints
,删除指定的断点,breakpoints为断点号。 -
delete range...
,删除指定范围内的断点。 -
delete
,删除所有断点。 -
disable breakpoints
,停止指定的断点。 -
disable range...
,停止指定范围内的断点。 -
disable
,停止所有断点 -
enable [breakpoints] [range...]
,使能断点 -
enable [breakpoints] once range...
,使能一次,程序停止后 ,会被GDB自动disable。 -
enable [breakpoints] delete range...
,使能一次,程序停止后,会被GDB自动删除。
可以用condition修改断点的停止条件,即满足后面的表达式:
condition <bnum> <expression> #修改断点号bnum的停止条件为expression
condition <bnum> #清除断点号为bnum的停止条件
比较特殊的维护命令ignore,可以指定程序运行,忽略停止条件几次。
ignore <bnum> <count>
还可以为停止点设定运行命令:
当运行程序被停止住时,可以让其自动运行一些别的命令。
commands [bnum]
... command-list ...
end
实例如下:
break foo if x>0
commands
printf "x is %d/n",x
continue
end
5. 恢复程序运行和单步调试
当程序被停住了,可以用continue命令恢复程序的运行直到程序结束,或者下一个断点的到来。如下三个命令:
continue [ignore-count]
c [ignore-count]
fg [ignore-count]
ignore-count
表示忽略其后的断点次数。
单步跟踪step,如果有函数调用,会进入该函数,前提是此函数被编译有debug信息。
step <count> #count表示执行指令的条数,不加则为1
单步跟踪next,如果有函数调用,不会进入该函数。
next <count> #count表示执行指令的条数,不加则为1
可以打开step-mode模式,在进行单步跟踪时,程序不会因为没有debug信息而靠不住。
set step-mode on
set step-mode off
finish运行程序,直到当前函数完成返回,并打印函数返回时的堆栈地址和返回值即参数值等信息。
until 或 u 可以在一个循环体内单步跟踪,这个命令可以运行程序直到退出循环体。
stepi或si,nexti或ni:单步跟踪一条机器指令,一条程序代码有可能由数条机器指令完成。
6.信号处理
GDB可以在调试程序的时候处理任何一种信号。当收到指定的信号时,拿上停住正在运行的程序,以供你进行调试。
handle <signal> <keywords ...>
信号可以书写为:SIGIO-SIGKILL
。
关键字有以下几种:
-
nostop
,收到信号时,GDB不会停住,但可以打印出消息。 -
stop
,当被调试的程序收到信号时,GDB会停住你的程序。 -
print
,当被调试的程序收到信号时,GDB会显示出一条信息。 -
noprint
,当被调试的程序收到信号时,GDB不会告诉你收到信号的信息。 -
pass
noignore
,当被调试的程序收到信号时,GDB不处理信号。这表示,GDB会把这个信号交给被调试程序会处理。 -
nopass
ignore
,当被调试的程序收到信号时,GDB不会让被调试程序来处理这个信号。
可以使用info signals
、info handle
查看有哪些信号在被GDB检测中。
7. 多线程
在多线程上定义断点:
break <linespec> thread <threadno>
break <linespec> thread <threadno> if ...
linespec指定了断点设置在的源程序的行号。threadno指定了线程的ID。
ID由GDB分配的,可以通过“info threads”命令来查看正在运行程序中的线程信息。
如果不指定thread <threadno>
,则表示断点设在所有线程上面。
还可以为某线程指定断点条件。如
(gdb) break frik.c:13 thread 28 if bartab > lim
当程序被GDB停住时,所有的运行线程都会被停住。
而在恢复程序运行时,所有的线程也会被恢复运行。那怕是主进程在被单步调试时。
8. 查看栈信息
当程序被停住,可以通过查看栈信息来确认执行情况:
backtrace
bt #函数调用栈
bt <n> #n是正整数,表示只打印栈顶上n层的栈信息
bt <-n> #-n表示一个负整数,表示只打印栈底下n层的栈信息
查看某一层栈的信息:
frame <n>
f <n>
n是一个从0开始的整数,是栈中的层编号,比如,frame 0
表示栈顶,frame 1
表示栈的第二层。
up <n> #表示向栈的上面移动n层,可以不打n,表示向上移动一层。
down <n> #表示向下移动n层。
以下命令不打印出移动的栈层信息:
select-frame <n> 对应于 frame 命令。
up-silently <n> 对应于 up 命令。
down-silently <n> 对应于 down 命令。
输出当前栈层信息的命令:
-
可以使用
info frame
或info f
打印出更为详细的当前栈层信息。 -
info args
打印当前函数的参数名及其值。 -
info locals
打印出当前函数中所有局部变量及其值。 -
info catch
打印出当前的函数中的异常处理信息。
9.显示源代码
编译时需要加上-g
参数。
list <[function:]linenum> #显示程序第linenum行的周围的源程序
list <[filename:]function> #显示函数名为function的函数的源程序。
list #显示当前行后面的源程序。
list - #显示当前行前面的源程序
设置一次显示源代码的行数:
set listsize <count>show listsize #查看当前listsize的设置
还有以下的选行显示:
list <first> ,<last> #显示从first行到last行之间的源代码
list , <last>· #显示从当前行到last行之间的源代码
list + #往后显示源代码
9.1 源码搜索
可搭配正则表达式进行源代码搜索:
forward-search <regexp>
search <regexp> #向前面搜索reverse-search <regexp> #全部搜索
9.2 指定源文件的路径
directory <dirname [: dirname2 ....]>
dir <dirname [: dirname2 ....]>show directories #显示定义了的源文件搜索路径
9.3 查看源代码在内存中的地址
打印出所指定的源码在运行时的内存地址:
info line 行业/函数名/文件名:行号/文件名:函数名
查看源程序的当前执行时的机器码,这个命令会把目前内存中的指令dump出来:
disassemble 函数名....
10. 查看运行时数据
print <expr>
print /<f> <expr>
<expr>
是表达式,是你所调试的程序的语言的表达式(GDB可以调试多种编程语言),<f>
是输出的格式。
print while(cur)
表达式可支持以下几种操作符:
-
::
,指定一个在文件或是在一个函数中的变量。 -
@
,一个和数组有关的操作符 -
{<type>} <addr>
,表示一个指向内存地址的类型为type的一个对象。
示例如下:
p 'file'::variable
p function::variable
p "f2.c"::x
p *array@len #int *array = (int *) malloc (len * sizeof (int));
静态数组直接print数组名即可输出数组内容。
</f>
格式有以下几种:
-
x 按十六进制格式显示变量。
-
d 按十进制格式显示变量。
-
u 按十六进制格式显示无符号整型。
-
o 按八进制格式显示变量。
-
t 按二进制格式显示变量。
-
a 按十六进制格式显示变量。
-
c 按字符格式显示变量。
-
f 按浮点数格式显示变量。
示例如下:
(gdb) p /f i
$24 = 1.41531145e-43
10.1 使用examine(x)查看内存地址的值
x [/<n/f/u>] <addr>
可选参数:
-
n,是一个正整数,表示显示内存的长度。
-
f,表示显示的格式,即上面所展示的。
-
u,表示从当前地址往后请求的字节数。默认是4个bytes。以下为预设的几种字符:
-
b表示单字节
-
h表示双字节
-
w表示四字节
-
g表示八字节
-
示例:
x/3uh 0x54320
表示从内存地址0x54320读取内容,h表示以双字节为一个单位,3表示三个单位,u表示按十六进制显示。
10.2 自动显示
当程序停住时,这些变量可以自动显示。
display <expr>
display /<fmt> <expr>
示例:
display /i $pc
$pc是GDB的环境变量,表示着指令的地址,/i 输出汇编指令。
删除自动显示:
undisplay <dnums...>
delete display <dnums...>
<dnums>
是需要自动显示的变量的序列号。
使用info display
可以观察当前所有的自动显示变量,以及它们的序列号。
使用示例:
delete display 2,3 #同时删除序列号2和3对应的自动显示变量
disable display 2,3 #同时失效序列号2和3对应的自动显示变量
enable display 2-5 #同时使能序列号2-5范围内的所有对应自动显示变量
11.设置显示(print)选项
1.打开地址输出,当程序显示函数信息时,GDB会显出函数的参数地址。系统默认为打开的
set print address on
set print address off
2.打开数组显示,打开后当数组显示时,每个元素占一行,如果不打开的话,每个元素则以逗号分隔。这个选项默认是关闭的。
set print array
set print array on
3.设置数组显示的最大长度以及显示当前print elements
的选项信息。
set print elements <number-of-elements>
show print elements
4.当显示字符串时,遇到结束符则停止显示,这个选项默认为off。
set print null-stop <on/off>
5.以漂亮的格式显示结构体等内容。
set print pretty on
set print pretty off
6.设置字符显示,是否按“/nnn”的格式显示
set print sevenbit-strings <on/off>
7.设置显示结构体时,是否显式其内的联合体数据。
set print union <on/off>
8.对象、成员、虚函数等C++类设置
set print object <on/off> #对对象指针的处理方法
在C++中,如果一个对象指针指向其派生类,如果打开这个选项,GDB会自动按照虚方法调用的规则显示输出,如果关闭这个选项的话,GDB就不管虚函数表了。这个选项默认是off。
set print static-members <on/off>
这个选项表示,当显示一个C++对象中的内容是,是否显示其中的静态数据成员。默认是on。
set print vtbl <on/off>
当此选项打开时,GDB将用比较规整的格式来显示虚函数表时。其默认是关闭的。
11.2 print历史记录
GDB会记录当前运行过程中产生的print记录,并以$1
、$2
、$3
…这样的方式来编号。
那么就可以使用$1
这样的方式来重新输入之前的print表达式。
11.3 GDB环境变量
可以在GDB的调试环境中定义自己的变量,用来保存一些调试程序中的运行数据。
使用以下命令即可定义一个GDB变量:
set $foo = *object_ptr
使用以下命令可以查看当前设置的所有的环境变量。
show convenience
环境变量可以和程序变量交互使用:
set $i = 0
print bar[$i++]->contents
11.4 查看寄存器
使用以下命令即可查看寄存器情况(不包括浮点寄存器):
info registers
以下命令可以包括浮点寄存器:
info all-registers
查看所指定的寄存器的情况:
info registers <regname ...>
也可以使用print
命令访问($+寄存器名):
print $sp
12. 查看内存映射信息
12.1 查看虚拟地址映射情况
info proc mapping
12.2 查看动态库加载情况
info sharedlibrary
12.3 加载特定动态库的符号表
当你在 GDB 中调试一个程序,并且这个程序在运行时加载了动态库,你可以使用 info sharedlibrary
命令查看已加载的动态库及其状态。例如:
(gdb) info sharedlibrary
这将显示所有已加载的动态库及其加载状态。如果某个动态库的符号表还没有加载,你可以使用 sharedlibrary
命令来加载它。例如,如果你想加载所有动态库的符号表,你可以使用:
(gdb) sharedlibrary
如果你只想加载特定动态库的符号表,你可以提供库的名称作为参数。例如,如果你想加载名为 libmylib.so
的库的符号表,你可以使用:
(gdb) sharedlibrary libmylib
请注意,你必须确保 GDB 能找到动态库的符号表。这通常意味着你需要在系统的库路径(例如 /usr/lib
或 /lib
)或者 GDB 的库路径(可以使用 set solib-search-path
命令设置)中有动态库的调试版本。
Once Day
也信美人终作土,不堪幽梦太匆匆......
如果这篇文章为您带来了帮助或启发,不妨点个赞👍和关注,再加上一个小小的收藏⭐!
(。◕‿◕。)感谢您的阅读与支持~~~