Marus / cortex-debug

Visual Studio Code extension for enhancing debug capabilities for Cortex-M Microcontrollers
MIT License
987 stars 238 forks source link

[RFC] Support west debug #969

Closed nguyenmthien closed 9 months ago

nguyenmthien commented 9 months ago

Is your feature request related to a problem? Please describe.

One of the best features of Zephyr RTOS is the possibility to switch between targets in development. An application can be written for many hardware architectures, and the toolchain (gcc, gdb, openocd, etc) can be switched on the fly using west and its subcommands (build, flash, debug, debugserver).

Natively supporting west would be nice for those who use Cortex-Debug in their Zephyr workflow.

At the moment, Cortex-Debug is hard-coding the path to GNU toolchain to each profiles, which make it impossible to change build targets without changing launch.json

Describe the solution you'd like

Each Zephyr build directory contains enough information to reconstruct the content of a Cortex-Debug launch.json profile. I suggest creating a new type of server. Such profile can look like:

    "configurations": [
        {
            "name": "Debug build/",
            "type": "cortex-debug",
            "request": "launch",
            "useWest": true,
            "buildDirectory": "${workspaceFolder}/build",
        }

Upon launching this profile, Cortex-Debug will look into the build directory, get the needed information, and lauch the correct toolchain.

Additional context

west stores in each build directory:

Side note

I would like to help on this issue. Any help on how to get started with this is apprecitated :smile:.

haneefdm commented 9 months ago

At the moment, Cortex-Debug is hard-coding the path to GNU toolchain to each profiles, which make it impossible to change build targets without changing launch.json

We don't hard code it. You did, by your choice. You can also use VSCode project settings.json (the preferred way), you can use env. vars, other extensions - including CMake.

I will welcome your contribution but first I want to understand the issue and gaps. If you are saying that we should pick up all of our settings from another file rather than what VSCode tells us (via settings and/or launch.json) then I am not sure we are headed in the right direction. Not ruling it out but there is a trend that says please look at this file or that file for my settings. I would ask them to modify their build process to update the right settings for VSCode so it cuts across all extensions.

ifyall commented 9 months ago

@nguyenmthien, this is a pretty active space for Zephyr, with different proposals and ongoing discussions. Fundamentally, the data that a Zephyr project uses for builds/debugs/etc. should be generated by Zephyr in a Visual Studio Code extension-agnostic way. This is because you want your intellisense, linters, CoPilots, debuggers and others all to have a set of common info about the Zephyr project, rather than each extension trying to draw that context from the Zephyr project in their own unique way.

I haven't seen any activity to actually introduce such an extension to the west tools for Zephyr, but their is an enhancement issue in that project that's been around for a while. See https://github.com/zephyrproject-rtos/zephyr/issues/21119

To get started on a Zephyr in VS Code solution now, you can follow @beriberikix 's work in this presentation from the 2023 Embedded Open Source Summit: https://blog.golioth.io/how-to-configure-vs-code-for-zephyr-development/.

haneefdm commented 9 months ago

Thanks @ifyall for the pointers I still maintain that your build process should do the right thing(s). I cannot realistically support every possible build system. And, not even have means of testing it. Way out of scope.

beriberikix commented 9 months ago

I've been thinking about this problem for some time and agree that Zephyr should be handling (not cortex-debug) and it should ideally be in an extension-agnostic way. I think the right approach is to create a West extension that generates the right files, like a settings or workspace.

Someone wrote a POC Python script called zephyr2vsc that's got the basic idea but it needs further development and would need to be converted into a West extension.

nguyenmthien commented 9 months ago

thank you all for your pointers and inputs :) I will now look into adapting the mentioned Python script to Zephyr.

Since this issue is out-of-scope for cortex-debug, I will henceforth close this.

beriberikix commented 9 months ago

Would love to test/help in any way I can!

haneefdm commented 9 months ago

Thanks @beriberikix

JPHutchins commented 9 months ago

I've also played with this, and experimented with zephyr2vsc. We commit the launch.json (don't really use tasks.json), and it might look like this:

{
    "version": "0.2.0",
    "configurations": [
        {
            "name": "dev",
            "type": "cortex-debug",
            "request": "attach",
            "servertype":"jlink",
            "executable": "${workspaceRoot}/build/zephyr/zephyr.elf",
            "device": "nRF52840_xxAA",
            "svdFile": "${workspaceFolder}/.vscode/nrf52840.svd",
            "toolchainPrefix": "arm-zephyr-eabi",
            "armToolchainPath": "${env:ARM_TOOLCHAIN_PATH}",
            "serverpath": "${env:JLINK_GDB_SERVER}",
            "rtos": "${env:JLINK_ZEPHYR_PLUGIN}",
        },
        {
            "name": "mcuboot",
            "type": "cortex-debug",
            "request": "launch",
            "servertype":"jlink",
            "executable": "${workspaceRoot}/build/mcuboot/zephyr/zephyr.elf",
            "device": "nRF52840_xxAA",
            "svdFile": "${workspaceFolder}/.vscode/nrf52840.svd",
            "toolchainPrefix": "arm-zephyr-eabi",
            "runToEntryPoint": "main",
            "armToolchainPath": "${env:ARM_TOOLCHAIN_PATH}",
            "serverpath": "${env:JLINK_GDB_SERVER}",
            "rtos": "${env:JLINK_ZEPHYR_PLUGIN}",
        },
        {
            "name": "ztest",
            "type": "cortex-debug",
            "request": "launch",
            "servertype":"jlink",
            "executable": "${workspaceRoot}/tests/build/zephyr.elf",
            "device": "nRF52840_xxAA",
            "svdFile": "${workspaceFolder}/.vscode/nrf52840.svd",
            "toolchainPrefix": "arm-zephyr-eabi",
            "armToolchainPath": "${env:ARM_TOOLCHAIN_PATH}",
            "serverpath": "${env:JLINK_GDB_SERVER}",
            "rtos": "${env:JLINK_ZEPHYR_PLUGIN}",
        },
    ],
}

