在Linux系统中,GDB(GNU Debugger)是功能强大的调试工具,主要用于C/C++等程序的调试,支持断点设置、变量查看、内存分析、堆栈跟踪等功能,掌握GDB的使用能显著提升程序问题定位的效率,以下从安装、启动、基本操作到高级功能详细说明其使用方法。
安装与启动GDB
安装GDB
不同Linux发行版的安装命令不同:
- Ubuntu/Debian:
sudo apt update && sudo apt install gdb
- CentOS/RHEL:
sudo yum install gdb
或sudo dnf install gdb
(较新版本)
安装完成后,通过gdb --version
验证是否成功,例如输出GNU gdb (Ubuntu 9.2-0ubuntu1~20.04.1) 9.2
表示安装成功。
启动GDB
启动GDB主要有三种方式:
- 调试可执行文件:
gdb <程序名>
,例如gdb ./a.out
(需先编译生成可执行文件,编译时加-g
选项保留调试信息,如gcc -g test.c -o test
)。 - 附加到运行中进程:
gdb -p <进程ID>
,例如gdb -p 1234
(适用于调试正在运行的程序,需配合ps aux
查看进程ID)。 - 分析core文件:
gdb <程序名> <core文件>
,例如gdb ./test core.1234
(程序崩溃时生成core文件,需通过ulimit -c unlimited
开启core文件生成功能)。
基本调试流程
调试的核心是“设置断点→运行程序→暂停观察→分析问题→修复验证”,以下是关键操作:
设置断点
断点是程序暂停的位置,GDB支持多种断点类型:
- 行断点:在指定代码行暂停,例如
break 10
(第10行)、b test.c:20
(test.c文件第20行)。 - 函数断点:在函数入口处暂停,例如
break main
(main函数)、b 'void func(int)'
(带函数签名)。 - 条件断点:满足条件时暂停,例如
break 20 if i==5
(第20行且变量i等于5时触发)。 - 临时断点:触发后自动删除,例如
tbreak 30
(第30行,仅暂停一次)。 - 硬件断点:基于CPU寄存器,适用于调试内存访问(如
watch
命令),数量有限但性能高。
查看断点列表:info breakpoints
(可缩写为i b
),删除断点:delete 1
(删除编号1的断点)、clear 10
(删除第10行的所有断点)。
运行与控制程序
- 运行程序:
run
(可缩写为r
),若需传参数,直接在命令后添加,例如r arg1 arg2
。 - 继续执行:
continue
(可缩写为c
),从当前断点继续运行到下一个断点或程序结束。 - 逐过程执行:
next
(可缩写为n
),执行下一行代码(若遇到函数调用,不进入函数内部)。 - 逐语句执行:
step
(可缩写为s
),执行下一行代码(若遇到函数调用,进入函数内部)。 - 执行到函数返回:
finish
,运行到当前函数返回,并显示返回值和局部变量。 - 跳转执行:
jump 15
,跳转到第15行执行(需谨慎,可能破坏程序状态)。
查看变量与内存
- 打印变量值:
print <变量名>
(可缩写为p
),例如p i
打印变量i的值,支持格式化输出,如p/x i
(十六进制)、p/c i
(字符)、p/s str
(字符串)。 - 自动显示变量:
display <变量名>
,每次程序暂停时自动显示该变量值,例如display i
;取消自动显示:undisplay <编号>
(通过info display
查看编号)。 - 监视变量变化:
watch <变量名>
,当变量被修改时暂停,例如watch i
;查看监视点:info watchpoints
(可缩写为i watch
)。 - 查看内存:
x/<格式><数量><地址>
,例如x/10xw &i
(从变量i的地址开始,查看10个32位十六进制整数),格式说明:x
(十六进制)、d
(十进制)、f
(浮点数)、c
(字符)、s
(字符串),单位:b
(字节)、h
(半字,2字节)、w
(字,4字节)、g
(双字,8字节)。
堆栈与线程分析
- 查看调用栈:
backtrace
(可缩写为bt
),显示函数调用链,例如bt 0 3
(显示0-3层栈帧),栈帧编号0表示当前函数。 - 切换栈帧:
frame <编号>
(可缩写为f <编号>
),例如f 1
切换到第1层栈帧,查看栈帧局部变量:info locals
,查看函数参数:info args
。 - 多线程调试:
info threads
(可缩写为i th
)列出所有线程,thread <编号>
切换线程(如thread 2
),设置线程断点:break main thread 3
(仅在3号线程的main函数处暂停),锁定线程调度:set scheduler-locking on
(防止调试时线程切换,聚焦当前线程)。
高级调试功能
调试多进程程序
默认情况下,GDB调试父进程,子进程由系统独立运行,可通过set follow-fork-mode child
(调试子进程)或set follow-fork-mode parent
(调试父进程)切换,
(gdb) set follow-fork-mode child
(gdb) run
此时GDB会附加到子进程进行调试。
调试动态库
程序使用动态库时,需加载调试符号:
- 查看已加载的动态库:
info sharedlibrary
(可缩写为i sh
)。 - 指定动态库路径:
set solib-search-path /path/to/libs
(若动态库不在默认路径)。 - 重新加载动态库符号:
sharedlibrary <库名>
,例如sharedlibrary libc.so.6
。
远程调试
通过GDB的远程调试功能,可在本地调试远程主机上的程序,需配合gdbserver:
- 远程主机运行:
gdbserver :1234 ./program
(监听1234端口)。 - 本地连接:
target remote <远程IP>:1234
,例如target remote 192.168.1.100:1234
。
GDB脚本与自动化
将调试命令写入脚本文件(如debug.cmd
),通过source debug.cmd
批量执行,
break main
run
print argc
next
finish
quit
执行命令:gdb -x debug.cmd ./program
。
常用GDB命令速查表
命令(缩写) | 功能描述 | 示例 |
---|---|---|
break (b) | 设置断点 | b 10, b main, b test.c:20 |
info breakpoints (i b) | 查看断点列表 | i b |
run (r) | 运行程序 | r arg1 arg2 |
continue (c) | 继续执行 | c |
next (n) | 逐过程执行(不进函数) | n |
step (s) | 逐语句执行(进函数) | s |
finish | 执行到当前函数返回 | finish |
print (p) | 打印变量值 | p i, p/x i |
display | 自动显示变量 | display i |
watch | 监视变量变化 | watch i |
backtrace (bt) | 查看调用栈 | bt, bt 0 3 |
frame (f) | 切换栈帧 | f 1 |
info threads (i th) | 查看线程列表 | i th |
thread | 切换线程 | thread 2 |
quit (q) | 退出GDB | q |
相关问答FAQs
Q1: GDB调试时如何进入汇编代码查看底层逻辑?
A: 使用display/i $pc
命令($pc
是程序计数器),每次暂停时自动显示当前汇编指令;或直接使用stepi
(可缩写为si
)逐条汇编指令执行,nexti
(可缩写为ni
)逐条汇编指令执行但不进入函数调用。layout asm
可进入汇编视图模式,layout regs
显示寄存器状态,退出布局视图用layout off
。
Q2: GDB调试多线程程序时,如何定位特定线程的问题?
A: 首先通过info threads
查看所有线程,重点关注状态为running
或sleeping
的线程,切换到目标线程:thread <线程ID>
(如thread 3
);设置线程断点:break <函数名> thread <线程ID>
(如break func thread 3
,仅3号线程进入func时暂停);为避免线程切换干扰,可锁定调度:set scheduler-locking on
(调试完成后用set scheduler-locking off
恢复),通过thread apply all <命令>
可对所有线程执行命令,如thread apply all bt
查看所有线程的调用栈。
原创文章,发布者:酷番叔,转转请注明出处:https://cloud.kd.cn/ask/25560.html