目录
1.概述
gdb是GNU开源组织发布的一个强大的Linux下的程序调试工具。
一般来说,GDB主要帮助你完成下面四个方面的功能:
1、启动你的程序,可以按照你的自定义的要求随心所欲的运行程序。
2、可让被调试的程序在你所指定的调置的断点处停住。(断点可以是条件表达式)
3、当程序被停住时,可以检查此时你的程序中所发生的事。
4、你可以改变你的程序,将一个BUG产生的影响修正从而测试其他BUG。
2.调试器的种类
两类调试器:GUI调试器(VS),GDB调试器 GDB命令调试器 对初学者不友好 需要掌握大量的调试命令 GUI调试器(上手简单,开销较大 ,某些GUI调试器可能出现调试冲突)
3.GDB的调试命令
bash
list [l] 行号 #打印行号附近的代码 默认显示10行
run[r ] #开始执行不调试
start #单步调试
step[s] #逐语句执行,如果在模块位置,进入到模块内部
next[n] #逐过程执行,如果是模块不进入模块内部,直接运行模块,返回结果
执行程序的命令 : -g
bash
gcc test.c -g -o app
启动GDB 调试命令: gdb app
常用断点调试命令
bash
#断点
break [b] 行号 或者函数名 #在指定位置加设断点
#某段代码假设断点后,调试时可用run全速运行,
#如果以start启动调试,可以使用continue快速跳转到断点位置暂停
info breakpoints #查看断点详细信息
delete [d] 断点编号 # 可以一次删除多个 比如 d 1 3 4
disable 断点编号 #禁用断点,不会暂停
enable 启用断点#恢复暂停功能
tbreak #临时断点(一次性断点)
continue [c] #继续运行
print [p] 变量 #输出程序中的数据信息
set var 变量#值,可以在运行调试阶段修改变量数据
finish #跳出模块,返回调用位置
until 行号#跳出循环
clear #清除指定行或函数处的断点
q #退出gdb调试
3.1打印内存
格式:
x/NFU ADDR
N:重复后面FU次数
F:/x16进制 /c字符 /s字符串 /a地址 /d十进制 /i汇编 /t二进制
U:b字节 h(2字节) w(4字节默认) g(8字节)
3.2自动换行
(gdb)set height 0 去掉less的功能,一次性打印所有
3.3打印所有线程堆栈
(gdb)thread apply all bt
3.4查看某个地址意义
(gdb)info line *0x00f43126 //会打印出这个函数名 等价于:arm-linux-addr2line 0x00f43126 -e ./build_debug
3.5查看结构体定义
bash
(gdb)ptype pTimeVal
type = struct{
int32 i32tv;
int32 i32Usec;
}
3.6打印格式美观
(gdb)set print pretty on
3.7打印数组
(gdb)p *pstTmpStruct->pst@4
$35 = {0x1, 0x2, 0x3, 0x4}
12
3.8display 每次断点时打印某个值 3.9查看指令
info args:查看当前函数的参数及其值
info line:查看源代码在内存中地址,可以跟行号、函数名
info locals:显示当前函数的局部变量
info symbol:显示全局变量信息
info function:显示所有函数名称
info thread:查看线程信息
info registers:列举寄存器值
1234567
3.10指定动态库位置
(gdb)set solib-search-patch ./libso/
(gdb)set solib-absolute-prefix ./libso/
12
3.11打印当前进程map信息 定位内存相关死机问题时比较常用,确认当前申请的大块内存的头尾
i proc map
4.多线程调试
单步调试n、s都会遇到一个问题,某个接口可能是多线程调用的,n执行一步可能会跑到其他线程中执行,造成调试不便,所以在执行单步调试前我们可以先将线程锁定,只能执行到当前线程:
(gdb)set scheduler-locking on
(gdb)set scheduler-locking off
4.1断点锁定某个线程
有时将某个接口,如get()函数增加断点时,该接口被多个线程调用,不是我们想分析的线程,如果想指定到某个线程的调用,需要指定线程号,针对线程打断点:
(gdb)b get thread 23
5.GDB一些小技巧
1.shell 终端命令
2.日志功能
set logging on
3.观察一个变量的值是否发生变化 watch *地址
4.info watchpointsq
5.设置core 文件 (如果 没有core 查看 ulimit -a )
6.调试运行的进程 gdb -p 进程ID
6.GDB的底层原理
系统启动GDB进程,这个进程会调用系统函数fork()来创建一个子进程,这个子进程做两件事:
1.调用系统函数ptrace(PTRACE_TRACEME,[其他参数]);
2.通过exec来加载、执行可程序程序test,那么test程序就在这个子进程中开始执行G
ptrace这个函数是使得GDB拥有了强大的调试能力。函数原型:
#include <sys/ptrace.h>
long ptrace(enum _ptrace_request request,pid_t pid,void* addr, void* data);
The ptrace() system call provides a means by which one process (the "tracer") may observe and control the execution of another process (the "tracee"), and examine and change the tracee's memory and registers. It is primarily used to implement breakpoint debugging and system call tracing.
tracer 就是调试程序,即GDB程序,tracee就是被调试程序,对应于上面的test.一般习惯用-er和-ee来表示主动和被动的关系。
ptrace系统函数是linux内核提供的一个用于进程跟踪的系统调用,通过它,一个进程(gdb)可以读写另外一个进程(test)的指令空间、数据空间、堆栈和寄存器的值。而且gdb进程接管了test进程的所有信号,也就是说系统向test发送的所有信号,都被gdb进程接收,这样一来,test进程的执行就被gdb控制了,从而达到调试的目的。
也就是说,如果没有gdb调试,操作系统与目标进行之间是直接交互的;如果使用GDB来调试,那么操作系统发给目标进程的信号就会被gdb截获,gdb根据信号的属性来决定:在继续运行目标程序时是否把当前截获的信号转交给目标程序,如此一来,目标程序就在gdb发来的信号指挥下进行相应的动作。