Article

Neovim 配置 clangd 与 GDB 远程调试 xv6

前言

之前在 macOS 下从零配置 Neovim 里,我把 Neovim 的基础外观、目录树和终端插件配好了;后来整理 Neovim 快捷键速查 时,留了个坑说 LSP 和 DAP 的配置还没写。

正好最近在写 MIT 6.1810 的 xv6 实验,每天都要在内核源码里跳来跳去,还要连上 QEMU 单步跟代码。这篇就把我本地 ~/.config/nvim 里的 clangd 语言服务和 nvim-dap 调试配置补上。

重要

LSP 与 DAP 的职责划分

  • 代码跳转与补全(LSP):走的是 Neovim → clangd,靠项目根目录的 .clangd 或 compile_commands.json 看懂代码结构。
  • 打断点单步调试(DAP):走的是 Neovim → nvim-dap → riscv64-elf-gdb → QEMU,靠编译出来的 ELF 符号表和远程端口连上虚拟机。

这两条路是完全独立的:gd 能跳转到函数,不代表 GDB 已经加载了符号;反过来 GDB 能连上 QEMU,也不代表 clangd 认识交叉编译的头文件。

配置 clangd 语言服务

1. 启用内置 LSP 客户端

我在 lua/plugins/lsp.lua 里直接用了 Neovim 0.11+ 原生自带的 vim.lsp.config 和 vim.lsp.enable 接口(参考 Neovim LSP 文档),不用再装 nvim-lspconfig 也能直接配:

lua
vim.lsp.config("clangd", {
  cmd = {
    "/usr/bin/clangd",
    "--background-index",
    "--clang-tidy",
    "--completion-style=detailed",
  },
  root_markers = {
    "compile_commands.json",
    ".clangd",
    ".git",
  },
})

vim.lsp.enable("clangd")

这里的 /usr/bin/clangd 是 Mac 自带 Command Line Tools 里的路径;如果自己用 Homebrew 装了 LLVM,改成对应的路径就行。

2. 配置 RISC-V 交叉编译参数

因为我的电脑是 Apple Silicon 的 Mac,如果什么都不配,clangd 默认会拿 macOS 的系统头文件去检查 xv6 代码,满屏都会是架构不匹配的报错。

最简单的办法是在 xv6 仓库根目录下建一个 .clangd 文件,加上交叉编译的目标架构(参考 clangd 配置说明):

yaml
CompileFlags:
  Add:
    - --target=riscv64-unknown-elf

提示

提示 上面这个简短的 .clangd 只是让它认识 RISC-V 架构,还没把 Makefile 里的 -ffreestanding、-I. 等参数全写进去。如果遇到稍微大一点的 C/C++ 项目,更推荐用 bear -- make 生成一份完整的 compile_commands.json 放在项目根目录下(详见 clangd 编译数据库说明)。

3. 绑定 LSP 缓冲区快捷键

我把 gd、gr、K 这些快捷键放在了 LspAttach 自动命令里,只有当前缓冲区真的连上了语言服务器才会生效,不会去干扰右侧目录树或者左侧终端窗口:

lua
vim.api.nvim_create_autocmd("LspAttach", {
  callback = function(args)
    local opts = { buffer = args.buf }
    vim.keymap.set("n", "gd", vim.lsp.buf.definition, opts)
    vim.keymap.set("n", "gr", vim.lsp.buf.references, opts)
    vim.keymap.set("n", "K", vim.lsp.buf.hover, opts)
    vim.keymap.set("n", "<leader>rn", vim.lsp.buf.rename, opts)
    vim.keymap.set("n", "<leader>ca", vim.lsp.buf.code_action, opts)
  end,
})

如果打开 .c 文件发现按 gd 没反应,可以先敲这几条命令看看是不是 clangd 没连上:

vim
:checkhealth vim.lsp
:verbose nmap gd
:lua vim.print(vim.lsp.get_clients({bufnr = 0}))

另外我没有专门装自动弹窗的补全插件,写代码时想看补全提示,在插入模式下按原生的 <C-x><C-o> 就能调出 LSP 的补全列表。

