1. GDB简介

gdb是gnu debugger的缩写, 是一款可以用于源码级和汇编级调试的无图形界面调试器, 虽然对新手并不算友好, 但是和vim一样, 都属于花时间学了就会真香的小工具,学费了就直接抛弃IDE和vscode吧…

2. 源码级调试

2.1 控制要调试的进程

gdb <executable_file_name>可以启动调试器, 获得如下界面

gdb start

  • run可以启动待调试的可执行文件, 可以简写成r. 如果启动需要带上命令行参数, 就直接加在后面.

例如r BV1zuCXYfE6z --resolution=1080p --downloader=/usr/bin/curl

  • start可以自动启动程序, 并在main函数的第一行停下, 若没有调试信息, 也会完成动态库加载等准备工作后之后停在入口处

  • kill可以杀死正在调试的进程

  • continue可以使程序继续运行, 直到遇到断点或崩溃

  • finish可以使程序运行到当前函数结束, 返回调用地点

gdb的几乎所有命令都能够简写, 在不具有二义性的情况下可以写尽量少的字母, 例如run可以写成r, continue可以写成c, 也可以使用TAB键补全命令

2.2 断点

gdb的断点指令是break, 可以只简写成``.

  • b <function>: 使程序在进入函数后的第一条语句处停下. 如果是C++代码, 可以写出作用域加函数原型, 保证断点在正确的重载处

  • b <line-number>: 将断点打在当前文件的对应行号处

  • b <file_name>:<line_number>: 将断点打在指定文件名称的指定行号处

  • info breakpoints: 查看断点信息

  • delete breakpoints <breakpoint number>: 删除对应标号的断点, 若没给出标号, 就删除所有断点

2.3 单步运行

单步不进入

next可以单步运行代码(简写n), 不进入下一层的函数内部

单步进入

step可以单步运行代码(简写s), 会进入下一层函数

例如: 当代码走到auto res...这一行停下时, 键入n会进入下一行std::fstream output_file;, 而键入s则会进入qrc_decode函数内部

1
2
3
4
5
6
// decode...
auto res = qqmusic::utils::qrc_decode(in_buf, out_buf, qqmusic::utils::qrc_type::local);
std::fstream output_file;
std::string output_file_name = (std::string)argv[i] + ".decode";
ulog(MSG_INFO, "output file is %s", output_file_name.c_str());
output_file.open(output_file_name, std::ios::out);

2.4 查看信息

print

print可以简写为p, 可以打印变量的值或表达式的值

print variable

p后面可以加一些修饰来表示以指定的格式输出内容

  • x: 十六进制显示

  • d: 十进制显示

  • u: 十六进制无符号

  • i: 反汇编之后显示

  • t: 八进制显示

  • c: 字符格式显示

  • f: 浮点数格式显示

  • s: 按C风格字符串显示

使用案例: p/s str1, 以字符串打印str1变量

x

x指令可以打印内存地址和寄存器中的值. 用法: x/nfu address

  • n 是一个正整数, 指定显示address向高地址方向打印的内存单元的数量, 不给出就默认一个

  • f 是指定显示格式的, 和上面p的选项一致, 默认十六进制显示

  • u 是指定内存单元长度的选项, b表示单字节, h表示双字节, w表示四字节, h表示八字节

使用案例: x/50wx char_ptr就是十六机制查看char_ptr指向的地址到char_ptr + 50 * 4地址, 也就是char_ptr指向地址偏移200字节内的内容

3. 汇编级调试

在没有调试信息时gdb仍然可以根据符号表进行调试

3.1 反汇编

  • disassemble <function>可以反汇编指定的函数

  • x/i $rip可以反汇编指令寄存器指向的指令

3.2 汇编级单步执行

  • ni: 汇编级单步不进入(遇到call指令不进入函数)

  • si: 汇编级单步进入(遇到call指令进入函数)

3.3 查看内存与寄存器

  • info register: 简写i r, 查看所有寄存器信息

register info

  • 使用x指令查看寄存器和其指向内容–例:x/50wx $rsp查看栈顶指针寄存器向高地址方向的200字节, 4字节一组十六进制显示

大部分常用的调试技巧其实只有寥寥几条指令, 读者稍加练习就能掌握, 并体验到命令行调试器的优势.

如果需要更详细的gdb调试技巧, 可以看看100个gdb小技巧.