Phase 0: Debugging Tools

Objective

Before writing complex C, learn to observe what programs do at the system level. These tools reveal the truth — not what you think your code does, but what it actually does.

xxd — Hex Dump

See the raw bytes of any file:

# Your compiled binary — machine code
xxd hello | head -20

# A text file — ASCII values
echo "Hello" > test.txt
xxd test.txt

xxd shows you that everything is bytes. Text files are bytes. Binaries are bytes. The only difference is interpretation.

strace — System Call Trace

Every program talks to the kernel via syscalls. strace shows every one:

strace ./hello

Look for:

  • execve — the program starts

  • openat — opening files (including shared libraries)

  • mmap — mapping memory

  • write(1, "Hello from C\n", 14) — THIS is your printf. fd 1 = stdout, 14 bytes written.

  • exit_group(0) — program exits with code 0

# Count syscalls by type
strace -c ./hello

# Only show write syscalls
strace -e trace=write ./hello

# Follow child processes
strace -f ./hello

ltrace — Library Call Trace

One level above syscalls — traces C library function calls:

ltrace ./hello

You’ll see printf("Hello from C\n") — the library call. strace shows the write() syscall underneath it.

gdb — GNU Debugger (basics)

Compile with -g first:

gcc -Wall -g hello.c -o hello
gdb ./hello

Inside gdb:

break main        # Set breakpoint at main()
run               # Start the program
next              # Step one line
print argc        # Print a variable
continue          # Run to next breakpoint or end
quit              # Exit gdb

Exercises

  1. [ ] Run strace ./hello — find the write syscall. What file descriptor? How many bytes?

  2. [ ] Run strace -c ./hello — which syscall is called most?

  3. [ ] Run ltrace ./hello — find the printf call

  4. [ ] Run strace cat /etc/hostname — find the openat and read syscalls for the file

  5. [ ] Compile with -g, run gdb ./hello, set a breakpoint at main, step through

Notes

Write your observations here.