本文是对以下文章的摘录:
C语言中文网-gdb调试

调试前提

只有具备调试信息的可执行文件才可被调试,即,编译时需要添加 -g 参数:

#gcc main.c main -g

调试时最好不要优化代码,使用 -O0 默认级别即可。对 GDB 调试器更友好的是 -Og 选项,-Og 对代码所做的优化程序介于 O0 ~ O1 之间,真正可做到“在保持快速编译和良好调试体验的同时,提供较为合理的优化级别”。


常用命令

调试指令 作 用
(gdb) help help commands 能够显示 commands 命令的具体用法
(gdb) file program 调试 program 程序
(gdb) break xxx
(gdb) b xxx
在源代码指定的某一行设置断点,其中 xxx 用于指定具体打断点的位置,可以是函数名、行号(当前源文件)、指定文件中的行号(b main.c:3)等
(gdb) run
(gdb) r
执行被调试的程序,其会自动在第一个断点处暂停执行。其后可以传入参数
(gdb) start 执行到 main 函数中的第一条指令停止。其后可以传入参数
(gdb) continue
(gdb) c
当程序在某一断点处停止运行后,使用该指令可以继续执行,直至遇到下一个断点或者程序结束
(gdb) next
(gdb) n
令程序一行代码一行代码的执行,也可指定一次执行的行数,不步入函数
(gdb) step
(gdb) s
令程序一行代码一行代码的执行,也可指定一次执行的行数,遇到函数则步入
(gdb) until
(gdb) u
使 GDB 调试器快速运行完当前的循环体,并运行至循环体外停止,可指定在某行停下
(gdb) finish 直接执行完当前函数
(gdb) return 立即跳出当前函数(即使有剩余代码也不会继续执行),并返回指定值
(gdb) print var
(gdb) p var
打印指定变量的值,也可以修改变量值:print var=1,高级用法参见print
(gdb) display 每次程序暂停后都打印指定变量,使用 undisplay 或 disable 来取消显示
(gdb) list
(gdb) l
显示源程序代码的内容,包括各行代码所在的行号
(gdb) delete 删除指定编号的断点,不指定则删除全部
(gdb) clear 删除指定行的断点,不指定则删除当前行的断点
(gdb) disable 禁用指定编号的断点,不指定则禁用所有断点
(gdb) enable 开启指定编号的断点,不指定则开启所有断点
(gdb) quit
(gdb) q
终止调试
-q
-quiet
-silent
取消启动 GDB 调试器时打印的介绍信息和版权信息
–args prog arg1
(gdb) set args arg1 …
调试可执行程序 prog 并传入参数 arg1
(gdb) cd 修改 GDB 调试器的工作目录
(gdb) path xx/xx 临时修改环境变量
(gdb) info xxx 提供有关程序状态和调试环境的信息
(gdb) bt
(gdb) backtrace
(gdb) info stack
显示函数的调用堆栈
(gdb) info frame 显示当前函数栈帧的详细信息
(gdb) up 返回上一函数栈帧
(gdb) down 返回下一函数栈帧
(gdb) x 显示指定地址处的值,有多种格式,详细可在 gdb 中输入 help x 查看
(gdb) disassemble 反汇编指定地址处的代码,不指定则反汇编当前函数
(gdb) layout asm 以汇编格式调试整个程序,可以使用 layout src 切换到源代码模式

(gdb) 表示已经运行 gdb,目前是 gdb 中的命令行。

break

gdb 中有三种断点——普通断点、观察断点、捕捉断点。

break 属于普通断点,其常用的语法格式有以下 2 种。

  • (gdb) break location
  • (gdb) break … if cond
location 的值 含 义
linenum linenum 是一个整数,表示要打断点处代码的行号,行号通过 list 命令可以看到。
filename:linenum filename 表示源程序文件名;linenum 表示具体行数。
+ offset - offset offset 为整数(假设值为 2),+offset 表示以当前程序暂停位置(例如第 4 行)为准,向后数 offset 行处(第 6 行)打断点。
function function 表示程序中包含的函数的函数名,即 break 命令会在该函数内部的开头位置打断点,程序会执行到该函数第一行代码处暂停。
filename:function filename 表示远程文件名;function 表示程序中函数的函数名。整体的意思是在指定文件 filename 中 function 函数的开头位置打断点。
(gdb) b 7 if num>10
(gdb) b 8 if p==null

break 还有两种变体:tbreak 和 rbreak

tbreak 和 break 命令的用法和功能都非常相似,唯一的不同在于,使用 tbreak 命令打的断点仅会作用 1 次

rbreak 命令的作用对象是 C、C++ 程序中的函数,它会在指定函数的开头位置打断点 。rbreak 命令的使用语法格式为:

(gdb) rbreak regex

其中 regex 为一个正则表达式,程序中函数的函数名只要满足 regex 条件,rbreak 命令就会其内部的开头位置打断点。

使用 delete 命令清除所有断点,info breakpoints 查看所有断点:

(gdb) delete
(gdb) info breakpoints

watch

watch 断点可以监控程序中某个变量或者表达式的值,只要发生改变,程序就会停止执行:

(gdb) watch var
(gdb) watch var if cond

watch 的变体还有 rwatch 和 awatch:

  • rwatch 命令:只要程序中出现读取目标变量(表达式)的值的操作,程序就会停止运行;
  • awatch 命令:只要程序中出现读取目标变量(表达式)的值或者改变值的操作,程序就会停止运行。

