Project 1: The Basics - First Steps

Build a tiny C program and use GDB to step, inspect variables, and understand control flow.

Quick Reference

Attribute Value
Difficulty Beginner
Time Estimate 1-2 hours
Language GDB commands (C target)
Prerequisites Basic C, compile with gcc -g
Key Topics Breakpoints, stepping, stack frames, symbols

1. Learning Objectives

By completing this project, you will:

  1. Compile a C program with debug symbols and understand why symbols matter.
  2. Control execution flow with break, run, next, and step.
  3. Inspect variables, stack frames, and the call stack.
  4. Modify program state during a debug session.

2. Theoretical Foundation

2.1 Core Concepts

  • Debug Symbols (DWARF): Symbol data maps machine addresses back to source lines and variable names. Without it, GDB can only show raw addresses.
  • Stack Frames: Each function call creates a stack frame containing locals, arguments, and a return address. backtrace walks these frames.
  • Breakpoints: GDB inserts a trap instruction (int 3 on x86) to halt execution at a chosen address.

2.2 Why This Matters

This is the minimum set of mechanics you need to make GDB useful on real bugs. Once you can stop, inspect, and continue, you can diagnose the majority of issues without adding logging.

2.3 Historical Context / Background

GDB dates to the 1980s and has remained the standard because it operates at the process and instruction level. These foundations predate modern IDEs and are still what IDE debuggers call under the hood.

2.4 Common Misconceptions

  • “GDB needs source code”: It does not, but it is much less helpful without symbols.
  • “next and step are the same”: next skips over function calls; step enters them.

3. Project Specification

3.1 What You Will Build

A small C program with a loop and a function call. You will use GDB to control execution and inspect the program state at key points.

3.2 Functional Requirements

  1. Compile with symbols: Build using gcc -g -O0.
  2. Stop at main: Set a breakpoint and run to it.
  3. Step through the loop: Use next and step to move in and out of functions.
  4. Inspect state: Print variable values and view a backtrace.

3.3 Non-Functional Requirements

  • Performance: Not applicable; this is a learning tool.
  • Reliability: Debug session should be repeatable.
  • Usability: Commands should be minimal and reproducible.

3.4 Example Usage / Output

(gdb) break main
(gdb) run
(gdb) next
(gdb) print i
$1 = 0
(gdb) step
(gdb) backtrace

3.5 Real World Outcome

You will see a complete GDB session in your terminal. Example output:

