Claude-skill-registry 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.

install
source · Clone the upstream repo
git clone https://github.com/majiayu000/claude-skill-registry
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/majiayu000/claude-skill-registry "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/data/gdb-attach" ~/.claude/skills/majiayu000-claude-skill-registry-gdb-attach && rm -rf "$T"
manifest: skills/data/gdb-attach/SKILL.md
source content

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

  1. Use hardware breakpoints early: Before paging is fully set up, use
    hbreak
    instead of
    break
  2. Serial output interference: GDB traffic and serial logs both compete for terminal output
  3. Optimization can confuse stepping: Release builds may inline or reorder code
  4. No symbols for assembly: Some early boot code won't have Rust symbols
  5. 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.