功能介绍#
OS Debug 是一款 VS Code 调试插件,让你可以在 VS Code 中像调试普通程序一样调试操作系统内核,以下是你安装插件后可以做的事情。
一、像调试普通程序一样调试 OS 内核#
1. 设置断点#
在源代码行号旁点击即可设置断点,支持在内核代码和用户程序代码中同时设置断点。除了普通断点外,还支持:
- 条件断点:右键断点,输入条件表达式(如
i > 10),只有条件满足时才暂停 - 计数断点:设置命中次数,例如"第 5 次经过这里才停下来"
- 日志断点(Log Points):不暂停程序,只在调试控制台输出一条日志消息,适合不想打断程序执行流程时使用
2. 单步调试#
程序暂停后,你可以使用调试工具栏进行:
- 继续(Continue):运行到下一个断点
- 单步跳过(Step Over):执行当前行,不进入函数内部
- 单步进入(Step Into):进入函数内部逐行执行
- 单步跳出(Step Out):执行完当前函数,回到调用处
- 逐指令步进:按机器指令逐条执行,适合分析汇编级行为
3. 查看变量#
程序暂停时,可以在 VS Code 的调试侧边栏中查看:
- 局部变量:自动展示当前作用域内的所有局部变量及其值
- 结构体/数组展开:复杂数据结构(结构体、数组、指针等)可以点击展开,逐层查看内部字段
- 鼠标悬停查看:将鼠标悬停在源代码中的变量名上,即可弹出当前值
- 修改变量值:在变量面板中双击变量值,可以直接修改,立即生效
- 表达式求值:在调试控制台中输入任意表达式(如
a + b * 2),实时查看计算结果
4. 查看寄存器#
在调试侧边栏的 Registers 区域,可以查看 RISC-V 处理器的所有寄存器值(如 ra、sp、pc、a0-a7、t0-t6 等)。也可以通过配置只显示你关心的寄存器。
5. 查看调用栈#
在调试侧边栏的调用栈区域,可以看到当前的函数调用链,点击任意一层即可跳转到对应的源代码位置,查看该层的局部变量。
6. 查看和编辑内存#
通过命令面板执行 “Examine Memory Location” 命令,输入内存地址,即可在 VS Code 的十六进制编辑器中查看和编辑原始内存数据。
二、跨越内核/用户态的联合调试(核心特色)#
这是本插件区别于普通调试器的最大特色。操作系统在运行时会在内核态和用户态之间频繁切换,传统调试器在这种场景下断点经常失效。OS Debug 解决了这个问题。
1. 自动切换调试上下文#
你只需要在内核代码和用户程序代码中各自设好断点,然后正常点击 Continue。当操作系统从内核态切换到用户态(或反过来)时,插件会自动完成以下操作:
- 保存当前地址空间的断点
- 卸载当前符号表,加载目标地址空间的符号表
- 恢复目标地址空间的断点
- 继续执行
整个过程对你透明,你只会看到程序停在了你设置的下一个断点上,无论它在内核还是在用户程序中。
2. 设置边界断点#
边界断点用来告诉插件"内核和用户态的切换发生在哪里"。你有两种方式设置:
- 从 launch.json 批量设置:在配置文件中预先定义好边界位置,点击编辑器标题栏的 “Set Border Breakpoints” 按钮一键加载
- 右键菜单设置:在代码行号右键,选择 “Set Breakpoint As Border” 将某个已有断点标记为边界断点;选择 “Disable Border” 可以取消
3. 设置钩子断点#
钩子断点设在内核的进程调度相关代码上。当内核准备切换到某个用户进程时,钩子会自动获取即将运行的进程名称,让插件知道接下来应该加载哪个用户程序的符号表和断点。
设置方式与边界断点类似:在 launch.json 中配置,然后点击 “Set Hook Breakpoints” 按钮加载。
4. 同时调试多个用户程序#
操作系统上通常同时运行着多个用户程序。你可以在不同用户程序的源代码中各自设置断点,插件会自动识别当前正在执行的是哪个程序,并加载对应的断点和符号表。
三、支持 QEMU 虚拟机调试#
插件可以直接在 VS Code 内启动 QEMU,无需手动在终端中执行 QEMU 命令。在 launch.json 中配置好 qemuPath 和 qemuArgs 后,按 F5 即可:
- 在 VS Code 集成终端中自动启动 QEMU
- GDB 自动连接到 QEMU 的调试端口
- 直接进入调试状态
支持的 QEMU 启动参数包括机器类型、内存大小、SMP 核心数、块设备、图形设备等,均可在 launch.json 中灵活配置。
四、支持 SSH 远程调试#
如果你的操作系统运行在远程服务器上,可以通过 SSH 连接远程机器进行调试,无需在远程机器上安装 VS Code。
在 launch.json 的 ssh 字段中配置主机地址、用户名和认证方式(密码或密钥)即可。插件会:
- 通过 SSH 在远程机器上启动 GDB
- 自动映射远程/本地的源文件路径,你在本地 VS Code 中看到的代码与远程一致
- 支持 X11 转发,可以在本地显示远程的图形界面
五、支持真实硬件调试#
除了 QEMU 虚拟机,插件也支持调试运行在真实 RISC-V 硬件(如昉·星光 2 开发板)上的操作系统。通过 JTAG 接口和 OpenOCD 连接硬件,GDB 即可对硬件上运行的内核进行源代码级调试。
六、eBPF 动态跟踪#
插件内置了一个 eBPF 调试面板,可以在不暂停程序的情况下动态跟踪内核和用户程序的执行。在面板中你可以:
- 连接 eBPF side-stub
- 查看并搜索内核/用户程序的符号表
- 选择感兴趣的函数地址,注册 kprobe(内核探针)或 uprobe(用户探针)
- 在探针被触发时收集寄存器值等运行时信息
这为性能分析和问题排查提供了一种不干扰程序运行的轻量级方法。
七、支持多种语言和平台#
插件并不局限于某一种编程语言。支持的语言包括:
C、C++、Rust、D、Objective-C、Fortran、Pascal、Ada、Nim、Zig、Kotlin、Crystal、Vala、RISC-V 汇编 等所有 GDB 支持调试的语言。
目前已验证的操作系统调试场景:
| 操作系统 | 语言 | 运行环境 |
|---|---|---|
| rCore-Tutorial | Rust | QEMU (RISC-V) |
| xv6 | C | QEMU (RISC-V) |
| rCore-Tutorial | Rust | 昉·星光 2 开发板 (RISC-V) |
八、灵活的配置方式#
所有调试行为均通过 VS Code 标准的 launch.json 进行配置,无需修改插件代码。你可以配置:
- QEMU 的启动命令和参数
- 内核/用户态的内存地址范围
- 边界断点和钩子断点的位置
- 文件路径与断点组的映射规则
- 断点组与符号表文件的映射规则
- GDB 启动时自动执行的命令
- SSH 连接参数
- 变量的显示格式
- 要显示的寄存器列表
插件支持 ${workspaceFolder} 等 VS Code 变量替换,同一份配置在不同机器上都能正常使用。