Course contentsShow
← Rust
Lesson 16 of 1,9101. Rust Fundamentals and ToolingFree lesson

Debugging Binaries with GDB and LLDB

Examine stack traces and variable states in compiled Rust binaries using standard debuggers.

In this lesson, you will learn how to inspect your running Rust program by navigating its execution flow and memory state using professional debuggers.

When you run a Rust program normally, it runs as a "black box." If it crashes or behaves unexpectedly, you can't easily see what's happening inside. By using gdb (on Linux) or lldb (on macOS), you can pause your program mid-execution to inspect the stack—the history of function calls—and view the current values of your variables.

Preparing for Debugging

Before you start, ensure you are building your project with debug symbols. Since we learned about Cargo.toml profiles in lesson 5, you know that cargo build defaults to a dev profile which includes these symbols. This allows the debugger to map machine code back to your Rust source code.

Using the Debugger

To start debugging your binary, you would typically run lldb ./target/debug/my_project or gdb ./target/debug/my_project. Once the debugger opens, you can set a breakpoint—a signal to pause execution at a specific line.

Imagine you have a function like this:

fn main() {
    let x = 10;
    let y = 20;
    let sum = x + y; // Set a breakpoint here
    println!("Sum is {}", sum);
}

Once execution hits your breakpoint, you can use debugger commands to interact with the state:

  1. backtrace (or bt): This prints the "stack trace," showing you the chain of functions that led to the current point.
  2. frame variable (or p variable_name): This allows you to inspect the current state of your variables. You can see exactly what x and y contain at that exact moment.
  3. step or next: These commands allow you to move through your code one line at a time to observe how your state changes.

By observing how your data transforms in memory, you can isolate exactly where your logic deviates from your expectations. Debuggers are the most powerful tools in your kit for solving complex, non-obvious issues that println! debugging simply cannot catch.

Key Takeaway: Debuggers allow you to pause execution and "look inside" your running Rust binary to inspect variable states and stack traces, turning invisible logic errors into observable data.