Rust in Everything
基于 probe-rs-tools 0.32.0

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
allowEraseAllfalse允许为解锁而整片擦除(nRF 的 CTRL-AP 擦除之类,见调试序列与 vendor)
consoleLogLevelConsole调试控制台里显示多少 probe-rs 自己的日志:Console、Info、Debug
cwd可省相对路径的基准
remoteServerModefalsedap-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

CoreConfig:

JSON 字段默认说明
coreIndex0调哪个核
programBinary可省ELF,调试信息和(开了烧录时)烧录都用它
svdFile可省CMSIS-SVD,给出 Peripherals 作用域(SVD 外设视图)
catchReset、catchHardfaulttruevector catch,和 probe-rs run 的默认一致
catchSvc、catchHlttrue只对 Armv7-A/R 有意义
rttEnabledfalse见下一节
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 runVS Code + dap-server
烧录总是flashingEnabled 为真才烧
复位后直接跑先停住,configurationDone 后按 haltAfterReset
RTTELF 有 _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 解码,写了任何一项就不再自动选择。

💬 关联讨论

发起讨论
加载中...