1. 运行
run/r 运行
运行带参数的可执行文件:r 后面接参数,例如:
$ gdb executablefile
(gdb) r arg1 arg2 arg3
continue/c 继续运行
next/n 单步运行
step/s 如果有函数则进入函数执行
finish 跳出当前的函数
jump/j 跳转到指定行/地址后继续执行,因此如果在跳转的目标行上如果没有设置断点,会继续往下执行
stop 停止运行
until xxx 可用于跳出循环
quit/ctrl+d 退出GDB
2. 打印
print/p var 打印变量的值
print/p &var 打印变量地址
printf/p *addr 打印地址的值
printf/p /x var 用16进制显示数据
x/(n,f,u为可选参数) 查看内存内容,与print不同的是,x后面接内存地址。
n: 需要显示的内存单元个数,也就是从当前地址向后显示几个内存单元的内容,一个内存单元的大小由后面的u定义
f:显示格式
x(hex) 按十六进制格式显示变量。
d(decimal) 按十进制格式显示变量。
u(unsigned decimal) 按十进制格式显示无符号整型。
o(octal) 按八进制格式显示变量。
t(binary) 按二进制格式显示变量。
a(address) 按十六进制格式显示变量。
c(char) 按字符格式显示变量。
f(float) 按浮点数格式显示变量
u:每个单元的大小,按字节数来计算。默认是4 bytes。GDB会从指定内存地址开始读取指定字节,并把其当作一个值取出来,并使用格式f来显示
b:1 byte h:2 bytes w:4 bytes g:8 bytes
比如x/10xw 0x54320(4字节显示单位等同于x/10x 0x54320)表示从内存地址0x54320读取内容,w表示以4字节为单位,10表示输出10个内存单位,x表示按照十六进制显示。
x/50x $rsp 打印堆栈前50个内存单元的内容,每个单元默认占用4个字节
设置demangling:
set print asm-demangle on
set print demangle on
x/s <地址> 命令用于显示内存中的字符串,比如:
(gdb) p str
$1 = 0x601050 "Hello, GDB!"
(gdb) x/s str
0x601050: "Hello, GDB!"
(gdb) x/s 0x601050
0x601050: "Hello, GDB!"
x/i <地址> 命令用于显示内存中的指令,比如:
(gdb) x/i 0x4005d0
0x4005d0: callq 0x400570 <foo>
(gdb) x/i $pc
0x4005d0: callq 0x400570 <foo>
3. 设置断点
break/b xxx 在某行打断点
break/b fun 在某个函数处加断点
b xxx if (condition)条件断点,
例如:
b test.c:30 if n==100 //当变量n等于100的时候在test.c的30行处加断点.
b test_function if $_streq(my_arg1,"test1") //当字符串参数my_arg1等于"test1"的时候加断点
$_streq是gdb内置函数,具体参见参考文献。
break fileName:N 在某个文件的N行加断点
break fileName:func 在某个文件的某个函数加断点
info break/b [n] 查看断点,(注:n表示断点号,可选。info b列出所有断点信息)
clear N 删除N行断点
delete N 删除N号断点
delete 删除所有断点
disable xxx 失能断点
enable xxx 使能断点
info b 查看断点
watch xxx 设置监控点,在变量改变的时候停下来。(不可直接设置,先加断点在监测)
4. info 查看
info source 查看当前程序
info symbol查看地址对应的符号
info args 打印出当前函数的参数值。
info locals 打印出当前函数中所有局部变量值。
info registers/r 查看所有寄存器的值
info registers/r后面接寄存器名 查看某个寄存器的值
例如:
(gdb) info r rbp
rbp 0x7ff064d078c0 0x7ff064d078c0
info proc mappings 查看动态库(包含映射地址段)
# info line 可以从汇编指令找到对应的源代码行数,或者反过来,由源代码行数找到对应的汇编指令。
4.1 从汇编指令找源代码
info line *0xxxxx 查看地址对应的function,类似于addr2line
例如:
(gdb) info line *0x40ac8810
Line 66 of "../../../../../../my_test.cpp" starts at address 0x40ac8810 <test_port(void*)+352> and ends at 0x40ac8819 <test_port(void*)+361>.
4.2 从源代码找汇编指令
info line filename:linenum
pwd查看程序路径
ctrl+p 前一条命令
ctrl+n 下一条命令
ctrl+l可能layout会造成控制台花屏,使用ctrl+L清屏
5. disass/dissassemble反汇编
5.1 disass [function]
function是需要进行反汇编的函数名,例如
(gdb) disassemble main
Dump of assembler code for function main:
0x4005d0 <+0>: push %rbp
0x4005d1 <+1>: mov %rsp,%rbp
0x4005d4 <+4>: callq 0x400570 <foo>
0x4005d9 <+9>: pop %rbp
0x4005da <+10>: retq
End of assembler dump.
5.2 disass [address]
address是需要进行反汇编的地址。
5.3 disass [addr_start] [addr_end]
反汇编起始地址和结束地址之间的代码,例如
(gdb) disassemble 0x4005d0 0x4005e0
Dump of assembler code from 0x4005d0 to 0x4005e0:
0x4005d0: callq 0x400570 <foo>
0x4005d5: mov %eax,%ebx
0x4005d7: add $0x1,%ebx
0x4005da: retq
End of assembler dump.
5.4 disass [addr_start],+[offset]
反汇编起始地址+偏移量之间的代码。例如
(gdb) disass 0x0000000000400937,+10
Dump of assembler code from 0x400937 to 0x400941:
0x0000000000400937 <main()+49>: mov $0x601060,%edi
0x000000000040093c <main()+54>: callq 0x4007e0 <std::basic_ostream<char, std::char_traits<char> >& std::operator<< <std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&, char const*)@plt>
End of assembler dump.
(gdb) disass 0x0000000000400937,+1
Dump of assembler code from 0x400937 to 0x400938:
0x0000000000400937 <main()+49>: mov $0x601060,%edi
End of assembler dump.
5.5 disass /m [addr]
结合源码查看汇编,比如:
(gdb) disass /m 0x00000000004005f6
Dump of assembler code for function main:
5 {
0x00000000004005e6 <+0>: push %rbp
0x00000000004005e7 <+1>: mov %rsp,%rbp
0x00000000004005ea <+4>: sub $0x10,%rsp
6 char hi_ch[10] = {0};
=> 0x00000000004005ee <+8>: movq $0x0,-0xa(%rbp)
0x00000000004005f6 <+16>: movw $0x0,-0x2(%rbp)
7 //int *hi_int = (int*)884097;
8 //printf("hi_int: %d\n", *hi_int);
9
10 printf("%.*s\n",2,"123");
6. 多线程
info threads 查看线程
thread apply all bt 查看所有线程back trace
thread 查看当前thread
例如:
(gdb) thread
[Current thread is 7 (Thread 1572)]
多线程下(禁止|允许|单步调试时禁止)线程切换:
set scheduler-locking on|off|step
show scheduler-locking: 查看gdb线程工作模式,默认为step, 即在调试的时候可以暂停其他线程的执行,以免线程混乱或者其他线程超时。
set scheduler-locking mode: 设置gdb工作模式,具体参考:
https://sourceware.org/gdb/onlinedocs/gdb/All_002dStop-Mode.html
6.1 backtrace
bt 查看函数back trace
6.2 frame
frame + 帧号 切换栈帧
7. 设置变量
set variable variable = [expression] 设置变量的值,可以缩写为 set var ......
The variable to set.
If variable starts with a dollar sign ($) then it is either a predefined register name or a debugger variable, either a predefined variable or a user variable.
If variable does not start with a dollar sign ($), it is a variable in the program.
expression
The value for the variable.
例子:
(gdb)
print node->_data
2
(gdb)
set variable node->_data = 5
(gdb)
print node->_data
5
===========================================================
8. memory dump
两种方式:
1)参考
https://stackoverflow.com/questions/16095948/gdb-dump-memory-in-specific-region-save-formatted-output-into-a-file
例子:
从地址0x601580开始打印1000个四字节为单位的内存。
(gdb) set height 0
(gdb) set logging on
Copying output to gdb.txt.
(gdb) x/1000xw 0x601580
0x601580 <data>: 0x00000e23 0x00000865 0x0000000e 0x00000909
... ...
执行完以后,会dump到gdb.txt里面。
注意,上述打印出的值为大端表示。
2)使用gdb的dump命令:
关于dump的使用可以参考
示例:
(gdb) dump binary memory my.dump 0x601580 0x602ff0
包含内存dump的二进制文件my.dump可以在C的程序中读入,下面是一个例子:
#include <stdio.h>
int main(int argc , char* argv[])
{
FILE *file;
char data[100];
int i;
file = fopen("my.dump", "rb");
fread(data, sizeof(data)-1, sizeof(char), file);
for (i=0; i<100; i++)
{
printf("%02x", data[i]);
}
fclose(file);
return 0;
}
输出结果为:
230e0000650800000e00000009090000
注意,上述输出为小端表示。(x86_64环境)
9. layout及代码查看
===========================================================
layout显示源码:
(gdb) layout src:显示源码窗口
(gdb) layout asm:显示汇编窗口
(gdb) layout regs:显示寄存器窗口
(gdb) layout split:显示源码和汇编窗口, 此时可以list filename:linenum同时查看源代码和汇编。
(gdb) layout next:显示下一个layout窗口
(gdb) layout prev:显示上一个layout窗口
Ctrl + L:刷新窗口
Ctrl + x,再按1:单窗口模式
Ctrl + x,再按2:双窗口模式
Ctrl + x,再按a:退出layout,回到执行layout之前的调试窗口。
===========================================================
list linenum:以linenum指定的行号为中心,显示10行
list function:以指定的函数为中心,显示10行
list:重复上一次的list指令,也可以直接按回车键,重复上次指令。
set listsize count:设置每次显示的行数。
show listsize:显示已设置的显示行数。
list first,last:显示指定起始行到结束结束行的源文件。
list ,last:显示以指定的last为结束行,显示10行。
list first,:以first为第一行,显示10行。
list +:以上次显示的结束行为起始行显示后10行
list –:以上次显示的起始行为结束行,显示前10行
9. core dump
gdb <executable_binary> <coredump_file>
其中可执行文件为带有符号表的binary. 进入gdb以后执行bt命令查看函数调用。
10. 打印数据结构偏移
gdb 8.1以后,可以直接通过(gdb) ptype /o XXX 来查看XXX内的数据结构成员偏移。
如果是早些版本的GDB,可以通过变通的办法,例如:
struct A {
int a;
char b;
short c;
};
(gdb) print (int)&((struct A*)0)->a
$1 = 0
(gdb) print (int)&((struct A*)0)->b
$2 = 4
(gdb) print (int)&((struct A*)0)->c
$3 = 6
===========================================================
常见问题
1. print变量的时候出现<value optimized out>
gdb调试程序的时候打印变量值会出现<value optimized out> 情况,可以在gcc编译的时候加上 -O0参数项,意思是不进行编译优,发布项目的时候不要使用 -O0参数项,gcc 默认编译或加上-O2优化编译会提高程序运行速度.
2. 关于debuginfo
GDB有时会报错:Missing separate debuginfos, use: yum debuginfo-install libgcc-8.5.0-4.el8_5.x86_64 libstdc++-8.5.0-4.el8_5.x86_64
解决方法是先确保/etc/yum.repos.d/CentOS-Linux-Debuginfo.repo 中 enabled=1
然后按照GDB的指示安装debug info,执行:
yum debuginfo-install libgcc-8.5.0-4.el8_5.x86_64 libstdc++-8.5.0-4.el8_5.x86_64
参考文献:
https://blog.youkuaiyun.com/u014015972/article/details/51620694
http://scc.ustc.edu.cn/zlsc/chinagrid/intel/debugger/cl/GUID-BD46F547-7EFA-4066-96BB-7CB9EF415140.htm
https://sourceware.org/gdb/onlinedocs/gdb/All_002dStop-Mode.html
https://sourceware.org/gdb/onlinedocs/gdb/Non_002dStop-Mode.html
https://www-zeuthen.desy.de/unix/unixguide/infohtml/gdb/All_002dStop-Mode.html
https://sourceware.org/gdb/onlinedocs/gdb/Convenience-Funs.html