Debugging

Serial console, QEMU debugging, GDB integration, crash analysis, and self-healing diagnostics.

Documentation

Debug Architecture

Helix provides a comprehensive debugging infrastructure that works from early boot through full runtime operation. The debug stack spans hardware (serial port, GDB stub), kernel (logging macros, panic handler), and tooling (QEMU, GDB, objdump).

Debug Architecture — Stack4N · 3E
GDB protocolserial / debugoutputHost Debugging ToolsDeveloper machine1QEMU Debug InterfaceVirtual machine2Kernel Debug LayerRuntime logging2HAL Debug SupportHardware access1
100%
☝ Drag to pan·🤏 Pinch to zoom·Tap a node

Debug Build

Build the kernel with full debug information:

# Debug build (opt-level 0, full debug symbols)
make build-debug

# Or directly with cargo
cargo build --target x86_64-unknown-none

Debug Profile Settings

[profile.dev]
opt-level = 0          # No optimization — code maps directly to source
lto = false            # No link-time optimization
debug = true           # Full DWARF debug info
panic = "abort"        # No unwinding
codegen-units = 256    # Fast compilation

Release + Debug Symbols

For debugging performance issues in optimized code:

[profile.release-debug]
inherits = "release"
debug = true           # Keep debug symbols with release optimizations
# Build with release-debug profile
cargo build --profile release-debug --target x86_64-unknown-none

Serial Console

The serial console is the primary debug output channel. It's available from the earliest boot stage.

Hardware Setup

ParameterValue
PortCOM1 (0x3F8) on x86_64
Baud rate115200
Data bits8
ParityNone
Stop bits1
Flow controlNone

Kernel Logging Macros

core/src/debug/console.rs
rust
1
// Always-on output
2
kprint!("value: {}", x); // No newline
3
kprintln!("boot complete"); // With newline
4
5
// Level-gated logging (compiled out in release for kdebug!)
6
kdebug!("allocator: free frames = {}", count); // Debug only
7
kinfo!("scheduler: thread {} started", tid); // Informational
8
kwarn!("memory: low watermark reached"); // Warning
9
kerror!("syscall: invalid number {}", num); // Error

Each macro automatically includes:

  • Timestamp (ticks since boot)
  • Source location (file:line) in debug builds
  • Log level prefix ([DBG], [INF], [WRN], [ERR])

Example Output

[    0.000] [INF] Helix OS v0.1.0
[    0.001] [INF] CPU: x86_64, 4 cores
[    0.002] [INF] Memory: 128 MB total, 124 MB free
[    0.003] [DBG] Bitmap allocator: 32768 frames tracked
[    0.005] [INF] IDT: 256 vectors configured
[    0.006] [INF] APIC: Local APIC at 0xFEE00000
[    0.010] [INF] Scheduler: Round-robin, 10ms quantum
[    0.015] [INF] HelixFS: mounted at /
[    0.020] [INF] Modules: 3 loaded (round_robin, nexus, benchmarks)
[    0.025] [INF] Userspace: shell ready
helix>

QEMU Debugging

QEMU is the primary testing environment for Helix. The build system provides several debugging options.

Launch Commands

# Normal run — serial output to terminal
make qemu

# Debug mode — GDB server on port 1234, waits for connection
make qemu-debug

# Manual QEMU command with full options
qemu-system-x86_64 \
    -kernel build/output/helix-kernel \
    -serial stdio \
    -m 128M \
    -smp 4 \
    -no-reboot \
    -no-shutdown \
    -d int,cpu_reset \
    -D build/logs/qemu.log

QEMU Flags Reference

FlagPurpose
-kernel <path>Boot directly from kernel ELF (no bootloader)
-serial stdioRedirect serial port to terminal
-m 128MGuest RAM size
-smp 4Number of CPU cores
-no-rebootDon't reboot on triple fault — halt instead
-no-shutdownKeep QEMU open after guest shutdown
-sStart GDB server on port 1234
-SFreeze CPU at startup (wait for GDB continue)
-d int,cpu_resetLog interrupts and CPU resets
-D <logfile>Write QEMU debug output to file
-monitor stdioQEMU monitor console (instead of serial)
-display noneHeadless mode (serial only)
-enable-kvmHardware acceleration (Linux host)

QEMU Monitor Commands

Access the QEMU monitor with Ctrl+A, C:

CommandPurpose
info registersDump all CPU registers
info memShow page table mappings
info mtreeMemory region tree
info cpusCPU state (running/halted)
xp /16xg 0xFFFFFFFF80000000Examine memory (16 giant words, hex)
gdbserverStart GDB server if not already running
quitExit QEMU

GDB Integration

GDB connects to QEMU's GDB stub for full source-level debugging.

Setup

# Terminal 1: Start QEMU with GDB server
make qemu-debug

# Terminal 2: Connect GDB
gdb build/output/helix-kernel
(gdb) target remote :1234
(gdb) break kernel_main
(gdb) continue

VS Code Launch Configuration

The workspace includes a VS Code debug configuration:

