VS Code 调试配置
主线位置:调试主线的入口。VS Code 按
launch.json发 DAP 的launch或attach请求,请求的参数原样落进probe-rs dap-server的配置结构体;之后的会话三阶段、请求循环、每次停下全量展开,在 DAP server 与 VS Code 里讲过。本页只讲配置:每个字段在源码里是什么、默认值是多少、哪些组合会被拒绝。
flowchart TB json["launch.json 一项"] -->|"DAP launch / attach 的 arguments"| sc["SessionConfig:chip、probe、speed 等"] sc --> fc["flashingConfig:FlashingConfig"] sc --> cc["coreConfigs:Vec of CoreConfig,必须恰好一个"] cc --> rtt["平铺的 RttConfig:rttEnabled、rttChannelFormats"] cc --> vc["catchReset、catchHardfault 等"] sc --> sd["SessionData::new:attach、烧录、加载 SVD"]
一份能用的配置
把上一页的 ticker 放到 VS Code 里调试,launch.json 大致是这样:
{
"version": "0.2.0",
"configurations": [
{
"type": "probe-rs-debug",
"request": "launch",
"name": "ticker on B-L475E-IOT01A",
"cwd": "${workspaceFolder}",
"chip": "STM32L475VGTx",
"flashingConfig": {
"flashingEnabled": true,
"haltAfterReset": false
},
"coreConfigs": [
{
"coreIndex": 0,
"programBinary": "target/thumbv7em-none-eabihf/debug/ticker",
"rttEnabled": true
}
]
}
]
}
type、request、name 是 VS Code 的约定:type 是 probe-rs 的 VS Code 扩展注册的调试器类型,扩展不在本仓库里,本卷没有核对它的源码;request 决定 DAP 请求是 launch 还是 attach。其余字段都由 dap-server 解析。扩展怎么启动 probe-rs dap-server(stdio 还是 TCP)也在扩展那一侧,服务端两种都支持(DAP server)。真板子上的调试会话待实测。
会话级字段:SessionConfig
SessionConfig 标了 #[serde(rename_all = "camelCase")],所以 Rust 的 chip_description_path 在 JSON 里写成 chipDescriptionPath:
| JSON 字段 | 类型 / 默认 | 说明 |
|---|---|---|
chip | 可省 | 芯片名,规则同命令行的 --chip(目标描述) |
chipDescriptionPath | 可省 | 自定义目标的 YAML,运行时加进注册表;PROBE_RS_TARGETS_DIR 在这里同样不起作用(常见坑) |
probe | 可省 | 探针选择器,VID:PID 或 VID:PID:序列号;不给而插着多个探针时,DAP 以非交互模式选探针(configuration.rs:259),直接报「N probes were found」 |
speed、wireProtocol、connectUnderReset | 可省 / 可省 / false | 同命令行的 --speed、--protocol、--connect-under-reset |
allowEraseAll | false | 允许为解锁而整片擦除(nRF 的 CTRL-AP 擦除之类,见调试序列与 vendor) |
consoleLogLevel | Console | 调试控制台里显示多少 probe-rs 自己的日志:Console、Info、Debug |
cwd | 可省 | 相对路径的基准 |
remoteServerMode | false | dap-server 跑在另一台机器上时,ELF、SVD、芯片描述以 base64 内联在请求里传过去 |
flashingConfig | 全 false | 见下一节 |
coreConfigs | 必填 | 见再下一节 |
烧录:FlashingConfig
FlashingConfig 的六个布尔量默认全是 false:flashingEnabled、haltAfterReset、verifyBeforeFlashing、verifyAfterFlashing、fullChipErase、restoreUnwrittenBytes,另有 formatOptions(镜像格式,同命令行的 --binary-format)。
两件容易误会的事:
flashingEnabled默认关。不写它,launch不烧录,调的是芯片里原有的固件,而断点、源码行用的是programBinary那个 ELF,二者对不上时断点会落在奇怪的地方。haltAfterReset不决定「复位不复位」。launch总是先reset_and_halt,好让 VS Code 在main之前设好断点;configurationDone之后才看它:为false就让核跑起来,为true就停在复位处(adapter.rs:797)。停在复位处时 RTT 控制块还没被固件初始化,ControlBlockNotFound的报错原文专门提了这一句(常见坑:RTT 什么都不打印)。
request: "attach" 时这几个选项不能用:validate_configuration_option_compatibility 发现 flashingEnabled、haltAfterReset、fullChipErase、restoreUnwrittenBytes 任一为真就报错,原文是「Please do not use any of the flashing_enabled, reset_after_flashing, halt_after_reset, full_chip_erase, or restore_unwritten_bytesoptions when usingattachrequest type.」:提示里用的是 Rust 字段名而不是 JSON 字段名,还列了一个已经不存在的reset_after_flashing`。
核级字段:CoreConfig
| JSON 字段 | 默认 | 说明 |
|---|---|---|
coreIndex | 0 | 调哪个核 |
programBinary | 可省 | ELF,调试信息和(开了烧录时)烧录都用它 |
svdFile | 可省 | CMSIS-SVD,给出 Peripherals 作用域(SVD 外设视图) |
catchReset、catchHardfault | true | vector catch,和 probe-rs run 的默认一致 |
catchSvc、catchHlt | true | 只对 Armv7-A/R 有意义 |
rttEnabled | false | 见下一节 |
rttChannelFormats | 空 | 按通道号指定格式 |
coreConfigs 是数组,但 SessionData::new 要求它恰好一项,否则报「probe-rs-debugger requires that one, and only one, core be configured for debugging.」。RP2040 这类双核芯片一次只能调一个核,这和 gdb server 把同类型的核当成线程的做法不同(gdb server)。
RTT:默认不开
rttEnabled 默认是 false。这和命令行相反:probe-rs run 只要 ELF 里有 _SEGGER_RTT 就自动轮询,而 DAP server 每轮 poll_cores 先看 rtt_config.enabled,为假就根本不碰 RTT。开了之后,等 configurationDone 之后才尝试 attach;读到的数据作为自定义事件交给扩展显示,扩展用 rttWindowOpened 请求告诉服务端哪个通道的窗口已经打开(命令表)。
RTT 的字段来自命令行和 DAP 共用的 RttConfig,用 #[serde(flatten)] 平铺进 CoreConfig,所以写在 coreConfigs 的每一项里,而不是单独一个对象。rttChannelFormats 的每一项是一个 RttChannelConfig:channelNumber、dataFormat(String、BinaryLE、Defmt)、mode(NoBlockSkip、NoBlockTrim、BlockIfFull)、showTimestamps(默认 true)、showLocation、logFormat。
rttChannelFormats 为空时,dap-server 按通道名自动选格式(core_data.rs:229):名叫 defmt 的用 Defmt,其他用 String,和命令行的规则一样(defmt 与 semihosting)。所以 defmt-rtt 的固件只要 "rttEnabled": true,不用写通道格式;而一旦写了任意一项 rttChannelFormats,自动选择就整体关掉,没列出的通道退回默认配置的 String,defmt 帧会被当成文本打出来。
RttConfig 本身没有 rename_all,它的第三个字段 default_config 按 serde 的规则在 JSON 里就叫 default_config,和周围的驼峰字段不一致;这是按属性推断的,没有用 VS Code 实际验证。
和命令行的对照
probe-rs run | VS Code + dap-server | |
|---|---|---|
| 烧录 | 总是 | flashingEnabled 为真才烧 |
| 复位后 | 直接跑 | 先停住,configurationDone 后按 haltAfterReset |
| RTT | ELF 有 _SEGGER_RTT 就读 | rttEnabled 为真才读 |
| defmt 通道格式 | 按名字自动 | 没写 rttChannelFormats 时按名字自动 |
| HardFault | 打印调用栈后退出 | 停下,VS Code 显示调用栈和变量 |
| 多核 | --core 选一个 | coreConfigs 恰好一项 |
小结
launch.json除type、request、name外的字段都是 dap-server 里的 serde 结构体:SessionConfig套FlashingConfig和Vec<CoreConfig>,RTT 配置平铺在CoreConfig里。flashingEnabled默认关;launch总是先复位并停住,haltAfterReset只决定configurationDone之后放不放行。coreConfigs必须恰好一项;attach请求不能带烧录相关的选项,报错里用的是 Rust 字段名。rttEnabled默认关;不写rttChannelFormats时defmt通道自动用 defmt 解码,写了任何一项就不再自动选择。