Skip to content

Debugging (GDB) — Breakpoints, Backtraces, Memory Inspection, Conditional Breakpoints, Core Dumps

DodaTech Updated 2026-06-28 8 min read

In this tutorial, you will learn about Debugging (GDB). We cover key concepts, practical examples, and best practices to help you master this topic.

GDB (GNU Debugger) enables interactive debugging of C++ programs — setting breakpoints, stepping through code, inspecting variables and memory, and analyzing core dumps to diagnose crashes.

What You'll Learn

You will compile C++ programs with debugging symbols (-g), set breakpoints on functions, lines, and conditions, step through code with next, step, continue, and finish, inspect variables, registers, and memory, use backtraces to find crash locations, analyze core dumps for post-mortem debugging, and use GDB's TUI and reverse debugging features.

Why It Matters

Debugging is where developers spend most of their time. Printf debugging is slow and limited — GDB lets you inspect any variable at any point without recompiling. For complex bugs (memory corruption, race conditions, crashes), GDB is essential. Every professional C++ developer should know the basic GDB workflow.

Learning Path

graph LR
    A["66: Unit Testing"] --> B["67: Debugging (GDB)"]
    B --> C["68: Performance Profiling"]
    C --> D["69: Best Practices"]
    style A fill:#4a90d9,stroke:#2c5f8a,color:#fff
    style B fill:#4a90d9,stroke:#2c5f8a,color:#fff
    style C fill:#4a90d9,stroke:#2c5f8a,color:#fff
    style D fill:#4a90d9,stroke:#2c5f8a,color:#fff

Compiling for Debugging

Debug symbols must be enabled during compilation.

# Debug build with symbols
g++ -g -O0 -o program main.cpp calculator.cpp

# With address sanitizer (catches memory errors)
g++ -g -O0 -fsanitize=address -o program main.cpp

# With undefined behavior sanitizer
g++ -g -O0 -fsanitize=undefined -o program main.cpp

# Debug + no optimizations + all warnings
g++ -g -O0 -Wall -Wextra -o program main.cpp
// buggy.cpp — program with bugs for debugging
#include <iostream>
#include <vector>
#include <cstring>

std::vector<int> createData() {
    std::vector<int> v = {1, 2, 3, 4, 5};
    return v;
}

int computeSum(const std::vector<int>& data) {
    int sum = 0;
    for (size_t i = 0; i <= data.size(); ++i) {  // Bug: off-by-one
        sum += data[i];
    }
    return sum;
}

void bufferOverflow() {
    char buffer[8];
    strcpy(buffer, "This string is too long for the buffer!");
    std::cout << buffer << "\n";
}

int main() {
    auto data = createData();
    int result = computeSum(data);
    std::cout << "Sum: " << result << "\n";

    // bufferOverflow();  // Uncomment for crash

    return 0;
}

GDB Session Walkthrough

# Start GDB
gdb ./program

# Run without arguments
(gdb) run

# Run with arguments
(gdb) run arg1 arg2

# Run with input redirection
(gdb) run < input.txt

Setting Breakpoints

# Break on function
(gdb) break main
(gdb) break computeSum

# Break on specific line
(gdb) break buggy.cpp:15

# Conditional breakpoint (trigger when condition is true)
(gdb) break buggy.cpp:16 if i > 3

# Break at memory address
(gdb) break *0x4005c0

# Break on C++ method (quoted)
(gdb) break 'std::vector<int>::push_back(int)'

# List all breakpoints
(gdb) info breakpoints

# Delete breakpoint
(gdb) delete 1

# Disable/enable
(gdb) disable 2
(gdb) enable 2

Stepping Through Code

# Run to next breakpoint
(gdb) continue

# Step into function
(gdb) step

# Step over function (don't enter)
(gdb) next

# Step one assembly instruction
(gdb) stepi

# Finish current function (return to caller)
(gdb) finish

# Continue until current line
(gdb) until

# Continue to specific line
(gdb) advance buggy.cpp:20

Inspecting State

# Print variable value
(gdb) print sum
(gdb) print data
(gdb) print data.size()

# Print with format
(gdb) print/x 42      # hexadecimal
(gdb) print/t 42      # binary
(gdb) print/c 65      # character
(gdb) print/f 3.14    # float

# Print all local variables
(gdb) info locals

# Print function arguments
(gdb) info args

# Print registers
(gdb) info registers

# Print stack trace
(gdb) backtrace
(gdb) bt full         # with local variables
(gdb) bt 5            # first 5 frames

# Switch to frame
(gdb) frame 2
(gdb) up              # up one frame
(gdb) down            # down one frame

Memory Inspection

# Examine memory at address
(gdb) x buffer        # default format
(gdb) x/10xb buffer   # 10 hex bytes
(gdb) x/10xw buffer   # 10 hex words (4-byte)
(gdb) x/s buffer      # as string
(gdb) x/i $rip        # instruction at instruction pointer
(gdb) x/10i main      # disassemble first 10 instructions

# Examine pointer
(gdb) print ptr
(gdb) print *ptr
(gdb) x/20xb ptr      # memory dump

# Find what value is at an address
(gdb) info symbol 0x7fffffffe000

Analyzing Crashes (Segfaults)

# Run until crash, GDB shows backtrace automatically
(gdb) run
# Program received signal SIGSEGV, Segmentation fault.
# 0x00005555555552a4 in computeSum (data=...) at buggy.cpp:16
# 16          sum += data[i];

(gdb) backtrace
# #0  0x... in computeSum (data=...) at buggy.cpp:16
# #1  0x... in main () at buggy.cpp:27

