VSCode

The probe-rs-debugger VS Code extension uses the Microsoft Debug Adapter Protocol to implement an interactive debugging experience between VS Code and a probe-rs target.

The extension is currently in pre-production/Alpha stage, with limited functionality. For details of current status and functionality please read this section.

Installation

  • Install the probe-rs-debugger extension in VS Code, by downloading the latest available probe-rs-debugger-x.x.x.vsix from the Releases page in our repo (we will publish to the Microsoft Extension marketplace when we exit the 'Alpha' phase of this project)

    • Install the downloaded extension file from the command line, for example: code --install-extension probe-rs-debugger-0.3.3.vsix in the terminal
    • Alternatively,
  • Install the probe-rs-debugger server component, from the comand line with: cargo install --git https://github.com/probe-rs/probe-rs --force --branch master probe-rs-debugger

Usage and Configuration

Visual Tour

To use the probe-rs-debugger extension for VS Code, you will need to configure a launch.json entry for your target project.

We recommend that you familiarize yourself with the VSCode debug process, and specifically the section that discusses the differences between launch and attach request types.

The configuration choices may differ based on your use case, as demonstrated in the following examples:

  • Start a debug session, by launching a new process on your target device.
  • Start a debug session, by attaching to an existing process on your target device.
  • Start a debug session (either form above) against a remote debug server process running on a server on a specific TCP/IP address and port.
  • Using RTT to transfer data (e.g. logs, and println) between VSCode and your target application

Many of the entries in launch.json are optional, and the values for each are described in the hover tips. When values are restricted, the allowable values are available through VS Code intellisense.

