You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
feat(remote): SSH gdbserver transport, board example, and design principles
Add remote_ssh backend with optional scp upload before ssh gdbserver; wire
config and proxy connect path. Include board_test_app example (Makefile,
rsgdb.remote.toml) plus install_ssh_key and debug_remote helper scripts with
SSH key preflight.
Document design principles (ease, automation, reliability), Linux target SSH
setup, and CONTRIBUTING cross-links. Update rsgdb.toml.example and gitignore
for the example binary.
Made-with: Cursor
**Project aim — zero-touch remote debugging:** move toward **one configured flow** (target address, SSH user, credentials via key or env) that automates **upload → gdbserver → proxy** without ad-hoc copy steps; see README **Design principles**, **Project Goals**, and roadmap row **Zero-touch remote debug**.
121
123
122
124
**Roadmap — follow-ups:** Deeper probe integration (beyond managed TCP spawn; CLI `backend_type` stays a label). **Optional:** SVD value decode + recording correlation (see [#11](https://github.com/DynamicDevices/rsgdb/issues/11) history); TUI, logging export, proxy-side breakpoint management — open an issue before large changes. [#9](https://github.com/DynamicDevices/rsgdb/issues/9) tracks native-spawn history; close with `Closes #9` when you consider it fully done from the project’s side.
123
125
@@ -131,6 +133,11 @@ Work is tracked in [GitHub issues](https://github.com/DynamicDevices/rsgdb/issue
131
133
132
134
Use this when a probe and target are available.
133
135
136
+
**Linux target over SSH (`transport = remote_ssh`) — setup first**
137
+
138
+
1.**One-time:** install your SSH public key on the target so `ssh`/`scp` and rsgdb do not depend on interactive passwords. From the repo root: `./examples/board_test_app/install_ssh_key.sh` (override host/user/port as needed; see `examples/board_test_app/README.md`).
139
+
2. Point your config at `[backend.remote_ssh]` + `[proxy]` and connect GDB through rsgdb as in the main README. Optional: `examples/board_test_app/debug_remote.sh` for an end-to-end smoke.
140
+
134
141
**Option A — stub already running (`transport = tcp`, default)**
135
142
1. Start your **backend** (e.g. OpenOCD) and note its **GDB TCP port** (often `3333`).
136
143
2. Start **rsgdb** so it listens for GDB and forwards to that port, e.g.
Copy file name to clipboardExpand all lines: README.md
+33-4Lines changed: 33 additions & 4 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -7,6 +7,18 @@
7
7
8
8
A Rust GDB **RSP proxy** with structured logging, optional CMSIS-SVD memory labels, JSONL session recording/replay, RTOS packet decode logs, and external flash orchestration — with room to grow into richer breakpoints and UIs.
9
9
10
+
## Design principles
11
+
12
+
rsgdb exists to make **remote debugging practical for embedded developers**: boards on the bench, in the lab, or over the network, without unnecessary ceremony. **Automation** and **reliability** are first-class goals, not afterthoughts.
13
+
14
+
| Principle | What it means in practice |
15
+
|-----------|---------------------------|
16
+
|**Ease**| Few steps from “target is reachable” to GDB inspecting your ELF; sensible defaults; documented flows (config + helper scripts) that match how teams actually work. |
17
+
|**Automation**| One place to describe the target (address, transport, optional binary upload); **`remote_ssh`** with optional **`scp`** so you are not hand-copying builds; preflight checks (e.g. SSH key access) before a long attach fails halfway. |
18
+
|**Reliability**| Fail **early** with **actionable** errors (auth, ports, firewall, stub not listening); predictable behavior and exit codes; logging you can use in the field; CI and local E2E smoke where we can prove the path. |
19
+
20
+
These principles align with **zero-touch remote debugging** below and inform roadmap work (fewer manual steps, clearer credential UX, stronger health checks over time).
21
+
10
22
## 🎯 Project Goals
11
23
12
24
**rsgdb** aims to bridge the gap between traditional GDB debugging and modern embedded development needs by providing:
@@ -15,7 +27,8 @@ A Rust GDB **RSP proxy** with structured logging, optional CMSIS-SVD memory labe
15
27
-**Advanced Breakpoint Management** (roadmap): Named breakpoints, conditional expressions, and hardware/software optimization — config and parsing exist; the proxy today **forwards** breakpoint RSP unchanged
16
28
-**State Inspection** (partial today): Peripheral/register **labels** for memory traffic via CMSIS-SVD in logs; snapshots / deep state are not implemented yet
17
29
-**Session Management**: JSONL **recording** and **`rsgdb replay`** mock-backend playback (see below)
18
-
-**Backend Flexibility** (today): **`transport = tcp`** connects to an existing stub on **`proxy.target_host`:`proxy.target_port`**. **`transport = native`** spawns a configured command (`[backend.spawn] program` with `{port}`), waits for TCP on `bind_host`, then uses the same RSP path; the child is killed when the GDB session ends ([#9](https://github.com/DynamicDevices/rsgdb/issues/9)). `[backend].backend_type` / `--backend` is a **label** for logs only.
30
+
-**Backend Flexibility** (today): **`transport = tcp`** connects to an existing stub on **`proxy.target_host`:`proxy.target_port`**. **`transport = native`** spawns a configured command (`[backend.spawn] program` with `{port}`), waits for TCP on `bind_host`, then uses the same RSP path; the child is killed when the GDB session ends ([#9](https://github.com/DynamicDevices/rsgdb/issues/9)). **`transport = remote_ssh`** runs **`gdbserver` on the target** over SSH; optional **`upload_local` / `upload_remote`** runs **`scp`** first so you are not forced to copy binaries by hand. `[backend].backend_type` / `--backend` is a **label** for logs only.
31
+
-**Zero-touch remote debugging** (direction / aim): Reduce manual steps for **Linux targets** (e.g. Yocto boards): one config with **target address**, **login** (SSH user), and **credential** (SSH key or `RSGDB_SSH_PASSWORD` + `sshpass`) should drive **upload → gdbserver → proxy → GDB** where possible; more automation and polish are expected over time — see **Design principles** above and roadmap below.
19
32
-**Modern Architecture**: Built with Rust for safety, performance, and reliability
20
33
21
34
## ✨ Key Features
@@ -29,7 +42,8 @@ A Rust GDB **RSP proxy** with structured logging, optional CMSIS-SVD memory labe
29
42
- ▶️ **`rsgdb replay`** — load a recording and serve a **mock TCP backend** for one client (order-preserving playback / tests)
30
43
- 📝 **SVD annotation (read-only)** — CMSIS-SVD → log labels for memory RSP (`m` / `M`): `Peripheral.REGISTER`, overlapping **fields**, and enumerated **variant names** where present (`target: rsgdb::svd`, debug)
31
44
- ⚡ **`rsgdb flash`** — run a configured external flash tool (`[flash].program` with `{image}` substitution; OpenOCD/probe-rs/etc.)
32
-
- 🔌 **`transport = native`** — spawn a GDB stub per session (`[backend.spawn] program` with `{port}`); TCP to loopback; teardown on disconnect
45
+
- 🔌 **`transport = native`** — spawn a GDB stub **on this machine** per session (`[backend.spawn]` + `{port}`); teardown on disconnect
46
+
- 🖧 **`transport = remote_ssh`** — run **`gdbserver` on the target** via **`ssh user@host …`** (`[backend.remote_ssh]` + `{port}`); optional **`upload_local`/`upload_remote`** → **`scp`** first; TCP to `proxy.target_host`:`proxy.target_port`; optional `RSGDB_SSH_PASSWORD` + `sshpass`
33
47
- 🧵 **RTOS RSP decode / log (Zephyr-first)** — thread-extension packets are decoded and logged at `target: rsgdb::rtos` (debug). Thread *data* comes from your stub (e.g. OpenOCD **Zephyr** RTOS awareness); other RTOSes use the same GDB RSP when the stub implements them (see below).
34
48
- 🧪 **CI + local E2E smoke** — `gdbserver` → `rsgdb` → `gdb` (batch), `scripts/e2e_gdb_smoke.sh` (Ubuntu job in **CI** workflow). **Zephyr `native_sim`** E2E (`scripts/e2e_zephyr_native_sim.sh`) runs in the **Zephyr E2E** workflow when those scripts/app change, on `main`/`develop`, weekly, or manually. See [CONTRIBUTING.md](CONTRIBUTING.md).
|**`transport = tcp`** (default) | The stub is **already running** (you started OpenOCD, probe-rs gdb, gdbserver, …). rsgdb **dials**`proxy.target_host`:`proxy.target_port`. |
94
-
|**`transport = native`**| You want rsgdb to **spawn** the stub per GDB session with `[backend.spawn] program` and `{port}`, then connect to `bind_host` at that port. Ignores `target_host` / `target_port` for the backend. Kills the stub when GDB disconnects. |
108
+
|**`transport = native`**| You want rsgdb to **spawn** the stub **on this machine** per GDB session with `[backend.spawn] program` and `{port}`, then connect to `bind_host` at that port. Kills the stub when GDB disconnects. |
109
+
|**`transport = remote_ssh`**| The stub runs **on a remote host** (e.g. Yocto board). Optional **`upload_local`** + **`upload_remote`** run **`scp`** first (same auth as SSH). Then rsgdb runs **`ssh user@host …`** with `[backend.remote_ssh] program` (must include `{port}` → `proxy.target_port`), then connects TCP to **`proxy.target_host`:`proxy.target_port`**. Kills the **local**`ssh` process when GDB disconnects (typically ends remote `gdbserver`). Requires **OpenSSH**`ssh`/`scp` on PATH; optional **`RSGDB_SSH_PASSWORD`** + **`sshpass`** for non-interactive password auth. |
110
+
111
+
#### Setting up a Linux target for `remote_ssh` debugging
112
+
113
+
Do this **once per host/user** so `ssh`, `scp`, and rsgdb agree on the same auth (no password prompts in normal use).
114
+
115
+
1.**Install your SSH public key on the target (recommended)** — from the repository root, run [`examples/board_test_app/install_ssh_key.sh`](examples/board_test_app/install_ssh_key.sh). Defaults match the example [`examples/board_test_app/rsgdb.remote.toml`](examples/board_test_app/rsgdb.remote.toml) (`fio` @ `192.168.2.139`). Override with `SSH_HOST`, `SSH_USER`, `SSH_PORT`, or positional `host` / `user`:
116
+
```bash
117
+
./examples/board_test_app/install_ssh_key.sh
118
+
```
119
+
If you must pass the account password non-interactively (e.g. first-time automation), set `RSGDB_SSH_PASSWORD` and install **`sshpass`**; the script uses the same variable as rsgdb.
120
+
2.**Or use password auth** — omit keys and rely on interactive prompts, or set **`RSGDB_SSH_PASSWORD`** + **`sshpass`** for rsgdb/`scp` (see table above).
121
+
3.**Verify** — `ssh -p <port> user@host` should succeed without a password after step 1. Then use your `[backend.remote_ssh]` + `[proxy]` config, or follow [`examples/board_test_app/README.md`](examples/board_test_app/README.md) for a full smoke (`debug_remote.sh`).
95
122
96
123
**Config:**`[proxy] listen_port`, `target_host`, `target_port`. **`timeout_secs`** applies only to **establishing** the TCP connection to the backend, not to idle GDB sessions (no read timeout on open connections).
97
124
@@ -307,11 +334,13 @@ Source of truth for ordering and scope: **[GitHub Issues](https://github.com/Dyn
- Enhanced logging export (JSON/CSV), advanced breakpoints, TUI, performance work — see **Planned** under Key Features and open an issue when starting.
343
+
- Deeper **zero-touch remote debugging** (beyond current `scp` + `remote_ssh`): e.g. integrated workflows, fewer external tools, clearer security story for secrets — track as project aim above.
# board_test_app — GDB smoke test on a Linux target
2
+
3
+
Tiny C program: infinite loop, `volatile` globals/locals you can watch in GDB (`print g_counter`, breakpoints on `printf`, etc.).
4
+
5
+
## First-time target setup (SSH)
6
+
7
+
Before debugging with **`transport = remote_ssh`** (so `scp` + `ssh` work without typing a password every time):
8
+
9
+
1.**Install your public key on the board** (one-time). From the **repository root**:
10
+
```bash
11
+
./examples/board_test_app/install_ssh_key.sh
12
+
```
13
+
Defaults match `rsgdb.remote.toml` in this directory (`fio` @ `192.168.2.139`). Use `SSH_HOST`, `SSH_USER`, `SSH_PORT`, or `./examples/board_test_app/install_ssh_key.sh <host> <user>` to match your board. For a non-interactive password on that first run: `export RSGDB_SSH_PASSWORD=…` (requires **`sshpass`**).
14
+
2. Confirm **`ssh`** to the target works without a password.
15
+
3. Continue with **Build** and **Debug** below. See the main README **Setting up a Linux target for `remote_ssh` debugging** for the full project-wide checklist.
16
+
17
+
## Build (aarch64 default)
18
+
19
+
The Makefile defaults to **`aarch64-linux-gnu-gcc`**:
20
+
21
+
```bash
22
+
cd examples/board_test_app
23
+
make
24
+
```
25
+
26
+
Host-only smoke build (x86_64): `make CC=gcc`
27
+
28
+
With a **Yocto/Poky SDK** (adjust path), use the SDK compiler:
**Manual:**`scp` the binary and `chmod +x` on the target.
39
+
40
+
**With rsgdb** (`transport = remote_ssh`): set `upload_local` to this built `board_test_app` path and `upload_remote` to e.g. `/tmp/board_test_app` — rsgdb can **`scp`** before starting `gdbserver` (see main README).
41
+
42
+
## Debug (gdbserver on board)
43
+
44
+
### Automated (rsgdb `remote_ssh` + `scp`)
45
+
46
+
From the **repository root**, with the board reachable at the address in `rsgdb.remote.toml` (default `192.168.2.139`):
47
+
48
+
```bash
49
+
cargo build --release
50
+
cd examples/board_test_app && make &&cd ../..
51
+
export RSGDB_SSH_PASSWORD=… # if using password auth; prefer SSH keys
52
+
./examples/board_test_app/debug_remote.sh
53
+
```
54
+
55
+
Use **`gdb-multiarch`** on Ubuntu (not always `aarch64-linux-gnu-gdb`). Pass an **absolute** path to `file` if you run GDB by hand:
0 commit comments