gdb-attach
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.
$ インストール
git clone https://github.com/ryanbreen/breenix /tmp/breenix && cp -r /tmp/breenix/breenix-gdb-attach ~/.claude/skills/breenix// tip: Run this command in your terminal to install the skill
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.