(gdb) print i
# $1 = 5
(gdb) print data.size()
# $2 = 5
# Bug: reading data[5] which is out of bounds!

(gdb) frame 1  # Go to main
(gdb) print data
# Shows main's data variable

Post-Mortem Debugging with Core Dumps

# Enable core dumps
ulimit -c unlimited

# Run the program (crashes, creates core dump)
./program

# Analyze with GDB
gdb ./program core

# GDB shows where it crashed automatically
(gdb) bt
(gdb) print variable
(gdb) list

Watchpoints (Data Breakpoints)

# Break when variable changes
(gdb) watch sum

# Break when variable is read
(gdb) rwatch sum

# Break on read or write
(gdb) awatch sum

# Conditional watchpoint
(gdb) watch sum if i > 5

Reverse Debugging (GDB Record)

# Start recording execution
(gdb) record

# Run to find bug
(gdb) continue

# Now you can reverse-step
(gdb) reverse-step
(gdb) reverse-next
(gdb) reverse-continue

# Go back to see where a variable was set
(gdb) reverse-step
(gdb) print variable

GDB TUI (Text User Interface)

# Start with TUI
gdb -tui ./program

# Or toggle during session
(gdb) tui enable

# Keyboard shortcuts in TUI:
# Ctrl+X A  — toggle TUI
# Ctrl+P/N  — prev/next command
# Ctrl+X O  — switch focus between windows

GDB with C++ Specifics

# Call functions during debugging
(gdb) call strlen(buffer)
(gdb) call std::vector<int>(5, 10)

# Print STL containers (with pretty-printing)
(gdb) print my_vector
(gdb) print my_map

# Set variables
(gdb) set var sum = 0
(gdb) set var i = 0

# Demangle C++ names
(gdb) set print demangle on
(gdb) info functions std::vector

GDB Scripting

# save_commands.gdb — GDB script
break main
break computeSum
run
print "Starting debugging session"
print data
continue
print sum
quit

# Run with script:
# gdb -x save_commands.gdb ./program

Common Mistakes

Mistake 1: Debugging optimized code

-O2 optimizes variables into registers, making them invisible. Always debug with -O0.

Mistake 2: Missing -g flag

Without debug symbols, GDB shows raw assembly and memory addresses, not source code and variable names.

Mistake 3: Forgetting to compile with address sanitizer

g++ -g -O0 -fsanitize=address -fno-omit-frame-pointer program.cpp

ASAN catches out-of-bounds, use-after-free, and memory leaks.

Mistake 4: Using step instead of next when you don't want to enter functions

step enters every function call. Use next to stay at the same level.

Mistake 5: Not using conditional breakpoints for loops

break buggy.cpp:16 if i == 1000000  # Much faster than hitting break 1M times

Practice Questions

  1. What is the GDB command to set a breakpoint on line 42 of main.cpp? Answer: break main.cpp:42

  2. What is the difference between next and step in GDB? Answer: next executes the current line without stepping into function calls. step enters functions.

  3. How do you inspect all local variables in the current function? Answer: info locals (or bt full for locals in all frames).

  4. What compile flags are needed for debugging? Answer: -g (debug symbols) and -O0 (no optimizations). Add -fsanitize=address for memory error detection.

  5. How do you analyze a core dump after a crash? Answer: ulimit -c unlimited (enable), run the program, then gdb ./program core and use bt for backtrace.

FAQ

What is GDB used for

GDB (GNU Debugger) debugs compiled C++ programs. You can set breakpoints, step through code, inspect variables, analyze crashes, and modify execution state at runtime.

Why does GDB show ?? in backtrace

Missing debug symbols. Compile with -g, and ensure libraries have debug symbols installed (libstdc++6-dbg on Debian/Ubuntu).

How do I debug a segfault in GDB

Run the program in GDB. When it crashes, use 'bt' (backtrace) to see where. Use 'frame N' to examine variables at each frame.

What is a core dump

A core dump is a file containing the memory state of a crashed program. GDB can analyze it post-mortem to find the crash location and inspect variables.

Is GDB the only C++ debugger

No. LLDB (LLVM debugger), Visual Studio debugger, and TotalView are alternatives. GDB is the standard on Linux.

Mini Project

Given this buggy program, debug it with GDB and identify all bugs:

// debug_me.cpp
#include <iostream>
#include <vector>
#include <cstring>

std::vector<int> extractPositive(const std::vector<int>& input) {
    std::vector<int> result;
    for (size_t i = 0; i <= input.size(); ++i) {  // Bug 1
        if (input[i] > 0) {                          // Bug 2
            result.push_back(input[i]);
        }
    }
    return result;
}

int* createArray() {
    int arr[5] = {10, 20, 30, 40, 50};  // Bug 3
    return arr;  // Returns pointer to local array!
}

int main() {
    std::vector<int> data = {-1, 2, -3, 4, -5, 6};
    auto positive = extractPositive(data);

    std::cout << "Positive count: " << positive.size() << "\n";

    int* arr = createArray();
    std::cout << "First: " << arr[0] << "\n";  // Undefined behavior

    return 0;
}

Bugs to find with GDB:

  1. Off-by-one in loop condition
  2. Out-of-bounds vector access
  3. Returning pointer to stack-allocated array

Use GDB breakpoints, watchpoints, and memory inspection to identify each issue. This exercise mirrors real C++ debugging scenarios that every developer encounters.

What's Next

You now debug C++ programs with GDB. Next, you will learn performance profiling — identifying bottlenecks, measuring cache misses, and optimizing C++ code with profilers like perf, Valgrind, and Intel VTune.

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro