环境准备
用 vscode 进行调试需要配合 debug 工具使用,一般系统会自带这些工具,如果你的系统下没有对应的可执行文件,请自行从网上下载并能够正常执行。
- Linux:gdb
- MacOS(包含 M 系列芯片): lldb
除了 debug 工具外,还要确保能够正常编译出 redis 的可执行文件,具体可以看官方指南。
编译 redis
我们可以直接在源码根目录下执行 make
来编译 redis 可执行文件,但默认情况下,redis 编译会开启 -O2
优化,这种情况下很难使用 debug 工具进行调试。我们可以将编译命令换成 make noopt
:
$ make noopt
如果在 Linux 上出现 jemalloc/jemalloc.h: No such file or directory
相关错误可以加上参数:
make noopt MALLOC=libc
执行完成后,在 src
目录下会生成 redis-server
、redis-cli
、redis-sentinel
等可执行文件,即对应 redis 服务端、客户端和哨兵。
vscode 配置
从 Run and Debug
面板创建一个 launch.json
文件,不同的操作系统对应不同的配置。
小技巧:在 configurations
下输入 gdb
或 lldb
可以快速生成启动模板,一般情况下选择 Launch
就行。
Linux
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"name": "(gdb) Launch", // 名字随便取
"type": "cppdbg",
"request": "launch",
// 可执行程序位置,这里我们启动 redis 服务端
"program": "${workspaceFolder}/src/redis-server",
// 程序参数
"args": [
// 例如:使用配置文件启动
// "/path/to/your/redis.conf"
],
"stopAtEntry": false,
// 可执行程序的工作目录,也可以使用绝对路径
"cwd": "${fileDirname}",
"environment": [],
"externalConsole": false,
"MIMode": "gdb",
"setupCommands": [
{
"description": "Enable pretty-printing for gdb",
"text": "-enable-pretty-printing",
"ignoreFailures": true
},
{
"description": "Set Disassembly Flavor to Intel",
"text": "-gdb-set disassembly-flavor intel",
"ignoreFailures": true
}
]
}
]
}
MacOS
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"name": "(lldb Launch)", // 名字随便取
"type": "cppdbg",
"request": "launch",
// 可执行程序位置,这里我们启动 redis 服务端
"program": "${workspaceFolder}/src/redis-server",
// 程序参数
"args": [
// 例如:使用配置文件启动
// "/path/to/your/redis.conf"
],
"stopAtEntry": false,
// 可执行程序的工作目录,也可以使用绝对路径
"cwd": "${fileDirname}",
"environment": [],
"externalConsole": false,
"MIMode": "lldb",
"setupCommands": [
{
"description": "Enable pretty-printing",
"text": "-enable-pretty-printing",
"ignoreFailures": true
}
]
}
]
}
配置完成后,我们就可以在程序中打断点进行调试了。例如在源码 src/server.c
的入口函数中打上断点,再使用 vscode 启动程序,就能够成功进入断点了:
最后贴一下 AI 总结的调试器底层原理:
GDB 和 LLDB 这类调试器可以调试二进制文件的底层原理主要有以下几点:
程序中断机制 - 调试器可以设置断点,当程序执行到断点时会触发中断,转而执行调试器的代码。这通常是通过修改可执行文件的机器代码实现的。 寄存器和内存读取 - 调试器可以读取 cpu 寄存器和内存的值,来检查程序的运行状态。这通过调试器和 cpu 之间的接口实现。 反汇编 - 调试器可以将二进制代码反汇编成汇编语言,以便于理解程序在做什么。 函数调用栈 - 调试器可以显示和操作函数调用栈,用于跟踪程序的执行流程。 符号表 - 调试器使用可执行文件中的调试符号表信息,将机器代码映射到源代码,以便于开发者调试。 线程控制 - 调试器可以控制不同线程的执行,用于多线程程序调试。 观察点 - 除了断点外,调试器还支持在读取/写入内存时等触发观察点。 总之,调试器直接作用于程序的机器代码,并通过不同的接口查看和控制程序的运行,来实现调试的功能。良好的二进制接口是调试器可以顺利工作的基础。
-- Claude