| name | gdb-attach |
| description | Use when debugging the Breenix kernel at assembly or C-level using GDB - investigating CPU exceptions, page faults, triple faults, examining register state during interrupt handling, stepping through boot sequence, analyzing syscall entry/exit paths, debugging context switches, or inspecting memory layout and page tables. |
Breenix GDB Debugging
When to Use This Skill
Use this skill when you need to debug the Breenix kernel at the assembly or C-level using GDB. Common scenarios:
- Investigating CPU exceptions, page faults, or triple faults
- Examining register state during interrupt handling
- Stepping through boot sequence or early initialization
- Analyzing syscall entry/exit paths
- Debugging context switches and process state transitions
- Inspecting memory layout and page tables
- Understanding TSC/APIC timer behavior
Quick Start
1. Launch QEMU in GDB Mode
# Set the GDB flag and run the kernel
BREENIX_GDB=1 cargo run --release --bin qemu-uefi
This will:
- Start QEMU with
-s -S(GDB server on localhost:1234, paused) - Wait for GDB to connect before executing any code
- Print connection instructions
2. Connect GDB (in another terminal)
# Connect to the running QEMU instance
gdb target/x86_64-breenix/release/kernel -ex 'target remote localhost:1234'
Or use the helper command from .gdbinit:
gdb target/x86_64-breenix/release/kernel
(gdb) breenix-connect
Essential GDB Commands for Kernel Debugging
Navigation & Execution
# Continue execution
c
# Step one instruction (into calls)
si
# Step one instruction (over calls)
ni
# Step one source line
s
# Step over source line
n
# Finish current function
finish
Breakpoints
# Hardware breakpoint (works before paging is set up)
hbreak kernel_main
# Software breakpoint (requires memory to be mapped)
break rust_syscall_handler
break timer_interrupt_handler
break process::manager::spawn_process
# Conditional breakpoint
break syscall_handler if $rax == 0x1 # Only break on specific syscall
# List breakpoints
info breakpoints
# Delete breakpoint
delete 1
Registers & State
# Show all general-purpose registers
info registers
# Show specific register
print $rip
print/x $rsp
print/x $cr3
# Show segment registers
info registers cs ds ss fs gs
# Custom helper to show segments nicely
show-segments
Memory Inspection
# Examine memory (format: x/nfu addr)
# n=count, f=format (x=hex, d=decimal, s=string), u=unit (b=byte, h=halfword, w=word, g=giant/8-bytes)
x/16xg $rsp # Show 16 8-byte values at stack pointer
x/32xb 0xffff800000000000 # Show 32 bytes at kernel base
x/s 0xsomeaddr # Show null-terminated string
x/i $rip # Show instruction at program counter
# Display memory continuously as you step
display/16xg $rsp
Backtraces & Frames
# Show call stack
backtrace
bt
# Show detailed backtrace with local variables
backtrace full
# Move between stack frames
frame 0
frame 1
# Show local variables in current frame
info locals
# Show function arguments
info args
Symbols & Source
# List source code around current location
list
# Show disassembly around current instruction
disassemble
# Show disassembly of specific function
disassemble kernel_main
disassemble rust_syscall_handler
# Show type information
ptype some_variable
Common Debugging Scenarios
Scenario 1: Boot Debugging
Set breakpoint at kernel entry and step through initialization:
(gdb) hbreak kernel_main
(gdb) c
(gdb) layout asm # Show assembly view
(gdb) si # Step through boot sequence
(gdb) info registers
Scenario 2: Syscall Debugging
Debug a specific syscall (e.g., clock_gettime):
(gdb) break rust_syscall_handler
(gdb) c
# When syscall hits:
(gdb) print/x $rax # Syscall number
(gdb) print/x $rdi # First argument
(gdb) print/x $rsi # Second argument
(gdb) s # Step into handler
Scenario 3: Page Fault Investigation
Examine CPU state on page fault:
(gdb) break page_fault_handler
(gdb) c
# When fault occurs:
(gdb) print/x $cr2 # Faulting address
(gdb) print/x $rip # Instruction that faulted
(gdb) x/i $rip # Show the faulting instruction
(gdb) print error_code # Error code (if captured)
(gdb) backtrace
Scenario 4: Timer Interrupt Debugging
Trace timer interrupt flow:
(gdb) break timer_interrupt_handler
(gdb) c
# First timer interrupt hits:
(gdb) print ticks # Check tick counter
(gdb) s # Step into APIC EOI
(gdb) finish # Return from handler
(gdb) c # Continue to next tick
Scenario 5: Context Switch Analysis
Debug process switching:
(gdb) break context_switch::switch_to
(gdb) c
# When switching:
(gdb) print from_process
(gdb) print to_process
(gdb) print/x $cr3 # Current page table
(gdb) s # Step through switch
(gdb) print/x $cr3 # New page table
Breenix-Specific Helpers
The .gdbinit file provides custom commands:
# Connect to QEMU
breenix-connect
# Show segment registers nicely
show-segments
# Set common kernel breakpoints
breenix-breaks
Advanced Techniques
Watchpoints (Memory Access Breakpoints)
# Break when memory location is written
watch *0xffff800000010000
# Break when memory location is read
rwatch some_global_variable
# Break on read or write
awatch some_global_variable
Examining Page Tables
# Get CR3 (page table root)
print/x $cr3
# Walk page table manually (requires understanding x86_64 paging)
x/4xg ($cr3 & ~0xfff) # PML4 entries
TSC Debugging
# Read TSC register value
print $ia32_tsc # May not be directly accessible
# Instead, examine TSC handling code:
break tsc::read_tsc
Tips & Gotchas
- Use hardware breakpoints early: Before paging is fully set up, use
hbreakinstead ofbreak - Serial output interference: GDB traffic and serial logs both compete for terminal output
- Optimization can confuse stepping: Release builds may inline or reorder code
- No symbols for assembly: Some early boot code won't have Rust symbols
- Context switches reset state: Watch out for process switching changing $cr3, $rsp, etc.
Exiting GDB
# Quit GDB (QEMU will also stop)
quit
# Detach but leave QEMU running
detach
Integration with Existing Workflow
This GDB debugging flow complements the existing log-based debugging:
- Use logs for: High-level flow, timing issues, multi-test scenarios
- Use GDB for: Precise state inspection, assembly-level debugging, crash analysis
You can combine both: run with logs first to narrow down the issue, then use GDB to investigate the exact instruction or register state.