Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Remote debug of Rust program in Visual Studio Code

I am developing a Rust program to run on an embedded Linux board that is based on a 64-bit ARM processor. The embedded Linux for the target is built using Yocto (dunfell). My Rust development environment is Visual Studio Code running on Linux (Ubuntu). How can I set it up to do remote debugging of the program on the target board?

I expect the sequence would be something like:

  • Compile the Rust program for the target (for debug).
  • Copy the debug bin file to the target.
  • Run it on the target, using gdbserver or lldb-server.
  • VS Code connects to the remote gdb/lldb, and controls the program for the usual debug.
like image 731
Craig McQueen Avatar asked Sep 17 '25 02:09

Craig McQueen


1 Answers

Extensions

  • I'm using the rust-analyzer extension for code analysis. It is better than the Rust extension.
  • CodeLLDB extension to enable debugging of a Rust program, using lldb.

Cross-compiling

The target board has a 64-bit ARM processor, so I want to use Rust to cross-compile for target aarch64-unknown-linux-gnu.

I'm using Ubuntu 18.04 LTS (it is old, but I have to use it to support an old project built with an old Yocto version). I used apt to install Ubuntu package gcc-8-aarch64-linux-gnu. On a more recent Ubuntu perhaps I could install a more modern package such as gcc-11-aarch64-linux-gnu.

In my ~/.cargo/config:

[target.aarch64-unknown-linux-gnu]
linker = "aarch64-linux-gnu-gcc-8"

Then, I can manually use cargo to cross-compile using the command:

cargo build --target=aarch64-unknown-linux-gnu

Then to cross-compile within VS Code — In my VS Code workspace for myapp, in .vscode/tasks.json:

{
    ...
    "tasks": [
        ...
        {
            "label": "rust: cargo build ARM"
            "type": "cargo",
            "command": "build",
            "args": ["--target=aarch64-unknown-linux-gnu"],
            "problemMatcher": [
                "$rustc"
            ],
            "group": "build",
        },
    ]
}

Then I just do Shift-Ctrl-B, and select the "rust: cargo build ARM" build task.

Remote debugging

The goal is to run myapp on targetboard.local as low-privileges user targetuser (rather than root).

In VS Code, the CodeLLDB extension uses lldb to debug Rust programs. But, lldb is a large compilation in Yocto for the target hardware. But, it works fine to connect to gdbserver running on the target board. The embedded Linux image running on the target board just needs to have gdbserver on it.

The target board also needs to have an SSH server running on it, with support for either rsync or plain SSH copy. rsync is good because it can be faster to do the copy of the executable to the target.

In my VS Code workspace for myapp, in .vscode/launch.json:

{
    ...
    "configurations": [
        ...
        {
            "type": "lldb",
            "request": "custom",
            "name": "Remote executable 'myapp'",
            "preLaunchTask": "rust: remote ARM debug setup",
            "targetCreateCommands": ["target create ${workspaceFolder}/target/aarch64-unknown-linux-gnu/debug/myapp"],
            "processCreateCommands": ["gdb-remote targetboard.local:17777"]
        }
    ]
}

In my VS Code workspace for myapp, in .vscode/tasks.json:

{
    ...
    "tasks": [
        ...
        {
            "label": "rust: remote ARM debug setup",
            "type": "shell",
            "command": "${workspaceFolder}/scripts/remote_debug.sh",
            "args": [ "${workspaceFolder}", "targetboard.local", "17777" ],
            "group": "none",
            "dependsOn": [
                "rust: cargo build ARM",
            ],
        },
    ]
}

In my VS Code workspace for myapp, I created the script scripts/remote_debug.sh:

#!/bin/bash

VSCODE_WS="$1"
SSH_REMOTE="$2"
GDBPORT="$3"

APP="myapp"
TARGET_ARCH="aarch64-unknown-linux-gnu"
BUILD_BIN_FILE="${VSCODE_WS}/target/${TARGET_ARCH}/debug/${APP}"
TARGET_USER="targetuser"
TARGET_BIN_FILE="/home/targetuser/${APP}"
TARGET_CWD="/var/lib/myapp"

ssh "${TARGET_USER}@${SSH_REMOTE}" "killall gdbserver ${APP}"

if ! rsync -avz "${BUILD_BIN_FILE}" "${TARGET_USER}@${SSH_REMOTE}:${TARGET_BIN_FILE}"; then
    # If rsync doesn't work, it may not be available on target. Fallback to trying SSH copy.
    if ! scp "${BUILD_BIN_FILE}" "${TARGET_USER}@${SSH_REMOTE}:${TARGET_BIN_FILE}"; then
        exit 2
    fi
fi

ssh -f "${TARGET_USER}@${SSH_REMOTE}" "sh -c 'cd ${TARGET_CWD}; nohup gdbserver *:${GDBPORT} ${TARGET_BIN_FILE} > /dev/null 2>&1 &'"

The target CWD /var/lib/myapp needs to exist with the right user permissions for targetuser. I could just specify /home/targetuser instead.

With that set up, I can just press F5 in VS Code, and it will compile, copy the file to the target board, and start remote-debugging it through gdbserver.

like image 63
Craig McQueen Avatar answered Sep 19 '25 15:09

Craig McQueen