Thank you for your interest in contributing to rsgdb! This document provides guidelines and instructions for contributing to the project.
We are committed to providing a welcoming and inclusive environment. Please be respectful and constructive in all interactions.
- Rust 1.70 or later
- Git
- A debug probe (optional, for hardware testing)
- Familiarity with GDB and embedded debugging concepts
- Fork the repository on GitHub
- Clone your fork:
git clone git@github.com:YOUR_USERNAME/rsgdb.git cd rsgdb - Add the upstream repository:
git remote add upstream git@github.com:DynamicDevices/rsgdb.git
- Build the project:
cargo build
- Run tests:
cargo test
From the repo root, run the same checks CI uses (fmt, tests with --all-features, clippy with -D warnings, docs with warnings denied):
./scripts/validate_local.shcargo test includes tests/native_spawn_integration.rs, which needs python3 or python on PATH (CI runners provide this). On Windows, use Git Bash or WSL so the script runs, or run the cargo commands from that script by hand.
./scripts/deps_check.shThis runs cargo tree -d (duplicate transitive versions — often benign, e.g. thiserror v1 via svd-parser and v2 via tracing-appender), cargo audit against RustSec (install: cargo install cargo-audit), and cargo outdated --workspace if cargo-outdated is installed (cargo install cargo-outdated). Major upgrades (e.g. toml 0.8 → 1.x) need a deliberate PR, not blind cargo update.
Version bumps, changelog entries, and git tags are summarized in RELEASING.md; user-facing notes accumulate in CHANGELOG.md.
Codec framing + proxy TCP integration tests only (~1s):
./scripts/e2e_rsp_regression.shUse this when iterating on src/protocol/codec.rs or tests/proxy_integration.rs without running the full cargo test suite.
- VS Code / Cursor: repo-root
.vscode/launch.jsonand.vscode/tasks.json— preLaunch runsexamples/board_test_app/prepare_cursor_debug.sh, thencppdbgattachesgdb-multiarchto127.0.0.1:<listen port>(seeexamples/board_test_app/README.md§ Visual debug). Requires the C/C++ extension (ms-vscode.cpptools). - Copy or source
scripts/gdbinit.rsgdb.examplewhen connecting GDB through rsgdb (adjusttarget extended-remoteto your listen port). - Backend thread-related RSP replies (
m…/l/QC…/ hex thread names) get short summaries atrsgdb::rtos(debug); packets are unchanged on the wire.
End-to-end smoke: gdbserver → rsgdb → GDB (batch), same shape as CI job E2E GDB smoke.
Requires gcc, gdb, and gdbserver. On Debian/Ubuntu, gdbserver is often a separate package: sudo apt-get install -y gcc gdb gdbserver.
cargo build --release
./scripts/e2e_gdb_smoke.shTo run the same check as part of local validation (after installing the tools above):
RUN_E2E_GDB=1 ./scripts/validate_local.shTo debug a real Zephyr app (still RSP/gdbserver) without QEMU or hardware, build for the native_sim board: Zephyr links a normal Linux executable (zephyr.exe). Flow matches CI: gdbserver → rsgdb → GDB.
Requires a full Zephyr west workspace (ZEPHYR_WORKSPACE with .west/ and zephyr/). See native_sim. The script builds native_sim/native/64 by default (LP64 host binary); the plain native_sim target is 32-bit and needs multilib on x86_64.
The app under scripts/zephyr_multi_printf_app/ (in this repo) has three printf lines with markers RSGDB_E2E line 1 … 3. The E2E script sets a breakpoint on the first printf, runs next twice, and asserts RSGDB_E2E line 1 and line 2 appear in the gdbserver log (inferior stdout). Override the app path with ZEPHYR_APP_SOURCE_DIR if needed.
export ZEPHYR_WORKSPACE=/path/to/zephyrproject
cargo build --release
./scripts/e2e_zephyr_native_sim.shFirst build can take several minutes. GDB is host gdb (not arm-none-eabi-gdb) because the ELF matches your machine.
RUN_E2E_ZEPHYR_NATIVE=1 ./scripts/validate_local.sh- GDB smoke — the CI workflow includes an Ubuntu job that runs
scripts/e2e_gdb_smoke.shaftercargo build --release(same idea asRUN_E2E_GDB=1withvalidate_local.sh, without duplicating fmt/clippy/doc in that job). - Zephyr
native_sim— the Zephyr E2E workflow (.github/workflows/zephyr-e2e.yml) provisions a cached west workspace, then runsscripts/e2e_zephyr_native_sim.sh. It runs whenscripts/e2e_zephyr_native_sim.shorscripts/zephyr_multi_printf_app/change, on pushes tomain/developfor those paths, on a weekly schedule, and via workflow_dispatch (Actions → Zephyr E2E → Run workflow). This mirrorsRUN_E2E_ZEPHYR_NATIVE=1locally without checking a Zephyr tree into this repo. The workflow buildstarget/debug/rsgdb(not release), frees some preinstalled SDKs on the runner, and installspyelftoolsin the west venv (Zephyr’sgen_kobject_list.pyimportselftools).
Work is tracked in GitHub issues. Blocked-by dependencies define order (e.g. Part A #1 → #3; #2 can run in parallel). Close an issue from a PR with Closes #N when it is fully done.
Foundation (closed issues): Part A (#1–#3), session recording (#4), SVD baseline (#5), breakpoint/semihosting spike (#6), flash (#7), RTOS decode/log (#8). Phase A/B in-tree: RSP matrix + proxy tests, gdbinit example, rsgdb::rtos decode logs — see README Key Features.
Current capabilities (same as README “Current”): RSP codec + TCP proxy, managed native stub spawn (transport = native, [backend.spawn] with {port}), SSH remote gdbserver (transport = remote_ssh, [backend.remote_ssh]; optional upload_local/upload_remote → scp before ssh; local ssh/scp + optional RSGDB_SSH_PASSWORD/sshpass), tracing logging, TOML/env config, JSONL record + rsgdb replay, SVD register/field/enum-name memory annotations, rsgdb flash, RTOS packet summaries, CI + optional GDB/Zephyr E2E scripts.
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.
Roadmap — follow-ups: Deeper probe integration (beyond managed TCP spawn; CLI backend_type stays a label). Optional: SVD value decode + recording correlation (see #11 history); TUI, logging export, proxy-side breakpoint management — open an issue before large changes. #9 tracks native-spawn history; close with Closes #9 when you consider it fully done from the project’s side.
CI: Workflow CI + optional Zephyr E2E — green on main (see workflow files).
CI jobs (overview): Workflow CI: test (matrix), fmt, clippy, docs, e2e-gdb-smoke, coverage, build (artifacts; upload may use continue-on-error for transient infra). Workflow Zephyr E2E: west + SDK + scripts/e2e_zephyr_native_sim.sh (path / schedule / workflow_dispatch).
Design ADRs: docs/ADR-001-breakpoints-semihosting.md — breakpoint policy + semihosting (Phase 2 spike).
Use this when a probe and target are available.
Linux target over SSH (transport = remote_ssh) — setup first
- One-time: install your SSH public key on the target so
ssh/scpand 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; seeexamples/board_test_app/README.md). - 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.shfor an end-to-end smoke.
Option A — stub already running (transport = tcp, default)
- Start your backend (e.g. OpenOCD) and note its GDB TCP port (often
3333). - Start rsgdb so it listens for GDB and forwards to that port, e.g.
rsgdb --port 3334 --target-host 127.0.0.1 --target-port 3333 - From GDB:
target extended-remote 127.0.0.1:3334 - Confirm: break / continue / step /
info reg(orx/4xwon a valid address).
Option B — rsgdb spawns the stub (transport = native)
- Put your OpenOCD (or other) argv in
[backend.spawn] programwith{port}; settransport = "native". - Start rsgdb on your GDB listen port only (no separate stub step).
- Connect GDB to rsgdb as usual.
Success means the session behaves the same with rsgdb in the path as without (direct to OpenOCD), aside from added logging. Capture ports and commands in the issue if something fails.
feature/description- New featuresfix/description- Bug fixesdocs/description- Documentation updatesrefactor/description- Code refactoringtest/description- Test additions/improvements
We follow the Conventional Commits specification:
<type>(<scope>): <subject>
<body>
<footer>
Types:
feat: New featurefix: Bug fixdocs: Documentation changesstyle: Code style changes (formatting, etc.)refactor: Code refactoringtest: Adding or updating testschore: Maintenance tasks
Examples:
feat(protocol): add support for vCont packet parsing
Implements parsing for the vCont packet which allows for more
granular control over thread execution.
Closes #42
fix(breakpoints): correct hardware breakpoint limit check
The previous implementation didn't account for already-set breakpoints
when checking if a new hardware breakpoint could be added.
-
Create a feature branch from
main:git checkout -b feature/my-new-feature
-
Make your changes following the coding standards below
-
Write or update tests for your changes
-
Validate like CI (preferred):
./scripts/validate_local.sh
Or run
cargo test,cargo fmt,cargo clippy --all-targets --all-features -- -D warnings, andcargo docseparately if you cannot run the script. -
Commit your changes with clear, descriptive commit messages
-
Push to your fork:
git push origin feature/my-new-feature
-
Open a Pull Request on GitHub with:
- Clear title and description
- Reference to any related issues
- Screenshots/examples if applicable
- Test results
-
Address review feedback promptly and professionally
- Follow the Rust API Guidelines
- Use
cargo fmtfor consistent formatting - Address all
cargo clippywarnings - Write idiomatic Rust code
- Keep modules focused and cohesive
- Use clear, descriptive names
- Document public APIs with doc comments
- Include examples in documentation where helpful
- All public items must have doc comments
- Use
///for item documentation - Use
//!for module documentation - Include examples in doc comments:
/// Parses a GDB RSP packet from the given buffer. /// /// # Examples /// /// ``` /// use rsgdb::protocol::parse_packet; /// /// let packet = parse_packet(b"$qSupported#37"); /// assert!(packet.is_ok()); /// ``` pub fn parse_packet(buffer: &[u8]) -> Result<Packet> { // ... }
- Use
Resultfor fallible operations - Use
thiserrorfor custom error types - Provide context with
anyhowwhere appropriate - Don't panic in library code (use
Resultinstead)
- Write unit tests for individual functions
- Write integration tests for end-to-end scenarios
tests/native_spawn_integration.rsexercisestransport = nativeagainst a Python TCP listener; CI and./scripts/validate_local.shassume Python is onPATH- Use descriptive test names:
test_parse_packet_with_checksum - Test both success and error cases
- Aim for high code coverage
Example test structure:
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_parse_valid_packet() {
let result = parse_packet(b"$qSupported#37");
assert!(result.is_ok());
}
#[test]
fn test_parse_invalid_checksum() {
let result = parse_packet(b"$qSupported#00");
assert!(result.is_err());
}
}- Use the
tracingcrate for logging - Choose appropriate log levels:
error!: Errors that prevent operationwarn!: Unexpected but recoverable situationsinfo!: Important state changesdebug!: Detailed debugging informationtrace!: Very verbose debugging
Example:
use tracing::{debug, info, warn};
info!("Starting GDB proxy on port {}", port);
debug!("Received packet: {:?}", packet);
warn!("Hardware breakpoint limit reached, using software breakpoint");Understanding the project structure will help you navigate the codebase:
rsgdb/
├── src/ # Library + binary
├── tests/ # Integration tests (e.g. proxy RSP smoke)
├── scripts/
│ ├── validate_local.sh # Local CI parity (run before PRs)
│ └── deps_check.sh # Optional: tree -d, audit, outdated
├── rsgdb.toml.example
└── .github/workflows/ # CI
We welcome contributions in these areas (see open issues for current priorities):
- Richer SVD (fields, enum names in annotations) — #11 (value decode / recording correlation still optional follow-ups)
- TUI, advanced breakpoints, logging export — open an issue before large changes
- Documentation and test coverage (RSP matrix, proxy integration, SVD fixtures)
- Bug fixes and small ergonomics (gdbinit, logging targets)
- Questions: Open a GitHub Discussion
- Bugs: Open a GitHub Issue
- Feature Requests: Open a GitHub Issue with the
enhancementlabel
All submissions require review. We use GitHub pull requests for this purpose. The review process typically includes:
- Automated checks: CI must pass (tests, formatting, linting)
- Code review: At least one maintainer approval required
- Testing: Verify the changes work as expected
- Documentation: Ensure docs are updated if needed
By contributing to rsgdb, you agree that your contributions will be licensed under the same terms as the project (MIT OR Apache-2.0).
Contributors will be recognized in:
- The project README
- Release notes
- Git commit history
Thank you for contributing to rsgdb! 🎉