$ gcc -g -O0 -o target target.c
$ gdb ./target
GNU gdb (GDB) 14.1
Reading symbols from ./target...
(gdb) break main
Breakpoint 1 at 0x401160: file target.c, line 9.
(gdb) run
Starting program: ./target
Breakpoint 1, main () at target.c:9
9       {
(gdb) next
10          for (i = 0; i < 5; ++i) {
(gdb) print i
$1 = 0
(gdb) step
greet (count=0) at target.c:5
5           printf("Hello for the %dth time!\n", count);

4. Solution Architecture

4.1 High-Level Design

┌────────────┐     ┌─────────────┐     ┌──────────────┐
│  target.c  │────▶│  gcc -g -O0 │────▶│  gdb session │
└────────────┘     └─────────────┘     └──────────────┘

4.2 Key Components

Component Responsibility Key Decisions
C target program Provide predictable control flow Keep logic small and observable
GDB session Control and inspect program Use breakpoints and stepping

4.3 Data Structures

struct FrameSnapshot {
    const char *function;
    int line;
    long rip;
};

4.4 Algorithm Overview

Key Algorithm: Manual stepping loop

  1. Break at main.
  2. Execute one line at a time.
  3. Inspect locals and call stack at each step.

Complexity Analysis:

  • Time: O(N) for N steps you take.
  • Space: O(1) within GDB.

5. Implementation Guide

5.1 Development Environment Setup

gcc --version
gdb --version

5.2 Project Structure

project-root/
├── target.c
└── README.md

5.3 The Core Question You’re Answering

“How do I stop a program at a precise point and see what it knows right now?”

5.4 Concepts You Must Understand First

Stop and research these before coding:

  1. Debug Symbols and -g
    • What is DWARF?
    • Why do optimized builds hide variables?
    • Book: “The Art of Debugging with GDB” Ch. 1
  2. Stack Frames
    • What does a call stack look like?
    • Why is the current frame at the top?
    • Book: CSAPP Ch. 3
  3. Stepping Semantics
    • next vs step
    • finish vs continue

5.5 Questions to Guide Your Design

  1. When should you use step instead of next?
  2. Which frame should you inspect to see function arguments?
  3. How do you verify the debugger is aligned with the source line?

5.6 Thinking Exercise

Trace the program on paper and predict i and count values at each greet() call.

5.7 The Interview Questions They’ll Ask

  1. What is the difference between next and step?
  2. Why does GDB show <optimized out> sometimes?
  3. How do you inspect the call stack?

5.8 Hints in Layers

Hint 1: Basic flow

  • break main, run, next, print i

Hint 2: Stack

  • backtrace, frame 0, info locals

Hint 3: Modify state

  • set var i = 10, continue

5.9 Books That Will Help

Topic Book Chapter
GDB basics “The Art of Debugging with GDB” Ch. 1-3
Stack frames CSAPP Ch. 3
C functions K&R C Ch. 4

5.10 Implementation Phases

Phase 1: Foundation (30 minutes)

Goals:

  • Build the target program with symbols.
  • Start a GDB session.

Tasks:

  1. Compile with gcc -g -O0.
  2. Set a breakpoint at main and run.

Checkpoint: You stop at main and see source lines.

Phase 2: Core Functionality (30-45 minutes)

Goals:

  • Step and inspect state.

Tasks:

  1. Use next for loop iterations.
  2. Use step to enter greet().
  3. Print locals and backtrace.

Checkpoint: You can explain where you are and what values exist.

Phase 3: Polish & Edge Cases (15-30 minutes)

Goals:

  • Practice modifying state.

Tasks:

  1. Change i mid-run.
  2. Observe altered output.

Checkpoint: Program reflects the modified value.

5.11 Key Implementation Decisions

Decision Options Recommendation Rationale
Optimization level -O0, -O2 -O0 Keeps variables visible
Breakpoint placement main, greet main first Establishes flow

6. Testing Strategy

6.1 Test Categories

Category Purpose Examples
Sanity checks Verify debug symbols file target shows debug_info
GDB usage Confirm command workflow break, run, print
State changes Confirm modifications work set var i = 10

6.2 Critical Test Cases

  1. Breakpoint hits: break main stops at line 9.
  2. Stepping works: step enters greet.
  3. Variable inspection: print i shows loop progress.

6.3 Test Data

Loop iterations: 0..4
Expected prints: 5 lines

7. Common Pitfalls & Debugging

7.1 Frequent Mistakes

Pitfall Symptom Solution
No symbols No line numbers Recompile with -g
Optimization <optimized out> Use -O0
Wrong binary Breakpoints not hit Ensure correct executable

7.2 Debugging Strategies

  • Use info files to confirm symbols loaded.
  • Use list to confirm source alignment.

7.3 Performance Traps

Not applicable at this scale.


8. Extensions & Challenges

8.1 Beginner Extensions

  • Add a second function and step into it.
  • Set a breakpoint by line number.

8.2 Intermediate Extensions

  • Use condition on a breakpoint to stop at i == 3.
  • Inspect registers with info registers.

8.3 Advanced Extensions

  • Use disassemble to inspect the loop.
  • Set a temporary breakpoint with tbreak.

9. Real-World Connections

9.1 Industry Applications

  • Production debugging: First response to a reported bug is often a GDB session.
  • Embedded systems: Many targets only support GDB-like workflows.
  • GDB: https://sourceware.org/gdb/ - The debugger itself.
  • rr: https://rr-project.org/ - Record and replay debugging.

9.3 Interview Relevance

  • Demonstrates understanding of call stacks and control flow.
  • Shows you can debug without IDE crutches.

10. Resources

10.1 Essential Reading

  • “The Art of Debugging with GDB” - Ch. 1-3 for fundamentals.
  • GDB Manual - “Running Programs” and “Stopping and Continuing” sections.

10.2 Video Resources

  • GDB basics walkthrough (search: “GDB basics break run step”).

10.3 Tools & Documentation

  • GDB: https://sourceware.org/gdb/ - Official docs.
  • gcc: https://gcc.gnu.org/ - Compiler documentation.

11. Self-Assessment Checklist

11.1 Understanding

  • I can explain why debug symbols are required.
  • I can describe what a stack frame contains.
  • I can explain the difference between next and step.

11.2 Implementation

  • Breakpoints hit reliably.
  • I can inspect locals and call stack.
  • I can modify a variable and see the effect.

11.3 Growth

  • I can describe a bug I could now diagnose faster.
  • I can explain this workflow to someone else.

12. Submission / Completion Criteria

Minimum Viable Completion:

  • Break at main and step into greet.
  • Print i and count values.
  • Show a backtrace with two frames.

Full Completion:

  • Modify i during execution and observe output changes.
  • Document the command sequence used.

Excellence (Going Above & Beyond):

  • Add a second function and debug across both.
  • Use a conditional breakpoint or watchpoint.

This guide was generated from LEARN_GDB_DEEP_DIVE.md. For the complete learning path, see the parent directory README.