{
    "name": "Debug Kernel (QEMU)",
    "type": "cppdbg",
    "request": "launch",
    "program": "${workspaceFolder}/build/output/helix-kernel",
    "miDebuggerServerAddress": "localhost:1234",
    "miDebuggerPath": "gdb",
    "setupCommands": [
        { "text": "set architecture i386:x86-64" },
        { "text": "symbol-file ${workspaceFolder}/build/output/helix-kernel" }
    ]
}

Useful GDB Commands

CommandPurpose
break kernel_mainBreak at kernel entry
break *0xFFFFFFFF80001234Break at address
break hal/src/x86_64/idt.rs:42Break at source line
info registersShow CPU registers
x/16xg $rspExamine stack (16 giant words)
btBacktrace
frame 3Select stack frame
print variable_namePrint variable value
watch *0xFFFFFFFF80010000Hardware watchpoint
layout srcTUI source view
layout asmTUI assembly view
stepiStep one instruction
nextiStep over one instruction

Conditional Breakpoints

# Break on page fault
break hal/src/x86_64/idt.rs:page_fault_handler
condition 1 error_code & 0x1 == 0   # Only on not-present faults

# Break when thread ID matches
break execution/src/scheduler.rs:next_thread
condition 2 current_thread.id == 42

Crash Analysis

When the kernel panics, the panic handler provides structured crash information.

Panic Output Format

Panic Output — Structure5N · 4E
KERNEL PANICPanic banner1Message + LocationError message + file…2Register DumpAll CPU registers2Stack TraceCall chain2ActionHalt / Reboot / Debu…1
100%
☝ Drag to pan·🤏 Pinch to zoom·Tap a node

Analyzing a Crash

  1. Read the panic message — usually tells you exactly what went wrong
  2. Check the location — file:line:column of the panic
  3. Examine the stack trace — trace the call chain back to the root cause
  4. Check registers — RAX often contains the return value, RIP is the instruction pointer
  5. Use addr2line for addresses without symbols:
addr2line -e build/output/helix-kernel 0xFFFFFFFF80023456
# Output: core/src/orchestrator.rs:142

Common Panic Causes

SymptomLikely CauseFix
Page fault in kernelNull pointer dereference or invalid addressCheck pointer validity
Double faultStack overflowIncrease kernel stack size (16 KiB default)
General protection faultMisaligned access or privilege violationCheck memory alignment
Triple fault (reboot)IDT not set up or corruptVerify GDT/IDT initialization
Assertion failureLogic errorRead the assertion message

Memory Debugging

Guard Pages

Kernel stacks are surrounded by unmapped guard pages. A stack overflow triggers a page fault on the guard page instead of silently corrupting adjacent memory:

Guard Page — Stack Memory Layout3N · 2E
Guard Page (top)Unmapped — page faul…1Kernel Stack16 KiB usable stack2Guard Page (bottom)Unmapped — page faul…1
100%
☝ Drag to pan·🤏 Pinch to zoom·Tap a node

Memory Leak Detection

In debug builds, the heap allocator tracks all allocations:

subsystems/memory/src/allocator/mod.rs
rust
1
#[cfg(debug_assertions)]
pub fn dump_allocations() {
3
// Print all live allocations with their source location
4
// Useful for finding memory leaks
5
}
Index

Allocation Poisoning

In debug builds:

  • Freed memory is filled with 0xDEADBEEF — use-after-free shows up as obvious bad data
  • Uninitialized memory is filled with 0xCDCDCDCD — uninitialized reads show up clearly
  • Guard bytes surround allocations — buffer overflows corrupt the guard bytes

Debug Tools

Binary Analysis

# Show kernel binary size breakdown
make size
# Output:
#    text    data     bss     dec     hex filename
#  245760   16384   32768  294912   48000 build/output/helix-kernel

# Disassemble the kernel
objdump -d build/output/helix-kernel | less

# Disassemble a specific function
objdump -d build/output/helix-kernel | grep -A 50 '<kernel_main>:'

# Show all symbols sorted by size
nm --size-sort build/output/helix-kernel | tail -20

# Show section sizes
size -A build/output/helix-kernel

QEMU Trace Events

# Trace all interrupt-related events
qemu-system-x86_64 ... -d int -D qemu_trace.log

# Trace CPU exceptions
qemu-system-x86_64 ... -d cpu_reset,int -D qemu_trace.log

# Trace memory accesses (very verbose)
qemu-system-x86_64 ... -d mmu -D qemu_trace.log

# Trace specific QEMU trace events
qemu-system-x86_64 ... -trace 'apic_*' -D qemu_trace.log

Kernel Log Levels

LevelMacroCompiled InDescription
Errorkerror!AlwaysUnrecoverable or severe errors
Warnkwarn!AlwaysPotential problems
Infokinfo!AlwaysSignificant events
Debugkdebug!Debug onlyDetailed diagnostics
Tracektrace!Debug onlyVery verbose, per-operation

In release builds, kdebug! and ktrace! compile to no-ops (zero overhead).

Performance Profiling

# Show time spent in each function (requires debug symbols)
# Use QEMU's -plugin mechanism:
qemu-system-x86_64 ... -plugin libinsn.so -d plugin -D profile.log

# Or use the built-in benchmark system:
helix> bench context_switch
helix> bench memory_alloc
helix> stats