Search

PythonReactSEOPDF
GitHub Skill

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.

ryanbreen☆ 6 Repository stars0 Repository forks

Installation

Follow the repository instructions for this skill and your agent. Review the files and dependencies before adding it to your workflow.

View source ↗Guides

Skill instructions

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.

Examples

Read the usage examples in the source repository.

View source ↗

Source history

Indexed on
2026-01-20
Index updated
2026-01-26
Branch
main

Index and sync dates describe this directory. View the source repository for code history.

GitHub ↗

chat Comments (0)

chat_bubble_outline

No comments yet. Be the first to share your thoughts!