配置 nvim-dap 与 GDB 适配器

1. 注册 GDB DAP 适配器

新版 GDB(14.1 以后)自己就内置了 --interpreter=dap 模式(见 GDB DAP 文档),不需要再去装第三方转接工具。

在 lua/plugins/dap.lua 里,我把之前用 Homebrew 装的 riscv64-elf-gdb 注册成了名为 gdb 的适配器:

lua
local dap = require("dap")

dap.adapters.gdb = {
  type = "executable",
  command = "riscv64-elf-gdb",
  args = {
    "-q",
    "-nx",
    "--interpreter=dap",
  },
}

注意里面的 -nx 参数很关键:它会让 GDB 忽略项目目录下的 .gdbinit 文件,避免 .gdbinit 里自带的自动连接命令和 nvim-dap 自己的连接流程撞车。

2. 添加项目调试连接配置

整理这篇笔记翻看自己的 dap.lua 时,我发现当时只写了上面的 dap.adapters.gdb,漏写了告诉它“具体怎么连 xv6”的 dap.configurations.c。如果少了这一段,按 <Space>dc 开始调试的时候就会没有配置可选。

在 dap.lua 的适配器配置下面补上这段即可:

lua
dap.configurations.c = {
  {
    name = "xv6: attach QEMU",
    type = "gdb",
    request = "attach",
    program = "${workspaceFolder}/kernel/kernel",
    target = "127.0.0.1:25501",
  },
}

这里有两个地方要注意:

  • program 指向的是 kernel/kernel(内核二进制)。
  • target 里的端口 25501 是我这台电脑上算出来的值。xv6 的 Makefile 会根据系统用户的 UID 算出一个端口号,配之前先在 xv6 目录里敲一下 make print-gdbport 看看你自己的端口是多少:
bash
make print-gdbport

启动调试与常用快捷键

因为上面配的是 attach(附加到已经跑起来的 QEMU),按 <Space>dc 并不会自动帮你跑 make。平时调试分两步走:

  1. 先在终端(或者按 <Space>tt 打开 Neovim 左侧终端)里启动等待调试的 QEMU:

    bash
    make CPUS=1 qemu-gdb
  2. 切回代码窗口,在想停的地方按 <F9> 打个断点,然后按 <Space>dc 选择 xv6: attach QEMU。

连上之后,底部的 nvim-dap-ui 面板会自动弹出来显示变量、寄存器和调用栈,代码行尾也会直接显示变量当前的值:

常用操作快捷键
打断点 / 取消断点<F9>
开始调试 / 继续运行<Space>dc
单步跳过(不进函数)<F10>
单步进入函数<F11>
跳出当前函数<F12>
弹窗查看变量或表达式的值<Space>de
打开 / 关闭 GDB 命令行控制台(REPL)<Space>dr
结束调试并关掉底部面板<Space>dq

用户态程序的符号加载

用上面那套配置直接给 kernel/ 底下的内核文件打断点非常丝滑,但如果你打开 user/find.c 按 <F9> 打断点,会发现断点变成 pending,GDB 还会提示找不到 find.c。

这是因为配置里的 program 只加载了内核 kernel/kernel 的符号表,还没有加载 user/_find 的符号。遇到这个问题不用去重装插件,按 <Space>dr 打开调试控制台,执行 add-symbol-file user/_find 把用户程序的符号追加进去即可,具体排查过程见 xv6 调试排错:解决 GDB 找不到用户态源码与断点挂起。

Neovim 配置 clangd 与 GDB 远程调试 xv6

https://www.renpengyu.com/articles/neovim-clangd-dap/
作者
Penry
发布于
2026.10.22
许可协议
CC BY-NC-SA 4.0
站内搜索

找到你感兴趣的内容

文章分类
选择文章分类,匹配任意一个所选分类

多选时显示属于任一所选分类的文章。

文章标签
选择文章标签,多个标签需同时包含

多选时只显示同时包含所选标签的文章。

输入关键词开始搜索