1. 逐个介绍这三大 Debugger
1.1 Linux 下的 GDB
1.1.1 原理
GDB(GNU Debugger)是在 Unix-like OS 上使用的开源 debugger,支持多种编程语言,如 C、C++ 和 Fortran。其原理是通过 Unix-like OS 上的 ptrace
系统调用来控制另一个进程的执行、获取和改变其内存中的内容以及其寄存器的状态。
伪代码用于说明 GDB 的工作原理:
int main(int argc, char **argv) {
Debugger gdb = initialize_debugger ();
Program program = load_program (argv [1]);
set_breakpoint (program, line_number);
while (execute_next_instruction (program)) {
if (current_instruction (program) == breakpoint) {
pause_execution (program);
print_debug_info (program);
switch (wait_for_user_input ()) {
case CONTINUE: continue_execution (program); break;
case STEP: step_into (program); break;
case QUIT: exit(0); break;
default: handle_other_commands (); break;
}
}
}
return 0;
}
ptrace
是 POSIX 兼容的一个系统调用,涉及两个进程:一个是控制进程(GDB),另一个是被控制进程(被调试的程序),可以使得一个进程去观察和控制另一个进程的执行,并能改变其寄存器和内存值。ptrace
直接提供了很多请求代码,像是以下操作可以直接返回结果:
-
启动、停止子进程的执行
-
读写子进程的内存
-
读写子进程的寄存器
-
单步执行
-
信号处理
给出一个 ptrace
使用示例的伪代码:
pid_t child = fork ();
if (child == 0) {
// 子进程:被调试的进程
ptrace (PTRACE_TRACEME, 0, NULL, NULL);
execvp (program, program_args); // 启动目标程序
} else {
// 父进程: GDB 进程
int status;
wait (&status); // 等待子进程停止
ptrace (PTRACE_SETOPTIONS, child, NULL, PTRACE_O_EXITKILL);
while (WIFSTOPPED (status)) {
struct user_regs_struct regs;
ptrace (PTRACE_GETREGS, child, NULL, ®s); // 读取寄存器
// 分析或修改寄存器
ptrace (PTRACE_SETREGS, child, NULL, ®s); // 设置寄存器
ptrace (PTRACE_SINGLESTEP, child, NULL, NULL); // 单步执行
wait (&status); // 等待子进程下次停止
}
}
不同的操作系统也都提供了类似 ptrace
的机制来允许进程之间的调试或者控制:
-
Windows 提供了调试 API,其中比如包括函数
DebugActiveProcess
来附加到一个进程,以及WaitForDebugEvent
和ContinueDebugEvent
来处理调试事件。
1.1.2 使用方式
GDB 一般在命令行里使用,可以启动 GDB 附加到一个正在运行的进程,或者加载一个程序的 core dump 文件。
1.2 最强大的(之一)WinDbg
1.2.1 原理
WinDbg 是微软提供的一个 Windows 平台的 debugger,用于调试 Windows 内核、驱动程序以及其他程序。WinDbg 使用 Windows 的调试引擎(dbgeng.dll)作为核心,可通过各种协议(如 USB、1394、网络等)与目标系统进行通信。WinDbg 能够处理复杂的调试任务,包括内存泄漏检测、性能问题分析、代码执行跟踪等。
WinDbg 有着其他调试器很少具备的系统级调试能力,查阅文档发现,是通过与 Windows 操作系统内核的深度集成实现的。
-
内核模式和用户模式:可在内核模式下运行,可以直接与操作系统内核交互,提供对驱动程序和硬件级别事件的调试能力。
-
符号服务器:用微软的符号服务器来获取系统和应用程序的符号信息,而在 Linux 下一般需要编译器编译时指定
-g
选项来产生本地的符号信息。微软的做法更方便,但也受其限制。 -
内核调试器协议:用专门的协议与目标系统通信,支持调试器在不同的机器上运行,从而调试另一台机器上的系统。
1.2.2 使用方式
WinDbg 提供了 GUI 和命令行,语法与 GDB 不同,但功能相似,还提供了脚本功能,以及与 Windows 深度集成的特性,如分析 kernel mode 和 dump 文件。
1.3 跨平台、没用过的 LLDB
1.3.1 原理
LLDB 是一个模块化的 debugger,支持 C、C++ 以及其他语言,并且跨平台。LLDB 使用 LLVM 的一些组件作为其后端,比如 JIT 编译的代码调试等。LLDB 的原理同样基于控制和检查进程的执行,但它基于 LLVM。
- 用 LLVM 的代码生成工具来优化调试信息的生成和使用。
- 提供了脚本接口,尤其是 Python,可编写脚本来扩展调试器。
1.3.2 使用方式
LLDB 与 GDB 相似,但有自己的一套扩展。LLDB 还可以集成到 IDE 中,如 Xcode 或 Visual Studio Code,提供 GUI 调试的功能。
2. Summary
-
都是 debugger,基本原理都是通过控制程序的执行,来提供查看和修改内存、寄存器等能力。
-
GDB 和 LLDB 是传统的调试模式,而 WinDbg 提供了更加深入的系统级调试。
-
LLDB 使用了 LLVM 的现代架构,在某些方面比 GDB 有更好一些的性能和现代的特性。