A minimum configuration would look something like this (required customizations are indicated with //!MODIFY tags )

Start a debug session with minimum configuration

{
  "version": "0.2.0",
  "configurations": [
    {
      "preLaunchTask": "${defaultBuildTask}",
      "type": "probe-rs-debug",
      "request": "launch",
      "name": "probe_rs Executable Test",
      "chip": "STM32H745ZITx", //!MODIFY
      "coreConfigs": [
        {
          "programBinary": "Relative or fully qualified path to your programBinary" //!MODIFY
        }
      ]
    }
  ]
}

The following fully configured examples can be used (with customizations to reflect your own project and chip) to help you get started.

Using the launch request type

{
  "version": "0.2.0",
  "configurations": [
    {
      "type": "probe-rs-debug",
      "request": "launch",
      "name": "probe_rs Executable launch example",
      "cwd": "${workspaceFolder}",
      "connectUnderReset": true,
      "speed": 24000, //!MODIFY (or remove)
      "probe": "PID:VID:<Serial>", //!MODIFY (or remove)
      "runtimeExecutable": "probe-rs-debugger",
      "runtimeArgs": ["debug"],
      "chip": "STM32H745ZITx", //!MODIFY
      "flashingConfig": {
        "flashingEnabled": true,
        "resetAfterFlashing": true,
        "haltAfterReset": true
      },
      "coreConfigs": [
        {
          "coreIndex": 0,
          "programBinary": "Relative or fully qualified path to your programBinary", //!MODIFY
          "svdFile": "Relative or fully qualified path to your programBinary" //!MODIFY
        }
      ],
      "consoleLogLevel": "Info" //Error, Warn, Info, Debug, Trace
    }
  ]
}

Using the attach request type

{
  "version": "0.2.0",
  "configurations": [
    {
      "type": "probe-rs-debug",
      "request": "attach",
      "name": "probe_rs Executable launch example",
      "cwd": "${workspaceFolder}",
      "speed": 24000, //!MODIFY (or remove)
      "chip": "STM32H745ZITx", //!MODIFY
      "probe": "PID:VID:<Serial>", //!MODIFY (or remove)
      "coreConfigs": [
        {
          "coreIndex": 0,
          "programBinary": "Relative or fully qualified path to your programBinary", //!MODIFY
          "svdFile": "Relative or fully qualified path to your programBinary" //!MODIFY
        }
      ],
      "consoleLogLevel": "Info" //Error, Warn, Info, Debug, Trace
    }
  ]
}

Using to an existing probe-rs-debugger server

To start probe-rs-debugger as a standalone server:

probe-rs-debugger debug --port 50000 # Replace the value 50000 with any available TCP port number

Then use the following launch.json to connect to it:

{
  "version": "0.2.0",
  "configurations": [
    {
      "preLaunchTask": "${defaultBuildTask}", //Configure a default build task for 'cargo build'
      "type": "probe-rs-debug",
      "request": "launch",
      "name": "probe_rs Server attach example",
      "server": "127.0.0.1:50001", //!MODIFY ... can be a server that is remote from the VSCode session, but local to the probe
      "cwd": "${workspaceFolder}",
      "speed": 24000, //!MODIFY (or remove)
      "chip": "STM32H745ZITx", //!MODIFY
      "probe": "PID:VID:<Serial>", //!MODIFY (or remove)
      "coreConfigs": [
        {
          "coreIndex": 0,
          "programBinary": "Relative or fully qualified path to your programBinary", //!MODIFY
          "svdFile": "Relative or fully qualified path to your programBinary" //!MODIFY
        }
      ],
      "consoleLogLevel": "Info" //Error, Warn, Info, Debug, Trace
    }
  ]
}

Configuring RTT to transfer data

probe-rs-debugger and the VS Code extension supports using RTT in your target application.

For more information on how to configure your target application to use RTT, please refer to the instructions under the cargo-embed section of this guide.

In order to capture the RTT output in the VSCode extension, you will need to supply additional entries to your applications launch.json entry. You can use the following example and modify the channel information to match the rtt-target channel configurations in your application.

{
  "version": "0.2.0",
  "configurations": [
    {
      "type": "probe-rs-debug",
      "request": "launch",
      "name": "probe_rs rtt-target example",
      "coreConfigs": [
        {
          "coreIndex": 0,
          // ... <snip> ...
          "rttEnabled": true,
          "rttChannelFormats": [
            {
              "channelNumber": 0,
              "dataFormat": "String", // Format RTT data as String data
              "showTimestamps": true // Include host-side timestamps for every line of data transferred from the target RTT output
            },
            {
              "channelNumber": 1,
              "dataFormat": "BinaryLE" // Treat data as raw binary data, and do not format in any way
            }
          ]
        }
      ]
    }
  ]
}

In addition to supporting RTT channels with rtt-target, we also support using the defmt (a highly efficient logging framework that targets resource-constrained devices, and supports complex formatting and RUST_LOG-like logging.)

When using defmt, we can configure the client side based on what is captured in your application, and the launch.json only requires a single additional entry.

{
  "version": "0.2.0",
  "configurations": [
    {
      "type": "probe-rs-debug",
      "request": "launch",
      "name": "probe_rs rtt-target example",
      // ... <snip> ...
      "rttEnabled": true
      // "rttChannelFormats": [ //!OPTIONAL
      //     {
      //         "channelNumber": 0,
      //         "dataFormat": "Defmt",
      //     }
      // ]
    }
  ]
}

NOTE: Using RTT along with low power modes has some limitations. Frameworks such as embassy and rtic uses low power by default and this can cause RTT to not produce any output on some devices. In some cases, this can be overcome by configuring your device to enable the RCC DMA clock before entering low power.

NOTE: When using defmt RTT, please keep the following limitations in mind.

  • Due to the way the defmt crate works, it is currently not possible to mix defmt and rtt-target channels in a single application.
  • The defmt crate always uses channel number 0 for its output.
  • The defmt crate introduced a new log filter that filters out log statements at build time. To ensure your defmt logging is not default filtered to error level, you can either add the environment variable in your .cargo/config file, or add the options setting of your tasks.json (see below), to match your desired logging level on the target.

Adding DEFMT_LOG to cargo/config

[env]
DEFMT_LOG = "info"

Adding DEFMT_LOG to launch.json

// ... <snip> ...
  "options": {
    "env": {
      "DEFMT_LOG": "info" //!MODIFY: Remove or use any of the supported DEFMT_LOG options.
    }
  },
  "tasks": [
// ... <snip> ...

TIP: When using RTT, the data is streamed into a terminal window on a per channel basis. If your application uses multiple RTT channels, you may want to consider using the new VSCode Terminal tabs setting.

Current working functionality and known limitations

  • Launch: Start a debug session on the target by (optionally) flashing the target firmware.
  • Attach:
  • By default, VSCode will manage (start/stop) the probe-rs-debugger process to facilite a debug session against a target process. It is also possible for the user to manage the probe-rs-debugger as a standalone process, and then use TCIP/IP port to connect to from VSCode.
  • Connect to the target with probe-rs
    • Supports connect-under-reset
    • Tested against the following architectures:
      • Cortex-M7 using STM32H745
      • Cortex-M4 using nRF52833_xxAA
      • Cortex-M0 using Raspberry PICO
      • RISC-V using ESP32-C3 - WIP
  • Flash the chip with your own binary.
    • Supports reset-after-flashing, full-chip-erase, and restore-unwritten-bytes
    • Supports halt-after-reset. This will allow you to set breakpoints in your main() function.
  • Set, clear, disable, enable hardware Breakpoints.
    • In VSCode Source view, breakpoint locations will automatically be adjusted to code boundaries that lie safely between function prologues and epilogues.
    • In VSCode Disassembly view, breakpoints are set at 'instruction level'
  • UPDATED: Stepping through executing code during debug:
    • Supports stepping at 'statement' level with Step Over, Step Into, Step Out. Stepping desitnations will automatically be adjusted to code boundaries that lie safely between function prologues and epilogues.
    • While VSCode 'Disassembly view` is open, all stepping automatically happens at 'instruction' granularity, and will allow the user to step to any target location, including instructions in a function prologue or epilogue.
  • Variables View
    • View values of core Registers, and changes during code execution
    • View values of Locals and Statics variables, and update values during code execution.
      • Shows datatypes and values for the following Rust datatypes.
        • Base types, including &str
        • Enumerations
        • Structures
        • Pointers
        • Variants
        • Arrays
        • Unions
        • Options & Results
        • Unit types
      • TODO: Add support for additional types, such as Generics, etc.
      • NEW: Use the Set Variable command in VSCode to update variable values.
      • NEW: Use the View Binary Data command in VSCode to perform binary memory edits on the target device.
  • Call Stack View
    • Supports a single thread, for a single core of the chip, but will allow selection of any frames that are in the current thread
    • NEW Supports the VSCode Dissambly view, and SetInstructionBreakpoints
    • TODO: Support multiple threads
    • TODO: Support chips with multiple cores
  • NEW: Watch View Monitor values of selected variables.
  • RTT - Configure RTT Channels and capture their output in the VSCode Integrated Terminal
    • RTT Channels that support logging data to VSCode Terminal windows
  • NEW: Use the REPL command line in the Debug Console evaluate variables
  • NEW: Navigate and monitor SVD Peripheral registers

Building and Testing the debug extension in VS Code

Please refer to the repository README.md file for the latest instructions on how to build and deploy the extension.

A visual guide of implemented features

The Basics, and viewing RTT data

Use the Set Variable command to update variable values during debugging

Use the View Binary Data to view and perform binary edits of target memroy

Use the Disassembly to see the assembly code executed on the target, and set instruction breakpoints in this view

Use CMSIS-SVD definitions to navigate and monitor device peripheral registers