Image of an arrow

Easy Debugging Zephyr with VSCode

Avatar

scuny

Author : Luc Beaufils, Embedded Systems Engineer

 

Debugging MCUs has always been the key to producing high-quality code, but reliance on vendor-specific IDEs and complex debug server configurations can hinder a smooth development workflow.

While VSCode has revolutionized the way developers write and debug code, setting up a debug session for MCUs can sometimes turn into a challenging task.

Thanks to the Zephyr west tool and some VSCode extensions, setting a debugging session has never been easier.

In this post, we will make the VSCode configuration to run west debugserver, which is in charge of setting a debug runner, and connect it to the VSCode IDE with the Cortex-Debug extension.

Prerequisites

For debugging Zephyr application through VSCode you’ll need to install the powerful Cortex-Debug extension. Also, the Zephyr SDK should be installed. Please follow the Getting Started Guide if you haven’t installed it.

Easy setup with West debugserver

The west debugserver command is very helpful to set up a minimal debug environment, as it run runners with the appropriate configuration for your board and can be used generically with any Zephyr supported board. The runners may be openocd, jlink, pyocd or others, depending on your board support.

With these two following steps, you will be able to run a debug session with VSCode:

  1. Create a task in your tasks.json file to run west debugserver.
  2. Create a launch.json file to configure Cortex-Debug.

For this example, we will use the STM32L562E_DK board as it supports west debugserver out of the box.

West debugserver task

The default runner of this board is pyocd, but an external package needs to be installed to use it. Due to this, we will set openocd as the runner, which is already installed with the Zephyr SDK. First, you have to create a VSCode task to run west debugserver in the background. In your tasks.json file, add the following task:

json
{
"label": "West Debugserver",
"type": "shell",
"command": "west",
"args": [
"debugserver",
"-r",
"openocd"
],
"problemMatcher": {
"pattern": {
"regexp":
"^(Info |Warn |Error):(.*)$",
"severity": 1,
"message": 2
},
"background": {
"activeOnStart": true,
"beginsPattern": "^Open On-Chip Debugger.*",
"endsPattern": ".*watchpoints.*",
},
},
"isBackground": true,
"runOptions": {
"instanceLimit": 1,
"instancePolicy": "terminateOldest"
},
"presentation": {
"reveal": "silent",
"close": true,
},
},

Some explanations about the configuration:

  • args : The -r option specifies the runner to use, in this case, openocd.
  • problemMatcher : This is used to parse the output of the west debugserver command and display it in the Problems panel. In our case, it matches the openocd output. If another runner is used, you may need to adjust the regex patterns.
  •  runOptions: As west debugserver opens a server, we don’t want to have port conflicts, so only one instance of the task can run at a time.
  • presentation: This is used to control the output of the task in the terminal. In this case, we want to reveal the output silently and close it after the task is finished.

Now the task is ready to use. You can try to run it by pressing Ctrl+Shift+P, selecting the Tasks: Run Task command and selecting the West Debugserver or whatever label you have given to the task.

Another small task is needed to stop the west debugserver task when you are done debugging. Otherwise, it will keep running in the background. You can add the following task to your tasks.json file:

json
{
"label": "Stop Debugserver",
"type": "process",
"command": "${command:workbench.action.tasks.terminate}",
},

 

Here, the command will open an input box to select the task to terminate.

And that’s it for the tasks.json file!

Cortex-Debug configuration

The Cortex-Debug will now make the link between the debugserver and the VSCode debugging interface. To do this, you need to create a launch.json file in the .vscode directory of your project.

Add this configuration to your launch.json file:

json
{
"version": "0.2.0",
"configurations": [
{
"name": "Zephyr Debugging",
"executable": "${workspaceFolder}/build/zephyr/zephyr.elf",
"request": "launch",
"type": "cortex-debug",
"runToEntryPoint": "main",
"servertype": "external",
"gdbPath": "/opt/sdk-0.17.0/arm-zephyr-eabi/bin/arm-zephyr-eabi-gdb",
"gdbTarget": "localhost:3333",
"device": "STM32L562E_DK",
"preLaunchTask": "West Debugserver",
"postDebugTask": "Stop Debugserver"
}
]
}

 

