# Rust Debugging Guide

This guide covers Rust debugging support in the MCP Debugger using the CodeLLDB debug adapter.

## Prerequisites

### 1. Rust Toolchain
Install Rust from [rustup.rs](https://rustup.rs/):
```bash
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
```

### 2. CodeLLDB (Automatic)
The Rust adapter automatically downloads CodeLLDB when you build it:
```bash
cd packages/adapter-rust
npm run build:adapter  # Downloads and extracts CodeLLDB
```

## CodeLLDB Vendoring

The Rust adapter bundles the CodeLLDB binaries into `packages/adapter-rust/vendor/codelldb`. Vendoring runs automatically when you install or build the workspace. To keep the published npm package within registry limits, the pre-built CLI ships only the Linux x64 CodeLLDB runtime. If you are on macOS or Windows, set the `CODELLDB_PATH` environment variable to your local CodeLLDB installation (for example from the VSCode extension) or run `pnpm --filter @debugmcp/adapter-rust run build:adapter` from a cloned repository to download your platform binaries.

- `pnpm install` (postinstall hook)
- `pnpm vendor` or `pnpm vendor:adapters`
- `pnpm --filter @debugmcp/adapter-rust run build:adapter`
- `node packages/adapter-rust/scripts/vendor-codelldb.js`

> **Default behavior:** Locally, the vendoring script downloads **all supported platforms** (win32-x64, linux-x64, linux-arm64, darwin-x64, darwin-arm64) so Docker builds can reuse the same artifacts. Set `CODELLDB_VENDOR_ALL=false` to vendor only the current host platform. In CI environments, the script vendors only the current platform unless `CODELLDB_VENDOR_ALL=true` is explicitly set.

### Manual control

```bash
# Force a fresh download for the current platform
pnpm vendor:force

# Skip vendoring temporarily (for air-gapped dev shells)
SKIP_ADAPTER_VENDOR=true pnpm install
```

### Useful environment flags

- `CODELLDB_VERSION`: override the release tag (the runtime resolver defaults to `1.11.8` when reading vendored version metadata fails)
- `CODELLDB_FORCE_REBUILD=true`: ignore cached binaries and re-download
- `CODELLDB_PLATFORMS=win32-x64,linux-x64,linux-arm64,darwin-x64,darwin-arm64`: vendor specific platforms (comma-separated)
- `CODELLDB_VENDOR_ALL=false`: opt out of the "vendor every platform" default and fall back to host-only downloads
- `CODELLDB_VENDOR_LOCAL_ONLY=true`: disable network downloads entirely and fail if the requested platform isn't already vendored (used by Docker builds that copy pre-fetched artifacts)
- `CODELLDB_KEEP_TEMP=true`: retain the downloaded VSIX and extracted temp folders for inspection
- `SKIP_ADAPTER_VENDOR=true`: opt out entirely (used by CI jobs that pre-bake artifacts)

### Troubleshooting vendoring

1. Run `pnpm vendor:status` to see which adapters are ready for the current machine.
2. Re-run `pnpm --filter @debugmcp/adapter-rust run build:adapter` and check the `[CodeLLDB vendor]` logs:
   - `HTTP response: ...` confirms GitHub access.
   - `Artifact magic header: 504b0304 (PK..)` verifies a valid VSIX download.
3. If you see `Failed to vendor`, re-run with `CODELLDB_KEEP_TEMP=true` to inspect the downloaded file under `packages/adapter-rust/vendor/codelldb/temp`.
4. Network errors or 404 responses usually indicate a typo in `CODELLDB_VERSION` or a transient GitHub outage—try again with `pnpm vendor:force`.
5. On Windows, ensure PowerShell is allowed to create executables in the workspace (no antivirus quarantine).

## Features

### Supported Capabilities

- ✅ **Breakpoints**: Set breakpoints in Rust source files
- ✅ **Stepping**: Step over, into, and out of functions
- ✅ **Variable inspection**: View local variables, including complex types
- ✅ **Stack traces**: Full call stack with Rust symbols
- ✅ **Cargo integration**: Debug Cargo projects directly
- ✅ **Debug/Release builds**: Support for both build configurations
- ✅ **Async debugging**: Debug tokio and async-std applications

### Rust-Specific Features

1. **Smart Type Display**: Collections like `Vec`, `HashMap`, and `String` are displayed in a readable format
2. **Ownership Tracking**: See borrowed vs owned values
3. **Pattern Matching**: Step through match expressions
4. **Macro Expansion**: Debug through macro-generated code

## Quick Start

### 1. Create a Debug Session

```json
{
  "tool": "create_debug_session",
  "arguments": {
    "language": "rust",
    "name": "My Rust Debug Session"
  }
}
```

### 2. Build Your Rust Project

```bash
# For debug build (recommended for debugging)
cargo build

# For release build (optimized, harder to debug)
cargo build --release
```

### 3. Set Breakpoints

```json
{
  "tool": "set_breakpoint",
  "arguments": {
    "sessionId": "your-session-id",
    "file": "src/main.rs",
    "line": 10
  }
}
```

### 4. Start Debugging

For a simple binary:
```json
{
  "tool": "start_debugging",
  "arguments": {
    "sessionId": "your-session-id",
    "scriptPath": "target/debug/my_program"
  }
}
```

For a Cargo project with arguments:
```json
{
  "tool": "start_debugging",
  "arguments": {
    "sessionId": "your-session-id",
    "scriptPath": "target/debug/my_program",
    "args": ["--verbose", "input.txt"],
    "dapLaunchArgs": {
      "cargo": {
        "bin": "my_program",
        "release": false
      }
    }
  }
}
```

### 5. Understand What the Rust Example Helper Does

Our test suite uses `tests/e2e/rust-example-utils.ts` to make sure every rust smoke test compiles and reuses binaries deterministically. The helper does three things you can mirror in your own workflows:

1. **Build with the GNU toolchain when available.** On Windows it looks up `dlltool.exe` (via `@debugmcp/adapter-rust`’s `findDlltoolExecutable`) and runs `cargo +stable-gnu build --target x86_64-pc-windows-gnu`. If that fails it logs a warning and falls back to an explicit MSVC-targeted build (`+stable-msvc build --target x86_64-pc-windows-msvc`). You can follow the same pattern manually:
   ```powershell
   # GNU-preferred build on Windows
   rustup target add x86_64-pc-windows-gnu
   cargo +stable-gnu build --target x86_64-pc-windows-gnu
   ```
   ```bash
   # Non-Windows hosts usually only need the default debug build
   cargo build
   ```
2. **Resolve the correct binary path for `start_debugging`.** Breakpoints should always reference the `.rs` source file (absolute paths avoid MCP resolution issues), but `start_debugging.scriptPath` must point to the compiled artifact:
   - Windows GNU: `examples/rust/<name>/target/x86_64-pc-windows-gnu/debug/<name>.exe`
   - Windows MSVC fallback: `examples/rust/<name>/target/debug/<name>.exe`
   - Unix-like hosts: `examples/rust/<name>/target/debug/<name>`

   Tokio/async builds use exactly the same rule—the helper’s `prepareRustExample('async_example')` just compiles a different crate and returns `{ sourcePath, binaryPath }` so the smoke tests can reuse those paths.
3. **Cache builds between tests.** Once an example has been compiled, the helper stores both paths in-memory so the rest of the suite can run without triggering another `cargo build`. For local work you can emulate this with Cargo’s default incremental builds: as long as you relaunch the same `target/<triple>/debug/<binary>` the MCP `start_debugging` call does not care when the binary was produced.

> **Tip:** The `prepareRustExample` function is an internal test helper, not a supported public utility. You can replicate its approach in your own build tooling. The important detail is that breakpoints reference source (`src/main.rs`) while `start_debugging.scriptPath` references the compiled executable. This is true for synchronous and async (Tokio) binaries alike.

## Cargo Integration

The Rust adapter provides special support for Cargo projects:

### Debug Configuration

```json
{
  "dapLaunchArgs": {
    "cargo": {
      "bin": "my_program",   // Binary target name
      "release": false        // false for debug build, true for release
    },
    "args": ["--help"],       // Program arguments (top-level, not inside cargo)
    "env": {                  // Environment variables
      "RUST_LOG": "debug"
    }
  }
}
```

The `cargo` object supports these fields:
- `bin`: Binary target name
- `example`: Example target name
- `test`: Test target name
- `release`: Build in release mode (boolean, default: false)

### Workspace Support

For Cargo workspaces with multiple packages:
```json
{
  "dapLaunchArgs": {
    "cargo": {
      "bin": "my-crate"    // Specify which binary target to debug
    }
  }
}
```

## Common Debugging Scenarios

### 1. Debug a Unit Test

```bash
# Build the test executable
cargo test --no-run

# Find the test executable (usually in target/debug/deps/)
ls target/debug/deps/*-*

# Debug it
```

```json
{
  "tool": "start_debugging",
  "arguments": {
    "sessionId": "your-session-id",
    "scriptPath": "target/debug/deps/my_crate-abc123def456",
    "args": ["test_name", "--nocapture"]
  }
}
```

### 2. Debug with Environment Variables

```json
{
  "tool": "start_debugging",
  "arguments": {
    "sessionId": "your-session-id",
    "scriptPath": "target/debug/my_program",
    "dapLaunchArgs": {
      "env": {
        "RUST_BACKTRACE": "1",
        "RUST_LOG": "debug",
        "DATABASE_URL": "postgres://localhost/mydb"
      }
    }
  }
}
```

### 3. Debug Async Code

When debugging async Rust code with tokio:

```rust
#[tokio::main]
async fn main() {
    // Set breakpoint here to debug async initialization
    // Your async code here
    my_async_function().await;
}
```

### 4. Inspect Complex Types

```json
{
  "tool": "get_variables",
  "arguments": {
    "sessionId": "your-session-id",
    "scope": 5
  }
}
```

Response will show Rust types clearly:
```json
{
  "variables": [
    {"name": "my_vec", "value": "[1, 2, 3, 4, 5]", "type": "Vec<i32>"},
    {"name": "my_string", "value": "\"Hello, Rust!\"", "type": "String"},
    {"name": "my_option", "value": "Some(42)", "type": "Option<i32>"},
    {"name": "my_result", "value": "Ok(\"success\")", "type": "Result<&str, Error>"}
  ]
}
```

## Troubleshooting

### Breakpoints Not Hit

1. **Ensure debug symbols are included**:
   - Use debug builds (`cargo build`) not release
   - Or add debug symbols to release: 
     ```toml
     [profile.release]
     debug = true
     ```

2. **Check optimization level**:
   - Optimizations can prevent breakpoints
   - Use `opt-level = 0` for debugging

3. **Verify file paths**:
   - Use absolute paths or paths relative to workspace root
   - Ensure the file exists and matches the compiled code

### Variables Show as Optimized Out

This happens in release builds or with optimizations enabled:
```toml
[profile.dev]
opt-level = 0  # No optimization for better debugging
```

### Can't Find CodeLLDB

If CodeLLDB is not found:
```bash
# Re-run the vendor script
cd packages/adapter-rust
npm run build:adapter

# Check if it was downloaded (fixed layout under vendor/codelldb/ with per-platform subdirectories)
ls vendor/codelldb/
# e.g., vendor/codelldb/win32-x64/, vendor/codelldb/linux-x64/, vendor/codelldb/darwin-arm64/
```

### Async Code Debugging Issues

- Use `tokio::time::sleep` instead of `std::thread::sleep` in async contexts
- Set breakpoints inside async blocks, not on the `async fn` declaration
- Use `.await` points as natural breakpoint locations

## Performance Tips

1. **Use debug builds for development**: They're slower but much easier to debug
2. **Limit scope of debugging**: Use conditional breakpoints to avoid stopping too often
3. **Use logging**: Combine debugging with `env_logger` or `tracing` for better insights
4. **Profile before optimizing**: Use `cargo flamegraph` or `perf` to find bottlenecks

## Advanced Features

### Conditional Breakpoints

```json
{
  "tool": "set_breakpoint",
  "arguments": {
    "sessionId": "your-session-id",
    "file": "src/main.rs",
    "line": 42,
    "condition": "counter > 100"
  }
}
```

### Expression Evaluation

Evaluate Rust expressions in the current debug context. Note that CodeLLDB evaluates expressions through LLDB, so some Rust-specific syntax (e.g., closures, trait methods) may not be supported:
```json
{
  "tool": "evaluate_expression",
  "arguments": {
    "sessionId": "your-session-id",
    "expression": "my_vec.len()"
  }
}
```

## Examples

See the `examples/rust/` directory for complete examples:
- `hello_world/`: Basic Rust debugging
- `async_example/`: Async/await with Tokio
- More examples coming soon!

## Known Limitations

1. **Macro debugging**: Stepping through macros can be confusing due to expansion
2. **Inline functions**: May not have breakpoint locations
3. **Generic functions**: Need concrete instantiation for breakpoints
4. **Async stack traces**: Can be deep due to runtime machinery

## Resources

- [CodeLLDB Documentation](https://github.com/vadimcn/vscode-lldb)
- [Rust Debugging Book](https://rustc-dev-guide.rust-lang.org/debugging.html)
- [Cargo Documentation](https://doc.rust-lang.org/cargo/)
- [Debug Adapter Protocol](https://microsoft.github.io/debug-adapter-protocol/)