watch 命令有 硬件断点软件断点 两种实现方式。软件断点监控目标变量后,GDB 调试器会以单步执行的方式运行程序,并且每行代码执行完毕后,都会检测该目标变量的值是否发生改变,因此软件断点会影响调试效率。硬件断点通过使用少量的寄存器(例如 32 位的 Intel x86 处理器提供有 4 个调试寄存器)来监控变量的值,而无需每次都主动检测变量是否改变,因此硬件断点不影响调试效率。

大多数 PowerPC 或者基于 x86 的操作系统,都支持采用硬件观点。并且 GDB 调试器在建立观察断点时,会优先尝试建立硬件观察点,只有当前环境不支持硬件观察点时,才会建立软件观察点。

catch

catch 捕捉断点的作用是监控程序中某一事件的发生 ,例如程序发生某种异常时、某一动态库被加载时等等,一旦目标时间发生,则程序停止执行。 tcatch 命令只监控一次事件的发生。

指令 描述
catch exec 捕获对exec的调用。
catch fork 捕获对fork的调用。
catch load 捕获共享库的加载。
catch unload 捕获共享库的卸载。
catch signal 按名称和/或编号捕获信号。
catch syscall 按名称、组和/或编号捕获系统调用。
catch throw 捕获被抛出的异常。

break、watch 有 if 形式,而 catch 则没有,如果想要使其成为条件断点,则需要用到 condition 命令

(gdb) info break
Num     Type            Disp Enb Address                        What
1       breakpoint      keep y     0x00005555555552d0  in main() at main.cpp:9    breakpoint already hit 1 time
2       read watchpoint keep y                                       num
3       catchpoint      keep y                                           exception throw      matching: int
(gdb) condition 1 num==3             <-- 为普通断点添加条件表达式
(gdb) condition 2 num==5             <-- 为观察断点添加条件表达式
(gdb) condition 3 num==7             <-- 为捕捉断点添加条件表达式

frame

(gdb)info frame                              <-- 打印当前栈帧的详细信息
Stack level 0, frame at 0x7fffffffe240:      <-- 栈帧编号0,地址 0x7fffffffe240
rip = 0x4004cf in func (main.c:3); saved rip 0x4004e9   
                                             <-- 函数的存储地址0x4004cf,返回后的地址为0x4004e9
called by frame at 0x7fffffffe260            <-- 当前栈帧的上一级栈帧(编号 1 的栈帧)的地址为 0x7fffffffe260
source language c.                           <-- 当前栈帧使用的编程语言
Arglist at 0x7fffffffe230, args: num=4       <-- 函数参数的地址和值
Locals at 0x7fffffffe230, Previous frame's sp is 0x7fffffffe240  
                                             <--函数内部局部变量的存储地址
Saved registers:                             <-- 栈帧内部存储的寄存器
  rbp at 0x7fffffffe230, rip at 0x7fffffffe238

info

常见的 info 命令如下:

命令 描述
info breakpoints 列出当前设置的所有普通断点
info watchpoints 列出当前设置的所有观察断点
info registers 显示所有寄存器的当前值
info address 显示符号对应的地址
info threads 列出当前正在运行的所有线程
info functions 列出程序中定义的所有函数
info variables 列出 当前所有可见 的变量
info locals 打印当前函数中所有局部变量的值
info args 打印参数值
info source 显示当前源文件的名称和行号
info sources 显示此程序包含的所有源文件
info frame 显示当前帧的信息,包括函数名称和参数
info stack 显示调用堆栈
info program 显示正在调试的程序的名称和进程ID
info sharedlibrary 列出当前加载的共享库
info target 显示当前目标的信息,例如目标文件或核心转储文件
info display display 命令查看的目标变量或表达式

更多 info 参数可输入 help info 进行查看。


调试方式

  1. 调试尚未运行的程序:

    #gdb program
    或者 
    (gdb) file program
    
  2. 调试正在运行的程序

    (gdb) attach program
    或者
    #gdb -p 进程号
    

    这种方式可能需要 root 权限。调式完毕后可执行 detach 指令,使 GDB 调试器和程序分离:

    #(gdb) detach
    或者直接退出
    #(gdb) q
    

核心转储

在 Linux 操作系统中,当程序执行发生异常崩溃时,系统可以将发生崩溃时的内存数据、调用堆栈情况等信息自动记录下载,并存储到一个文件中,该文件通常称为 core 文件,Linux 系统所具备的这种功能又称为核心转储(core dump)。当程序发生异常崩溃时,通过 GDB 调试 core 文件,往往可以更快速的解决问题。

默认情况下,Linux 系统不开启 core dump 这一功能,执行以下命令:

[root@bogon demo]# ulimit -a
core file size          (blocks, -c) 0
.......

如果 core file size(core 文件大小)对应的值为 0,表示当前系统未开启 core dump 功能。这种情况下,可以通过执行如下指令改变 core 文件的大小:

[root@bogon demo]# ulimit -c unlimited
[root@bogon demo]# ulimit -a
core file size     (blocks, -c) unlimited

假设执行 main.exe 发生数组越界错误生成 core 文件,则调试方法如下:

[root@bogon demo]# gdb main.exe core

如果没有生成 core,请参考此处

注意,Core文件中包含了程序运行时的内存映像(代码、数据和堆栈信息)、进程状态、系统信息、调试符号等,如果程序在崩溃时使用了大量的内存,那么 Core 文件可能会很大,可以使用 ulimit 命令来限制 Core 文件的最大大小。

文章作者: 极简
本文链接:
版权声明: 本站所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 后端技术分享
工具
喜欢就支持一下吧