Some explanations about the configuration:

  • executable: This is the path to the ELF file generated by the Zephyr build system. By default, the build directory is build/zephyr/zephyr.elf, but you can change it if you have a different build directory.
  •  runToEntryPoint: This option specifies the entry point to run to when starting the debug session. In this case, it is set to main.
  • gdbPath: The gdb path is set to the Zephyr SDK’s GDB executable.
  • preLaunchTask and postDebugTask: these options specify the tasks we created just before. The name should be the same as task’s label.

The VSCode debugging session is now ready to be launched. You can add some breakpoints to your code and try running the debug session by pressing F5 or selecting the session by pressing Ctrl+Shift+D and clicking on the green play button.

Special case: Attach debugger to running target

Sometimes, target cannot support a reset when starting a debugging session.

We have dealt with this behavior when supporting the Cortex-M33 of the STM32MP2 board.

Thus, the VSCode configuration has been slightly changed to attach the debugger to the running target instead of reset and run the code. In the tasks.json file, the West Debugserver task is updated as follows:

json
{
"label": "West Debugserver",
"type": "shell",
"command": "west",
"args": [
"debugserver",
"-r",
"openocd",
"--no-init",
"--cmd-reset-halt",
"''",
],
"problemMatcher": {
"pattern": {
"regexp": "^(Info |Warn |Error):(.*)$",
"severity": 1,
"message": 2
},
"background": {
"activeOnStart": true,
"beginsPattern": "^Open On-Chip Debugger.*",
"endsPattern": ".*watchpoints.*",
},
},
"isBackground": true,"runOptions": {
"instanceLimit": 1,
"instancePolicy": "terminateOldest"
},
"presentation": {
"reveal": "silent",
"close": true,
}
},

Only the args parameter is updated to ask the west debugserver to not reset the target when launching the task. Accordingly, the launch.json file is also modified to permit VSCode to attach the debugger:

json
{
"name": "Attach Zephyr Debugging",
"executable": "${workspaceFolder}/build/zephyr/zephyr.elf",
"request": "attach",
"type": "cortex-debug",
"runToEntryPoint": "main",
"servertype": "external",
"gdbPath": "/opt/sdk-0.17.0/arm-zephyr-eabi/bin/arm-zephyr-eabi-gdb",
"gdbTarget": "localhost:3334",
"device": "STM32MP2",
"preLaunchTask": "West Debugserver",
"postDebugTask": "Stop Debugserver",
},

 

The request parameter is updated to attach the debugger.

Also the gdb port is set to 3334, because the stm32mp2 SoC has multiple architecture, so the debugserver set by default the Cortex-M33 to the 3334.

Conclusion

As you can see, setting up a debug environment for Zephyr development is trivial, thanks to the tools provided by the RTOS and the full-featured VSCode extension Cortex-Debug.

Feel free to use the configuration for running or attaching a debug session!

Find the config on GitHub : Main branch 

 

 

Leave a comment

Your email address will not be published. Required fields are marked *


Similar articles

Image of an arrow

  Empowering the Embedded Industry to Tackle the EU Cyber Resilience Act and Cyber regulations with an Innovative & Open Source Vulnerability Management Tool.   Nuremberg, March 11, 2025 – Savoir-faire Linux, a leading open-source software engineering and consulting firm, is proud to announce the official release of VulnScout.io, an open source cybersecurity solution designed […]

Savoir-faire Linux is excited to announce DHTNet, a powerful new C++17 library spun off from the Jami project that simplifies peer-to-peer connectivity. DHTNet enables developers to establish secure P2P connections between devices using only public keys, eliminating the need for centralized infrastructure or direct IP addressing. Features Connection Management: DHTNet simplifies the establishment and management […]

What’s new? We’re happy to announce the release of v2.8.0, which includes a few new features as well as bug fixes. Check out the summarized changelog below: Renamed command “Pick configuration” to “Change active buildConfiguration” Automatically re-scan when changing buildConfiguration Cache per-buildConfiguration scan results Make the recipes view appear as “loading” while a scan is […]

What’s new? We’re happy to announce the release of v2.7.0, which includes a few new features as well as bug fixes. Check out the summarized changelog below: Add bitbake environment scan for global variables Add skipped recipes to the tree view with skip reason Add support for latest Yocto devtool status output Add sanity check […]

What’s new? We’re happy to announce the release of v2.6.0, which includes which a few new features, improvements to user experience, and minor bug fixes. Check out the summarized changelog below: Handle completion on bash simple variable expansion Handle keywords import, require and inherit in Bash/Python context Add SPDX license suggestions Show license description on […]