Bonus content: we can debug on-target tests from VSCode 😮

As you can see, environment variables are used so that this file can be shared and tracked by git.

There's a huge catch, which is that you must set the environment variables before opening VSCode, otherwise launch.json cannot access them. I've been involved in discussions with MS, and they're sympathetic, but the tldr seems to be that once VCPKG found a nice workaround, any institutional pressure to improve things evaporated.

FWIW, I maintain a MacOS/Windows/Linux 0-dependency script that manages environment variables, aliases, etc - simplifies things quite a bit in complex Zephyr environments. Handles setting ZEPHYR_BASE, using venv, and of course the interface is the same for all OS, cause that's the whole point of envr. https://github.com/JPhutchins/envr

A typical Zephyr project config might look like:

[PROJECT_OPTIONS]
PROJECT_NAME=my_cool_product
PYTHON_VENV=.venv

[VARIABLES]
PROJECT_ZEPHYR_VERSION=3.4.99

_NCS=$HOME/ncs/v2.5.0
ZEPHYR_BASE=$_NCS/zephyr

# Toolchain and Debugging
ZEPHYR_SDK_INSTALL_DIR=$HOME/ncs/toolchains/v2.5.0/opt/zephyr-sdk/
ARM_TOOLCHAIN_PATH=$HOME/zephyr-sdk-0.16.3/arm-zephyr-eabi/bin
ARM_GDB_PATH=$ARM_TOOLCHAIN_PATH/arm-zephyr-eabi-gdb

# JLink Linux names, overwrite in envr-local for Windows (e.g. .exe, .dll)
JLINK_PATH=/opt/SEGGER/JLink
JLINK_GDB_SERVER=$JLINK_PATH/JLinkGDBServerCLExe
JLINK_ZEPHYR_PLUGIN=$JLINK_PATH/GDBServer/RTOSPlugin_Zephyr.so

[ALIASES]
twister=python3 $ZEPHYR_BASE/scripts/twister
runtest=twister --clobber-output --jobs 1 --board-root boards --platform my_cool_product --device-testing

# build a single on-target unit test
#   Usage:
#       buildtest <PATH_TO_TEST>
buildtest=west build -p auto -b my_cool_product --build-dir tests/build

# flash the last test that was built at tests/build
flashtest=west flash --hex-file tests/build/zephyr/zephyr.hex --skip-rebuild

# run the default twister config - QEMU tests plus build-only for on-target platforms
emutest=twister --clobber-output --jobs 1 --board-root boards

It's critical to us that VSCode is a TOOL and never a DEPENDENCY - west debug is working pretty darn well I think!

JPHutchins commented 9 months ago

@nguyenmthien As to your initial ask; what I've taken to doing in Zephyr and non-Zephyr CMake systems that have multiple targets is add a target that always copies the "most recent ELF" to the same place, typically build/latest.elf. It's not perfect, but usually the target you want to debug is that last one you built! Of course, this doesn't handle SVD and some other important options... You can look into some hack extensions that allow use of variables in launch.json.

Truly, it reminds me that MS needs to be pressured to allow use of environment variables - here's the issue thread https://github.com/microsoft/vscode/issues/152806

nguyenmthien commented 9 months ago

@JPHutchins thanks a lot for your information, I am now working on a west extension command to create a launch.json file or the likes :)

Because this issue is out-of-scope for cortex-debug, I have moved this discussion to the official Zephyr discord server, please chime in if you have any additional inputs :smiley:

haneefdm commented 9 months ago

If I may, I have a suggestion. I am not sure generating launch.json is a good idea. Because this file is generally edited heavily - ofcourse you can use a template. Instead, your extension can create a set of commands that can be run from launch.json. For instance, your extension can export a command like myext.jlinkpath

Then in launch.json, you can do "serverpath": "${command:myext.jlinkpath}"

See: https://code.visualstudio.com/docs/editor/variables-reference#_command-variables

Now, in your extension myext.jlinkpath, you can use whatever logic you want to return a value back. This disadvantage is since you cannot pass arguments (not sure why) to the command, there have to be a predefined set of commands. I don't think you can generate a set of commands dynamically.

CMake makes extensive use of this mechanism

https://github.com/microsoft/vscode-cmake-tools/blob/HEAD/docs/cmake-settings.md#command-substitution

My two cents.