GDB上手
GDB
官网:https://www.gnu.org/software/gdb/ Debugging with GDB:https://sourceware.org/gdb/current/onlinedocs/gdb/
简介
GDB, the GNU Project debugger, allows you to see what is going on `inside' another program while it executes -- or what another program was doing at the moment it crashed.
GDB的功能:
- Start your program, specifying anything that might affect its behavior.
- Make your program stop on specified conditions.
- Examine what has happened, when your program has stopped.
- Change things in your program, so you can experiment with correcting the effects of one bug and go on to learn about another.
启动gdb
启动gdb之前,需要编译出debug版本的程序,编译时加-g
选项
bash
$ gcc -g main.c
启动方法:
- 调试直接启动程序
- 调试一个正在运行的程序,attach
- 调试core文件
调试直接启动程序:
bash
# 语法: gdb <program>
$ gdb a.out
调试一个正在运行的程序,attach:
bash
# 语法: gdb <program> <PID>
$ gdb a.out 11127
调试core文件:
可以通过core文件调试程序来找到错误原因.比如跟踪常见的断错误
bash
# 语法: gdb <program> <core dump file>
$ gdb program core
通用
help
help <keyword>
- tab键补全
- Enter回车:执行上一条命令
- 大多命令都有简写
断点
- 设置断点:break/b
- 查看断点:info breakpoints
- 删除断点:delete
- 关闭或开启断点:disable/enable
- disable <断点序列号>:关闭指定序列号的断点
- enable <断点序列号>:开启指定序列号的断点
bash
# 设置断点:break语法
# * break <行号>
# * break <文件名>:<行号>
# * break <函数名>
# * break <文件名>:<函数名>
(gdb) break 30 # 设置30行为断点
(gdb) break main.c:30 # 设置main.c的30行断点
(gdb) break main # 设置main函数为断点
(gdb) break main.c:main # 设置main.c文件的main函数为断点
# 查看断点
(gdb) info breakpoints
# 删除断点:delete语法
# * delete <断点序列号>
# * delete breakpoints
(gdb) delete 3 # 删除序列号为3的断点
(gdb) delete breakpoints # 删除所有断点
# 其它操作:disable/enable
# * disable <断点序列号>:关闭指定序列号的断点
# * enable <断点序列号>:开启指定序列号的断点
(gdb) disable 3
(gdb) enable 3
运行
- run/r:运行程序,当遇到断点后,程序会在断点处停止运行,等待用户输入下一步的命令
- next/n:单步跟踪程序,不会跳进去函数
- nexti:单步跟踪程序(汇编)
- continue/c:继续执行,到下一个断点处(或运行结束)
- step/s:单步调试如果有函数调用,则进入函数
- finish:运行程序,直到当前函数完成返回
- call:调用程序中可见的函数
- quit/q:退出
查看源代码
两种方式
- list/l
- list <行号>:默认显示10行代码信息
- list <函数名>:以函数为中心,显示10行代码信息
- list <文件名>:<函数名>:
- show listsize:查看list命令显示的大小
- set listsize:设置list命令显示的大小
- layout(src源代码/asm汇编/regs寄存器/分屏)
- layout src:源代码
- layout asm:汇编
- layout regs:寄存器
- layout prev:上一种布局
- layout next:下一种布局
- refresh/Ctrl+L:刷新(屏幕出现花屏时使用)
信息查看显示
- print/p:打印一个变量,取地址,表达式,函数
- backtrace full:打印栈里的所有局部变量
- display:类似print,每次单步调试都会输出
- undisplay:取消display的设置
- watch:监控变量,当变量变化时输出(删除watch使用delete命令)
- whatis:查询变量或函数信息
- info:查看相关信息
- info breakpoints:查看断点设置
- info display:查看display设置
- info watchpoints:查看watch设置
bash
# print语法: print <expression>
# * print <变量>
# * print <变量取地址>
# * print <表达式>
# * print <函数调用>
(gdb) print a # 变量值
(gdb) print &a # 取地址
(gdb) print a++ # 表达式
(gdb) print random_range(10, 20) # 函数调用
# print 数组
# * print <数据变量名>
# * print <数据变量名[index]@lenght>
(gdb) print array # 打印数组
(gdb) print array[5]@10 # 从第五个元素开始,打印10个元素
调试core文件
core文件:核心文件通常在系统收到特定的信号时由操作系统生成. 信号可以由程序执行过程中的异常触发,也可以由外部程序发送. 动作的结果一般是生成一个某个进程的内存转储的文件,文件包含了此进程当前的运行堆栈信息.
系统默认不会产生core文件,默认core文件大小为0,需要设置ulimit -c unlimited
bash
$ ulimit -a # 查看一个进程启动时的资源限制参数
$ ulimit -c unlimited # 设置core文件大小不设限制
$ gdb a.out core
(gdb) backtrace full
多线程调试
todo...
远程调试
如果远程和本地两台机架构相同:远程使用gdbserver,本地使用gdb 如果远程和本地两台机架构不同:远程使用gdbserver,本地使用gdb-multiarch
bash
# 远程
$ gdbserver :12345 a.out
# 本地
$ gdb
(gdb) target remote 127.0.0.1:port # 连接远程gdbserver
(gdb) break main # 设置main函数断点
(gdb) continue # 开始调试
(gdb) next # 单步调试
(gdb) monitor exit # 关闭远程gdbserver
演示
c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
int random_range(int min_num, int max_num) {
if (min_num > max_num) {
exit(EXIT_FAILURE);
}
int ret = 1 + rand() % (max_num - min_num) + min_num;
return ret;
}
int main(int argc, char* argv[]) {
int* nil = NULL;
srand(time(NULL));
int ret = 0;
for (int i = 0; i < 200; i++) {
ret = random_range(-100, 100);
if (ret == 13) {
printf("boom!\n");
memcpy(nil, &ret, sizeof(nil)); // segmentation fault
}
printf("ret = %d\n", ret);
usleep(10000);
}
return 0;
}
Refrences: