Project 1: The Basics - First Steps

Build a tiny C program and learn the LLDB loop: break, step, inspect, repeat.

Quick Reference

Attribute Value
Difficulty Beginner
Time Estimate 1-2 hours
Language C (debug target), LLDB commands
Prerequisites Basic C syntax, compiling with -g
Key Topics Breakpoints, stepping, stack frames, variable inspection

1. Learning Objectives

By completing this project, you will:

  1. Set and manage breakpoints in LLDB.
  2. Step through code while tracking control flow.
  3. Inspect variables, arguments, and stack frames.
  4. Understand the LLDB object model at a basic level (target/process/thread/frame).

2. Theoretical Foundation

2.1 Core Concepts

  • Debug Symbols (DWARF): Debug info maps source lines and variables to addresses and registers. Without -g, LLDB can only show assembly and raw addresses.
  • Breakpoints vs. Stepping: Breakpoints choose where execution stops; stepping controls how you move between statements.
  • Stack Frames: Each function call pushes a frame containing arguments, locals, and a return address. bt and frame commands are views into this stack.

2.2 Why This Matters

This workflow is the base layer for every later debugging task. If you cannot reliably stop at a point of interest, inspect state, and reason about the call stack, you will not be able to diagnose crashes, hangs, or data corruption effectively.

2.3 Historical Context / Background

LLDB was built as part of the LLVM project to be fast, scriptable, and Clang-aware. The command set differs from GDB, but the mental model is identical: load a target, start a process, select a thread, inspect a frame.

2.4 Common Misconceptions

  • “Step” means the next line: It really means the next instruction that belongs to the current frame; function calls change the frame.
  • Breakpoints are only for lines: You can break on functions, symbols, addresses, or conditions.

3. Project Specification

3.1 What You Will Build

A minimal C program with a loop and a helper function, compiled with debug symbols. The learning deliverable is a repeatable LLDB session that stops at main, steps into greet, and inspects i and count.

3.2 Functional Requirements

  1. Compile with symbols: Produce a binary with -g.
  2. Set a breakpoint: Break at main or a specific line.
  3. Step and inspect: Use next, step, frame variable, and bt.

3.3 Non-Functional Requirements

  • Performance: Not relevant; keep the program tiny.
  • Reliability: Deterministic behavior so LLDB output is consistent.
  • Usability: Your LLDB command history should be repeatable.

3.4 Example Usage / Output

$ clang -g -o target target.c
$ lldb ./target
(lldb) b main
(lldb) run
Process 12345 stopped
* thread #1, stop reason = breakpoint 1.1
    frame #0: 0x0000000100003f7a target`main at target.c:9
(lldb) n
(lldb) fr v i
(int) i = 0
(lldb) s
(lldb) bt

3.5 Real World Outcome

You will see LLDB pause at your breakpoint and display the current source line. The debugger will show the call stack and print the current value of i. For example, after stepping once:

$ lldb ./target
(lldb) b main
(lldb) run
Process 12345 stopped
* thread #1, stop reason = breakpoint 1.1
    frame #0: 0x0000000100003f7a target`main at target.c:9
(lldb) fr v i
(int) i = 0

4. Solution Architecture

4.1 High-Level Design

Source (.c) -> Compile with -g -> Binary + DWARF
                                -> LLDB target -> Process -> Thread -> Frame

4.2 Key Components

Component Responsibility Key Decisions
target.c Simple loop + function Keep control flow easy to follow
LLDB session Breakpoint, stepping, inspection Use function breakpoints for clarity

4.3 Data Structures

// Minimal program state for inspection
void greet(int count) {
    printf("Hello for the %dth time!\n", count);
}

4.4 Algorithm Overview

Key Algorithm: Step/Inspect Loop

  1. Break at main.
  2. Step over loop setup.
  3. Inspect variable i.
  4. Step into greet.
  5. Observe new frame on stack.

Complexity Analysis:

  • Time: O(n) for loop iterations
  • Space: O(1)

5. Implementation Guide

5.1 Development Environment Setup

clang --version
lldb --version

5.2 Project Structure

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

5.3 The Core Question You’re Answering

“How does a debugger map source code to running machine instructions, and how do I navigate that mapping?”

5.4 Concepts You Must Understand First

Stop and research these before coding:

  1. Debug Symbols (-g)
    • What does DWARF store?
    • Why does stripping symbols break source-level debugging?
    • Book Reference: “Linkers and Loaders” Ch. 1-2
  2. Stack Frames
    • What is a call frame, and what lives inside it?
    • How does bt relate to the frame chain?
    • Book Reference: CS:APP Ch. 3.7
  3. Breakpoints vs. Watchpoints
    • What triggers each type of stop?
    • When should you use a breakpoint only?

5.5 Questions to Guide Your Design

Before implementing, think through these:

  1. Where should you place a breakpoint to see the loop initialize?
  2. How can you verify you are inside greet without reading source?
  3. What do you expect bt to show before and after stepping into greet?

5.6 Thinking Exercise

Trace the Call Stack

Draw the stack as you step into greet. Which frame is on top? Which frame is below it? How does the call stack change when you return?

5.7 The Interview Questions They’ll Ask

Prepare to answer these:

  1. “What is the difference between next and step in LLDB?”
  2. “Why does bt sometimes show functions you did not call explicitly?”
  3. “What do debug symbols do, and how do they get into the binary?”

5.8 Hints in Layers

Hint 1: Use function breakpoints

(lldb) b greet

Hint 2: Print locals quickly

(lldb) fr v

Hint 3: Use bt after step

You should see greet on the top frame.

5.9 Books That Will Help

Topic Book Chapter
Debug symbols “Linkers and Loaders” Ch. 1-2
Stack frames “Computer Systems: A Programmer’s Perspective” Ch. 3.7
Debugging fundamentals “The Art of Debugging with GDB” Ch. 1-3

5.10 Implementation Phases

Phase 1: Foundation (20 minutes)

Goals:

  • Compile with debug symbols.
  • Launch LLDB and load the binary.

Tasks:

  1. Write target.c.
  2. Compile with clang -g.
  3. Start lldb ./target and set a breakpoint.

Checkpoint: LLDB stops at main and shows the correct source line.

Phase 2: Core Functionality (30 minutes)

Goals:

  • Step over the loop.
  • Inspect variables.

Tasks:

  1. Use next to move through the loop setup.
  2. Inspect i with frame variable i.

Checkpoint: You can explain why i changes as you step.

Phase 3: Polish & Edge Cases (20 minutes)

Goals:

  • Step into greet and confirm the call stack.

Tasks:

  1. Use step on the greet(i) line.
  2. Use bt and frame select.

Checkpoint: You can select the correct frame and read locals.

5.11 Key Implementation Decisions

Decision Options Recommendation Rationale
Breakpoint location main vs greet Start with main See full setup before function call
Stepping method next vs step Use both Compare frame behavior

6. Testing Strategy

6.1 Test Categories

Category Purpose Examples
Manual Debugging Validate LLDB commands b main, n, s, fr v
Sanity Check Ensure program output matches expectation ./target prints 5 lines

6.2 Critical Test Cases

  1. Breakpoint hits main: LLDB stops at line containing main body.
  2. Step into greet: bt shows greet on top frame.
  3. Variable inspection: i and count show correct values.

6.3 Test Data

Loop runs 5 iterations (0..4)

7. Common Pitfalls & Debugging

7.1 Frequent Mistakes

Pitfall Symptom Solution
Forgot -g LLDB shows assembly only Recompile with -g
Confusing s/n Unexpected frame changes Use help thread step-in
Optimizations on Variables seem optimized away Compile with -O0 -g

7.2 Debugging Strategies

  • Check symbol load: image list and target modules list.
  • Verify line mapping: list to confirm source lines.

7.3 Performance Traps

Not applicable for this project.


8. Extensions & Challenges

8.1 Beginner Extensions

  • Add a second helper function and step through both.
  • Use a conditional breakpoint: b main -c 'i == 3'.

8.2 Intermediate Extensions

  • Use thread backtrace all to see all threads.
  • Compare stepping in optimized vs. unoptimized builds.

8.3 Advanced Extensions

  • Use disassemble --frame to connect source to assembly.
  • Write a short LLDB command script that sets your standard breakpoints.

9. Real-World Connections

9.1 Industry Applications

  • Crash triage: Quickly stop, inspect, and report repro steps.
  • Performance hot spots: Step through code to confirm expected path.
  • LLDB: https://lldb.llvm.org - The debugger you are using.
  • LLVM: https://llvm.org - Toolchain context for LLDB.

9.3 Interview Relevance

  • Breakpoints, stepping, and call stacks are universal debugging topics.

10. Resources

10.1 Essential Reading

  • The LLDB Tutorial - https://lldb.llvm.org/use/tutorial.html
  • The Art of Debugging with GDB by Norman Matloff - Ch. 1-3

10.2 Video Resources

  • LLDB basics walkthrough - LLDB YouTube channel

10.3 Tools & Documentation

  • LLDB Command Reference: https://lldb.llvm.org/use/command.html

11. Self-Assessment Checklist

11.1 Understanding

  • I can explain how LLDB uses DWARF symbols to map source lines.
  • I can describe the target/process/thread/frame model.
  • I can explain the difference between next and step.

11.2 Implementation

  • All breakpoints and steps behave as expected.
  • I can inspect locals and the call stack.
  • I can repeat the debugging session without notes.

11.3 Growth

  • I can describe my debugging workflow in a job interview.
  • I can teach a teammate the LLDB basics.

12. Submission / Completion Criteria

Minimum Viable Completion:

  • Compile with -g and run LLDB.
  • Break at main and step into greet.
  • Print i and confirm the call stack.

Full Completion:

  • Use a conditional breakpoint and explain why it works.
  • Demonstrate stepping into and out of frames.

Excellence (Going Above & Beyond):

  • Connect source lines to assembly using disassemble.
  • Record your LLDB session commands as a reusable script.

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