1、说明
gdb -v 查看是否安装gdb
gcc main.c -o main.exe -g ;-o = -o0 优化级别 -g产生gdb可用的exe
2、调用gdb的方式
2.1、调试尚未执行的程序
gdb main.exe
此时会创建两个进程:gdb调试器进程,程序本身的进程
包含入参的gdb调试
gdb --args ./main.exe para
2.2、调试正在执行的程序
为了方便管理当前系统中运行的诸多进程,每个进程都配有唯一的进程号(PID)
./main.exe
pidof main.exe
gdb attach PID
当 GDB 调试器成功连接到指定进程上时,程序执行会暂停
当调试完成后,如果想令当前程序继续执行,消除调试操作对它的影响,需手动将 GDB 调试器与程序分离
执行 detach 指令,使 GDB 调试器和程序分离;
执行 quit(或 q)指令,退出 GDB 调试。
2.3、调试执行异常崩溃的程序
Linux 操作系统中,当程序执行发生异常崩溃时,系统可以将发生崩溃时的内存数据、调用堆栈情况等信息自动记录下载,并存储到一个文件中,该文件通常称为 core 文件;Linux 系统所具备的这种功能又称为核心转储(core dump)
ulimit -a 指令来查看当前系统是否开启此功能
为0 表示未开启core dump,使用下面的指令开启此功能
ulimit -c unlimited
默认情况下,core 文件的生成位置同该程序所在的目录相同;
gdb main.exe core 启动调试:现场恢复
GDB 对 core 文件的分析和调试提供有非常强大的功能支持
野指针举例
段错误又称为访问权限冲突,指的是当前程序访问了不可访问的存储空间,比如访问的不存在的空间,又或者是受系统保护的内存空间
3、gdb命令介绍
运行控制
r(run) | 指令会一直执行程序,直到断点;还可以在任何时候重新启动程序 |
start | 指令会执行程序至 main() 主函数的起始位置 |
n count | 参数 count 表示单步执行多少行代码,默认为 1 行。 |
c | 继续执行到断点 |
q | 终止 |
s(step) count | 单步;当包含函数时,会进入该函数内部,并在函数第一行代码处停止执行 |
u | 快速运行完当前的循环体; +某行代码的行号,执行至指定位置后停止 |
fi(finish ) | 执行函数到正常退出 |
return | 立即结束执行当前函数并返回 |
jump | 直接跳到 location 处的代码继续执行程序 |
l 显示代码
报 No such file or directory 错误解决方法:
解决: 将源代码文件拷贝相应目录(保持编译后源文件与可执行文件的相对位置)
结论: 使用gdb调试时,要将源文件,可执行文件一同复制过去
断点
GDB 调试器支持 3 种断点,分别为普通断点(用 break 命令创建)、观察断点(用 watch 命令建立)以及捕捉断点(用 catch 命令建立)
b {filename:}[linenum/function] {if cond}
举例:b function ;b linenum ;b 7 if num>10
tbreak 程序暂停后,断点失效。使用方法与break一致
(w)watch cond conde 指的就是要监控的变量或表达式
rwatch cond read
awatch cond :read or write
断点管理
info break info watchpoints 查看所有断点信息;
clear 命令可以删除指定位置处的所有断点;clear 删除断点是基于行的,不是把所有的断点都删除
delete 命令(可以缩写为 d )通常用来删除所有断点,也可以删除指定编号的各类型断点
禁用断点可以使用 disable 命令,语法格式如下:
disable [breakpoints] [num...]
breakpoints 参数可有可无;num... 表示可以有多个参数,每个参数都为要禁用断点的编号。如果指定 num...,disable 命令会禁用指定编号的断点;反之若不设定 num...,则 disable 会禁用当前程序中所有的断点
对于禁用的断点,可以使用 enable 命令激活
内容查看
display num 程序停止自动打印
undisplay num...
delete display num…
参数 num... 表示目标变量或表达式的编号,编号的个数可以是多个
p num{=4 修改 num 的值为 4}
(gdb) p TestCase
报错解决方法 value of type `TEST_CASE' requires 70908 bytes, which is more than max-value-size
(gdb) set max-value-size unlimited
(gdb) p/x &TestCase.TestCaseData[0].Scale 可以查看变量地址
$21 = 0xaaaab8fcd354
(gdb) x/28xb $21 可以直接使用gdb给出的代表地址的变量
print高级用法
print [options --] [/fmt] expr
用 [ ] 括起来的部分是可选的,可以使用也可以省略
options 参数和 /fmt 或者 expr 之间,必须用--( 2 个 - 字符)分隔
print 命令不指定任何 options 参数时,print 和 /fmt 之间不用添加空格 :print/x num
-address on|off 查看某一指针变量的值时,是否同时打印其占用的内存地址,默认值为 on。
该选项等同于单独执行 set print address on|off 命令。
-array on|off 是否以便于阅读的格式输出数组中的元素,默认值为 off。
该选项等同于单独执行 set print array on|off 命令。
-array-indexes on|off对于非字符类型数组,在打印数组中每个元素值的同时,是否同时显示每个元素对应的数组下标,默认值为 off。该选项等同于单独执行 set print array-indexes on|off 命令。
-pretty on|off以便于阅读的格式打印某个结构体变量的值,默认值为 off。
该选项等同于单独执行 set print pretty on|off 命令。
@与::运算符
int array[5] = {1,2,3,4};
如果我们想查看第 1 个元素和第 2 个元素的值,可以执行如下指令:
(gdb) print array[0]@2
$1 = {1, 2}
当程序中包含多个作用域不同但名称相同的变量或表达式时,可以借助::运算符明确指定要查看的目标变量或表达式。::运算符的语法格式如下:
(gdb) print file::variable
(gdb) print function::variable
内存查看
examine 命令(简写是 x )来查看内存地址中的值。
x/<n/f/u> <addr>
n 是一个正整数,表示显示内存的长度,也就是说从当前地址向后显示几个单位的内容
f 表示显示的格式(f可选值)
x 按十六进制格式显示变量。
d 按十进制格式显示变量。
u 按十六进制格式显示无符号整型。
u 表示每个单位长度,默认为4字节(u可选值)
b 表示单字节
h 表示双字节
w 表示四字 节
g 表示八字节
其他指令
bt:backtrace,查看当前线程调用栈,顺序为自下往上的调用
frame 切换到当前调用栈的任意一个函数中
回车:重复执行上一次操作
info local以及其他gdb调试信息(快按两次Tab键会显示提示)
disassemble [Function] 反汇编
4、多线程调试
标号为1的线程是主线程
未指明线程时,命令作用于所有线程,停止时所有线程也会停。遇到当某个线程执行遇到断点时,GDB 调试器会自动将该线程作为当前线程
set scheduler-locking on 可以用来锁定当前线程,只观察这个线程的运行情况。当锁定这个线程时,其他线程就处于了暂停状态,也就是说你在当前线程执行 next、step、until、finish、return 命令时,其他线程是不会运行的
使用 thread <线程编号> 可以切换到对应的线程,然后使用 bt 命令可以查看对应线程从顶到底层的函数调用,以及上层调用下层对应的源码中的位置;可以使用 frame <栈函数编号> 切换到当前函数调用堆栈的任何一层函数调用中去,然后分析该函数执行逻辑;