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, &regs); // 读取寄存器 
        // 分析或修改寄存器 
        ptrace (PTRACE_SETREGS, child, NULL, &regs); // 设置寄存器 
        ptrace (PTRACE_SINGLESTEP, child, NULL, NULL); // 单步执行 
        wait (&status); // 等待子进程下次停止 
    }
}

不同的操作系统也都提供了类似 ptrace 的机制来允许进程之间的调试或者控制:

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 有更好一些的性能和现代的特性。