# How to Set Up an Effective Rust Debugger for Complex Projects

> Effectively set up a rust debugger for complex projects. Learn best practices for DWARF symbols, zero optimization, and pretty printers to enable reliable source-level debugging.

- Repository: [The Rust Programming Language/rust](https://github.com/rust-lang/rust)
- Tags: best-practices
- Published: 2026-02-16

---

**Configuring a rust debugger with full DWARF symbols, zero optimization, and official pretty‑printers enables reliable source‑level debugging even for large, macro‑heavy codebases.**

Debugging complex Rust projects requires more than just installing a debugger; it demands an understanding of how the compiler emits debug metadata. The official rust‑lang/rust repository demonstrates that a robust rust debugger setup hinges on specific compiler flags, profile configurations, and toolchain components that preserve source‑level mapping across macros and optimizations.

## Build with Full Debug Information

### Configure Cargo Profiles for Maximum Debuggability

To ensure your rust debugger can map binary addresses back to source lines, variable names, and types, you must generate full DWARF symbols. In your [`Cargo.toml`](https://github.com/rust-lang/rust/blob/main/Cargo.toml), set the **debug** level to `2` and disable aggressive optimizations:

```toml
[profile.dev]
opt-level = 0          # Disable optimizations that remove variables

debug = 2              # Maximum debug info

debug-assertions = true
overflow-checks = true
codegen-units = 1      # Prevent splitting into multiple object files

incremental = false    # Simplify stepping behavior

```

These settings align with the internal conventions documented in [`src/doc/rustc-dev-guide/src/debuginfo/debugger-internals.md`](https://github.com/rust-lang/rust/blob/main/src/doc/rustc-dev-guide/src/debuginfo/debugger-internals.md), which details how the compiler generates debug metadata.

### Preserve Macro Expansion Information

When debugging code generated by macros (such as `serde` or `prost`), enable the **`-Zdebug-macro`** flag to preserve macro expansion details in the DWARF output:

```bash
export RUSTFLAGS="-Zdebug-macro"
cargo build

```

This flag ensures the debugger can display expanded source code, as detailed in [`src/doc/rustc-dev-guide/src/debuginfo/debugger-visualizers.md`](https://github.com/rust-lang/rust/blob/main/src/doc/rustc-dev-guide/src/debuginfo/debugger-visualizers.md).

## Choose the Right Rust Debugger

The Rust toolchain provides specialized debugger wrappers that automatically load pretty‑printers for common types like `Vec`, `String`, and `Option`.

### rust‑gdb and rust‑lldb

Install the official debugger components via rustup:

```bash
rustup component add rust-gdb
rustup component add rust-lldb

```

- **rust‑gdb** wraps GNU GDB with Rust‑specific Python pretty‑printers located in `$(rustc --print sysroot)/lib/rustlib/etc/`.
- **rust‑lldb** provides equivalent functionality for LLDB users on macOS.

Both tools are referenced in [`src/tools/rust-analyzer/docs/book/src/contributing/debugging.md`](https://github.com/rust-lang/rust/blob/main/src/tools/rust-analyzer/docs/book/src/contributing/debugging.md), which documents how the pretty‑printers are generated and loaded.

### IDE Integrations

For visual debugging, use **CodeLLDB** in VS Code or the **IntelliJ Rust** plugin in CLion. These extensions automatically detect the Rust toolchain and respect the `dev` profile settings.

To configure VS Code debugging, create [`.vscode/launch.json`](https://github.com/rust-lang/rust/blob/main/.vscode/launch.json) with the following structure:

```json
{
  "version": "0.2.0",
  "configurations": [
    {
      "name": "(gdb) Launch",
      "type": "cppdbg",
      "request": "launch",
      "program": "${workspaceFolder}/target/debug/my_project",
      "args": [],
      "stopAtEntry": false,
      "cwd": "${workspaceFolder}",
      "environment": [
        { "name": "RUST_BACKTRACE", "value": "full" }
      ],
      "externalConsole": false,
      "MIMode": "gdb",
      "setupCommands": [
        {
          "description": "Enable pretty printing",
          "text": "-enable-pretty-printing",
          "ignoreFailures": true
        }
      ]
    }
  ]
}

```

## Configure Your Debugger Environment

### Load Rust Pretty‑Printers

If you must use a plain `gdb` binary instead of `rust-gdb`, manually source the pretty‑printer scripts in your `~/.gdbinit`:

```gdb
source $(rustc --print sysroot)/lib/rustlib/etc/gdb_load_rust_pretty_printers.py

```

Without these printers, container types display as raw memory addresses rather than human‑readable structures.

### Inspect MIR for Compiler‑Level Bugs

When investigating intricate control‑flow issues, dump the Mid‑level IR (MIR) using `-Z dump-mir`. The mapping from MIR to DWARF is implemented in [`compiler/rustc_codegen_ssa/src/mir/debuginfo.rs`](https://github.com/rust-lang/rust/blob/main/compiler/rustc_codegen_ssa/src/mir/debuginfo.rs) and [`compiler/rustc_mir_dataflow/src/debuginfo.rs`](https://github.com/rust-lang/rust/blob/main/compiler/rustc_mir_dataflow/src/debuginfo.rs), which handle variable lifetimes and debug info emission. Refer to these files when analyzing how the compiler represents Rust constructs to the debugger.

### Use `dbg!` for Rapid Inspection

The **`dbg!`** macro prints both the expression and its value along with the source file and line number. It requires no debugger setup and is invaluable for quick sanity checks without attaching a full rust debugger session.

## Debug Tests and Async Code

### Attach to Test Binaries

To debug a specific test, build without running, then attach your rust debugger to the test binary:

```bash
cargo test --no-run
rust-gdb target/debug/deps/my_crate-<hash>

```

In GDB, set breakpoints at specific test lines:

```gdb
break tests/my_integration_test.rs:15
run

```

The test harness implementation in [`src/tools/compiletest/src/debuggers.rs`](https://github.com/rust-lang/rust/blob/main/src/tools/compiletest/src/debuggers.rs) demonstrates how the Rust project itself validates debugger behavior against compiled test cases.

### Debug Async Code

For asynchronous code, combine your rust debugger with runtime‑specific tools like `tokio-console`. Set breakpoints in async blocks and inspect futures using the standard pretty‑printers. Note that async test harnesses emit additional DWARF information to handle suspension points correctly, as seen in the compiler's async test suite.

## Avoid Common Debugging Pitfalls

**Breakpoints never hit**: The binary was likely built with `opt-level > 0` or stripped symbols. Rebuild using the dev profile configuration shown above.

**Variables appear as `<optimized out>`**: Aggressive optimizations removed the variable from the stack. Add `#[inline(never)]` to the function or lower `opt-level` to `0`.

**GDB prints `??` for line numbers**: The DWARF information references an unreachable source path. Ensure you compile from the exact source tree and have not moved the binary relative to its build directory.

**No Rust‑specific pretty printers**: You launched plain `gdb` instead of `rust-gdb`, or the Python script failed to load. Verify the path to [`gdb_load_rust_pretty_printers.py`](https://github.com/rust-lang/rust/blob/main/gdb_load_rust_pretty_printers.py) matches your sysroot.

## Summary

- Configure [`Cargo.toml`](https://github.com/rust-lang/rust/blob/main/Cargo.toml) with `debug = 2`, `opt-level = 0`, and `codegen-units = 1` to generate accurate DWARF symbols.
- Use `rust-gdb` or `rust-lldb` from the official toolchain to automatically load Rust pretty‑printers.
- Set `RUSTFLAGS="-Zdebug-macro"` when debugging macro‑generated code to preserve expansion info.
- Reference the compiler's debuginfo implementation in [`compiler/rustc_codegen_ssa/src/mir/debuginfo.rs`](https://github.com/rust-lang/rust/blob/main/compiler/rustc_codegen_ssa/src/mir/debuginfo.rs) for deep insight into variable mapping.
- Build tests with `cargo test --no-run` and attach the debugger to the specific test binary in `target/debug/deps/`.

## Frequently Asked Questions

### What is the difference between rust‑gdb and standard gdb?

**rust‑gdb** is a wrapper script distributed with the Rust toolchain that automatically configures GDB to load Rust-specific Python pretty‑printers. These printers format standard library types like `Vec` and `HashMap` correctly, whereas standard gdb displays them as opaque pointers. The pretty‑printer source is maintained in the rust‑analyzer documentation at [`src/tools/rust-analyzer/docs/book/src/contributing/debugging.md`](https://github.com/rust-lang/rust/blob/main/src/tools/rust-analyzer/docs/book/src/contributing/debugging.md).

### Why do my variables show as `<optimized out>` in the rust debugger?

This occurs when the compiler optimizes away local variables due to high optimization levels. Rust enables optimizations even in debug profiles for dependencies. Set `opt-level = 0` in your `[profile.dev]` section or add the `#[inline(never)]` attribute to functions where you need to inspect every variable.

### How do I debug Rust macros effectively?

Macros expand at compile time, so standard debug info points to the expansion site rather than the macro definition. Pass `-Zdebug-macro` in your `RUSTFLAGS` environment variable when building. This preserves macro expansion details in the DWARF output, allowing the rust debugger to step through the generated code as described in [`src/doc/rustc-dev-guide/src/debuginfo/debugger-visualizers.md`](https://github.com/rust-lang/rust/blob/main/src/doc/rustc-dev-guide/src/debuginfo/debugger-visualizers.md).

### Can I use VS Code for debugging complex Rust projects?

Yes. Install the **CodeLLDB** extension or configure the **C/C++** extension with `rust-gdb`. Create a [`.vscode/launch.json`](https://github.com/rust-lang/rust/blob/main/.vscode/launch.json) that points to `${workspaceFolder}/target/debug/your_binary` and ensure your [`Cargo.toml`](https://github.com/rust-lang/rust/blob/main/Cargo.toml) uses the debug profile settings. VS Code will respect the DWARF symbols and display variables using the same pretty‑printers available in the command‑line tools.