功能介绍#

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 处理器的所有寄存器值(如 rasppca0-a7t0-t6 等)。也可以通过配置只显示你关心的寄存器。

5. 查看调用栈#

在调试侧边栏的调用栈区域,可以看到当前的函数调用链,点击任意一层即可跳转到对应的源代码位置,查看该层的局部变量。

6. 查看和编辑内存#

通过命令面板执行 “Examine Memory Location” 命令,输入内存地址,即可在 VS Code 的十六进制编辑器中查看和编辑原始内存数据。


二、跨越内核/用户态的联合调试(核心特色)#

这是本插件区别于普通调试器的最大特色。操作系统在运行时会在内核态和用户态之间频繁切换,传统调试器在这种场景下断点经常失效。OS Debug 解决了这个问题。

1. 自动切换调试上下文#

你只需要在内核代码和用户程序代码中各自设好断点,然后正常点击 Continue。当操作系统从内核态切换到用户态(或反过来)时,插件会自动完成以下操作:

  1. 保存当前地址空间的断点
  2. 卸载当前符号表,加载目标地址空间的符号表
  3. 恢复目标地址空间的断点
  4. 继续执行

整个过程对你透明,你只会看到程序停在了你设置的下一个断点上,无论它在内核还是在用户程序中。

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 中配置好 qemuPathqemuArgs 后,按 F5 即可:

  1. 在 VS Code 集成终端中自动启动 QEMU
  2. GDB 自动连接到 QEMU 的调试端口
  3. 直接进入调试状态

支持的 QEMU 启动参数包括机器类型、内存大小、SMP 核心数、块设备、图形设备等,均可在 launch.json 中灵活配置。


四、支持 SSH 远程调试#

如果你的操作系统运行在远程服务器上,可以通过 SSH 连接远程机器进行调试,无需在远程机器上安装 VS Code。

launch.jsonssh 字段中配置主机地址、用户名和认证方式(密码或密钥)即可。插件会:

  • 通过 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-TutorialRustQEMU (RISC-V)
xv6CQEMU (RISC-V)
rCore-TutorialRust昉·星光 2 开发板 (RISC-V)

八、灵活的配置方式#

所有调试行为均通过 VS Code 标准的 launch.json 进行配置,无需修改插件代码。你可以配置:

  • QEMU 的启动命令和参数
  • 内核/用户态的内存地址范围
  • 边界断点和钩子断点的位置
  • 文件路径与断点组的映射规则
  • 断点组与符号表文件的映射规则
  • GDB 启动时自动执行的命令
  • SSH 连接参数
  • 变量的显示格式
  • 要显示的寄存器列表

插件支持 ${workspaceFolder} 等 VS Code 变量替换,同一份配置在不同机器上都能正常使用。