gdb的使用与调试方法

📅 2026/8/6 4:56:50
gdb的使用与调试方法
CGDB 使用方法详解CGDB可以理解成GDB 的终端图形界面版。它不是另一套调试器底层仍然是 GDB只是在终端中增加了源码窗口、断点标记、当前执行行高亮和快捷键。上面通常显示源码下面是标准 GDB 命令行因此所有 GDB 命令在 CGDB 里仍然可以使用。CGDB┌──────────────────────────────────────┐ │ 源码窗口显示代码、当前执行行、断点 │ ├──────────────────────────────────────┤ │ GDB窗口(gdb) 输入调试命令和看输出 │ └──────────────────────────────────────┘一、安装 CGDBUbuntu 中执行sudo apt update sudo apt install cgdb gdb检查cgdb --version gdb --versionCGDB 在 Ubuntu 软件仓库中属于基于 curses 的 GDB 界面。Ubuntu软件包二、程序必须带调试信息例如你的项目有client.c server.c com.c com.h编译客户端gcc -Wall -Wextra -stdc11 -g -O0 \ client.c com.c -o client编译服务端gcc -Wall -Wextra -stdc11 -g -O0 \ server.c com.c -o server关键参数-g 在可执行文件中加入源码、变量、行号等调试信息 -O0 关闭优化让代码执行顺序更接近源码没有-gCGDB 可能只能看到汇编或者无法查看局部变量。优化开启后还可能出现变量被优化掉、代码跳行等现象。GDB 官方建议编译时生成调试信息关闭优化通常更容易逐行调试。SourcewareMakefile CFLAGS -Wall -Wextra -stdc11 -g -O0三、启动 CGDB调试服务端cgdb ./server调试客户端cgdb ./client进入后通常会自动显示包含main()的源文件。若没有显示想看的文件在源码窗口按o打开源码文件列表然后选择client.c、server.c或com.c。源码窗口支持文件选择、搜索、跳转和可视化断点。CGDB四、最重要CGDB 有不同模式CGDB 初学最容易混乱的地方就是“我现在在哪个窗口”。1. 源码模式启动后一般处于源码模式。这里可以j / ↓ 向下移动 k / ↑ 向上移动 gg 跳到文件开头 G 跳到文件结尾 25G 跳到第25行 /内容 向下搜索 ?内容 向上搜索 n 下一个搜索结果 N 上一个搜索结果 空格 在当前行设置断点 t 设置临时断点 o 打开源码文件列表2. GDB 命令模式在源码模式按i光标会进入下面的 GDB 窗口现在可以输入break main run print i next continue3. 返回源码模式在 GDB 命令窗口按Esc回到上方源码窗口。因此最核心的切换是源码窗口 --按 i-- GDB命令窗口 GDB窗口 --按 Esc-- 源码窗口4. GDB 输出滚动模式GDB 输出很多时在 GDB 窗口按PageUp会进入历史输出滚动模式。滚动时PageUp / Ctrl-u 向上 PageDown / Ctrl-d 向下 gg 最前面 G 最后面 q / Enter / i 退出滚动模式这些模式和按键均来自 CGDB 的官方模式设计。CGDB五、第一次完整调试流程假设调试cgdb ./client第一步在main()设置断点在源码窗口移动到int main()或者函数内第一条有效语句然后按空格断点行旁边会出现断点标记。也可以按i进入 GDB 窗口输入break main简写b main第二步启动程序源码模式按F5等价于run简写r程序运行到断点前停止源码窗口会高亮当前即将执行的行。第三步查看变量按i输入print i简写p i查看指针p addr十六进制显示p/x addr查看指针指向的字符p *addr查看第二个字节p addr[1]查看第二个字节地址p addr[1]第四步单步执行按Esc回源码窗口然后使用快捷键F8 next执行下一行不进入函数 F10 step执行下一行遇到函数则进入函数 F7 finish执行完当前函数返回调用者 F6 continue继续运行到下一个断点官方默认快捷键对应F5 run F6 continue F7 finish F8 next F10 step六、next 和 step的区别假设当前行int shmid create_shm(SHM_SIZE);执行next会把整个create_shm()当成一行执行完不进入函数内部。执行step会进入int create_shm(size_t size) { ... }常用简写n next s step fin finish c continue注意在源码模式直接按字母s它不是 GDB 的step命令而是切换到 GDB 输出滚动模式。因此初学阶段建议使用F10或者先按i再输入s。七、程序执行控制命令命令作用run/r从头启动程序start启动并自动停在main()continue/c继续到下一个断点next/n下一源码行不进入函数step/s下一源码行进入函数finish运行到当前函数返回until运行到循环后面或指定行kill终止当前被调试程序quit/q退出调试器例如start会直接启动并停在main()附近比先设置break main再run更方便。程序带命令行参数时set args hello 123 show args run相当于正常运行./client hello 123八、断点详解1. 按函数名设置break main break create_shm break attach_shm2. 按行号设置当前文件第 25 行break 25或者 b 25指定文件break client.c:25 break com.c:153. 条件断点例如只在i 80时停止break client.c:30 if i 80也可以先设置普通断点break client.c:30假设编号为 2再添加条件condition 2 i 804. 查看所有断点info breakpoints简写info bGDB 会给每个断点编号并显示是否启用、地址、源码位置和条件。Sourceware5. 禁用和启用disable 2 enable 2禁用不会删除断点只是暂时不生效。6. 删除断点删除编号 2delete 2删除全部delete按源码位置删除clear client.c:307. 临时断点tbreak main临时断点命中一次后自动删除。九、查看变量和表达式printp i p shmid p addr p addr[0] p addr[1] p strlen(addr) p sizeof(addr)格式化显示p/x i 十六进制 p/d i 十进制 p/c i 字符 p/t i 二进制例如p/c 65结果类似65 A查看变量类型ptype addr ptype shmid例如ptype addr可能显示type char *查看当前函数局部变量info locals查看当前函数参数info args自动显示变量display i display addr以后每次程序停止GDB 都会自动显示它。查看自动显示列表info display取消undisplay 1临时修改变量set variable i 90简写通常也可以set var i 90这只修改当前调试进程运行时的变量不会修改源代码文件。十、查看内存x命令x表示 examine专门查看内存。格式x/数量格式单位 地址可以理解为x / n f u address n查看多少个单位 f以什么格式显示 u每个单位多大常见格式x 十六进制 d 有符号十进制 u 无符号十进制 c 字符 s 字符串 i 汇编指令常见单位b 1字节 h 2字节 w 4字节 g 8字节GDB 官方将x定义为独立于程序数据类型的内存查看命令。Sourceware查看共享内存中的字符串x/s addr查看 32 个字节的十六进制值x/32bx addr含义32查看32个 b 每个1字节 x 十六进制查看 64 个字符x/64cb addr查看 10 个整数假设p是int *x/10dw p查看指令x/10i $pc从当前程序计数器开始查看 10 条机器指令。十一、特别适合你当前共享内存代码的调试方法你之前有char *addr attach_shm(shmid); while (i 90) { snprintf(addr, SHM_SIZE, %c, i); addr 2; }可以在循环内部设置断点然后查看p/x addr p i x/16cb addr如果保存了原始地址char *base addr;可以查看移动了多少字节p addr - base例如输出$1 20说明addr已经向后移动了 20 字节。查看整个共享内存开头x/64cb base查看字符串x/s base循环结束前检查p/x base p/x addr p addr - base这样可以直接证明base 仍是 shmat() 返回的起始地址 addr 已经因为 addr 2 向后移动最后必须detach_shm(base);而不是detach_shm(addr);十二、查看函数调用栈程序崩溃或卡住时最重要的命令是backtrace简写bt输出可能类似#0 commshm() at com.c:15 #1 create_shm() at com.c:6 #2 main() at server.c:8意思是main() └── create_shm() └── commshm() 当前停在这里显示局部变量bt full切换栈帧frame 0 frame 1 frame 2向调用者移动up向被调用函数移动down调用栈由一系列栈帧组成GDB 可以查看每层函数的参数、局部变量和源码位置。Sourceware十三、调试段错误程序出现Segmentation fault在 CGDB 中重新运行run发生段错误后通常会自动停住然后按顺序执行bt frame 0 info locals info args list p 相关指针 x/16bx 相关指针例如p addr p/x addr x/16bx addr如果地址是0x0通常说明空指针。还可以检查p shmid p addr (void *)-1调试shmat()时尤其有用。十四、监视变量什么时候被修改例如想知道addr什么时候变化watch addr程序继续运行continue当addr 2修改addr时GDB 会停止并显示旧值和新值。监视共享内存第一个字节watch base[0]监视iwatch i查看监视点info breakpoints删除方式与普通断点相同delete 3Watchpoint 是一种在表达式值发生变化时停止程序的特殊断点某些平台使用硬件监视点软件监视点则可能明显降低执行速度。Sourceware对于你的共享内存实验最好在实际执行写操作的客户端进程中设置监视点服务端和客户端是两个独立进程通常应分别调试。十五、调试客户端和服务端client和server是两个独立可执行程序最简单可靠的方法是使用两个 CGDB。终端一cgdb ./server设置断点break main break server.c:读取数据的行号 run终端二cgdb ./client设置断点break main break client.c:写共享内存的行号 run终端三继续监视进程watch -n 1 \ ps -o pid,ppid,pgid,sid,stat,wchan:25,cmd -C client -C server这样可以同时观察服务端停在哪里 客户端停在哪里 共享内存内容如何变化 进程当前是否睡眠或运行十六、附加到已经运行的进程假设服务端已经运行./server另一个终端查询 PIDpgrep -x server假设结果1281513打开 CGDBcgdb ./server按i输入attach 1281513附加后GDB 会先暂停该进程。接下来可以bt info locals p addr continue完成后detachdetach会解除调试控制并让进程继续独立运行。GDB 官方支持通过 PID 附加到已运行进程附加时会先停止该进程随后可以设置断点、单步和查看变量。Sourceware这个方法特别适合判断服务端到底阻塞在read()、sleep()、open()还是别的位置。附加后执行bt就能看到当前调用链。十七、调试fork()父子进程如果程序内部调用fork()想让调试器跟踪子进程set follow-fork-mode child想继续跟踪父进程set follow-fork-mode parent希望父子进程都保留在 GDB 控制下set detach-on-fork off查看所有被调试进程info inferiors切换到第二个进程inferior 2恢复默认的“分离另一个进程”模式set detach-on-fork onGDB 在 Linux 上支持通过follow-fork-mode选择父进程或子进程并通过detach-on-fork off同时保留多个 fork 产生的进程。Sourceware十八、调试系统调用你正在学习管道、共享内存可以让 GDB 在系统调用发生时停止。捕获read()catch syscall read捕获write()catch syscall write同时捕获catch syscall read write捕获 System V 共享内存相关调用catch syscall shmget shmat shmdt shmctl查看捕获点info breakpoints继续continue注意这可能会非常频繁地停止因为标准库和动态链接器也可能执行很多系统调用。GDB 的catch syscall可以根据系统调用名称或编号创建捕获点具体可用名称取决于目标系统和 GDB 的系统调用数据库。Sourceware十九、调试命名管道中的SIGPIPE如果写端向已经没有读端的管道写入程序可能收到SIGPIPE。让 GDB 收到SIGPIPE时停住handle SIGPIPE stop print pass含义stop 收到信号时停止 print 显示提示 pass 继续时仍把信号交给程序查看信号配置info signals SIGPIPEGDB 可以配置不同信号是停止、打印、传递还是忽略SIGPIPE在 Unix 系统中是标准信号之一。Sourceware二十、程序输入输出和 CGDB 命令冲突假如被调试程序需要scanf(...) read(STDIN_FILENO, ...)程序输入和 GDB 命令可能都在同一个终端看起来比较乱。对于你的 SSH 环境建议给被调试程序指定另一个终端。在另一个终端执行tty假设输出/dev/pts/5在 CGDB 的 GDB 窗口执行set inferior-tty /dev/pts/5然后run此时CGDB窗口输入调试命令 /dev/pts/5显示程序输出、接收程序输入查看当前设置show inferior-ttyGDB 官方支持通过set inferior-tty将被调试程序的输入输出定向到指定终端。SourcewareCGDB 源码模式还提供Ctrl-T打开新的被调试程序终端不过在纯 SSH 环境中手动指定/dev/pts/N往往更稳定。CGDB二十一、窗口快捷键在源码模式中快捷键作用Ctrl-W横向/纵向切换窗口-缩小源码窗口一点放大源码窗口一点_缩小源码窗口约 25%放大源码窗口约 25%Ctrl-L清屏并重新绘制.跳回当前执行行o打开源码文件列表这些是 CGDB 自身快捷键不是 GDB 命令。CGDB二十二、配置 CGDB配置文件默认是~/.cgdb/cgdbrc创建mkdir -p ~/.cgdb nano ~/.cgdb/cgdbrc推荐配置set autosourcereload set color set hlsearch set ignorecase set tabstop4 set winsplitorientationhorizontal set winsplitsrc_big含义autosourcereload 源文件修改后自动重新加载 color 开启颜色 hlsearch 高亮搜索结果 ignorecase 搜索时忽略大小写 tabstop4 Tab显示为4个空格 winsplitorientation 源码和GDB上下排列 winsplitsrc_big 源码窗口稍大也可以改成左右布局set winsplitorientationverticalCGDB 默认把用户配置、历史和日志放在~/.cgdb/启动时会读取其中的cgdbrc窗口布局、搜索、颜色、Tab 宽度等均可配置。CGDB临时设置时在源码模式输入:set winsplitorientationvertical配置文件中则直接写set winsplitorientationvertical二十三、常见问题1. 看不到源码重新编译gcc -g -O0 main.c -o main cgdb ./main确认启动的是可执行文件不是源码cgdb ./main不是cgdb main.c2. 变量显示optimized out关闭优化gcc -g -O0 main.c -o main3. 显示No symbol addr in current context可能原因当前还没执行到 addr 的作用域 当前栈帧不是定义 addr 的函数 程序没有使用 -g 编译 编译器把变量优化掉了先执行bt frame 0 info locals4. 程序正在运行不能输入命令按Ctrl-C这会中断被调试程序让控制权回到 GDB。注意这里终止的是程序当前运行不一定退出 CGDB。5. 终端突然“冻住”可能误按了Ctrl-S按Ctrl-Q恢复。然后输入等价 GDB 命令run next step continue finish二十四、命令快查i 进入GDB命令窗口 Esc 回到源码窗口 空格 设置源码断点 start 启动并停在main run / r 从头运行 continue / c 继续运行 next / n 下一行不进函数 step / s 下一行进入函数 finish 跳出当前函数 break / b 设置断点 info breakpoints 查看断点 delete 删除断点 print / p 查看变量 info locals 查看局部变量 display 每次停止自动显示变量 watch 变量变化时停止 x/32bx addr 查看32字节十六进制内存 x/s addr 按字符串查看内存 bt 查看调用栈 frame N 切换栈帧 up / down 上下切换栈帧 quit / q 退出