# Debugging Guide Comprehensive guide to debugging ModuOS. ## Debugging Tools ### Serial Port Logging (COM) **Primary method** for debugging kernel code. **Setup**: Automatically enabled in `run.bat` **Viewing Logs**: ```bash # View in real-time tail -f com1.log tail -f com2.log # Or use Log Viewer (Windows) vendor/NTSoftware/Log Viewer.exe com1.log ``` **Adding Debug Output**: ```c #include "moduos/kernel/COM/com.h" COM_LOG_INFO(COM1_PORT, "Starting initialization"); COM_LOG_DEBUG(COM1_PORT, "Variable value: %d", value); COM_LOG_ERROR(COM1_PORT, "Failed to initialize"); ``` **Log Levels**: - `COM_LOG_INFO`: General information - `COM_LOG_DEBUG`: Detailed debug info - `COM_LOG_WARN`: Warnings - `COM_LOG_ERROR`: Errors ### VGA Text Output **For early boot** before COM is initialized: ```c #include "moduos/drivers/graphics/VGA.h" VGA_print("Debug message\n"); VGA_print_hex(value); ``` ### QEMU Monitor **Access**: Press `Ctrl+Alt+2` in QEMU window **Useful Commands**: ``` info registers # Show all CPU registers info mem # Memory mappings info pic # PIC status info tlb # TLB entries x/10i $rip # Disassemble 10 instructions x/16x 0xaddress # Dump memory ``` **Switch back to console**: `Ctrl+Alt+1` ## GDB Debugging ### Setup **Start QEMU with GDB server**: ```bash qemu-system-x86_64 -cdrom dist/AMD64/kernel.iso -s -S ``` **Options**: - `-s`: Enable GDB server on port 1234 - `-S`: Pause CPU at startup **Connect GDB**: ```bash gdb dist/AMD64/mdsys.sqr (gdb) target remote localhost:1234 (gdb) break kernel_main (gdb) continue ``` ### Useful GDB Commands ``` # Breakpoints break kernel_main # Set breakpoint at function break *0x100000 # Set breakpoint at address info breakpoints # List breakpoints delete 1 # Delete breakpoint 1 # Execution continue (c) # Continue execution step (s) # Step into next (n) # Step over finish # Step out # Inspection print variable # Print variable value print /x variable # Print in hex x/10i $rip # Disassemble x/16x address # Dump memory backtrace (bt) # Stack trace info registers # Show registers # Memory x/10gx $rsp # Show stack (10 qwords) watch variable # Watchpoint on write ``` ### GDB with Symbols Compile with debug symbols: ```makefile CFLAGS += -g ``` This allows GDB to show source code and variable names. ## Common Debugging Scenarios ### Kernel Panic **Symptom**: System halts with panic message **Debug**: 1. Check VGA output for panic message 2. Review `com1.log` for stack trace 3. Note the faulting address and registers **Example Panic**: ``` KERNEL PANIC: Page Fault Faulting address: 0x0000000000000000 Error code: 0x0002 (Write to non-present page) RIP: 0xFFFFFFFF80100123 ``` **Investigation**: ```bash # Find function at RIP objdump -d dist/AMD64/mdsys.sqr | grep 80100123 ``` ### Triple Fault **Symptom**: QEMU resets immediately **Causes**: - Invalid IDT entry - Stack overflow - Page fault in fault handler - Invalid page tables **Debug**: 1. Add `-d int,cpu_reset` to QEMU 2. Check `qemu.log` for fault details 3. Use GDB to step through early boot ### Hang/Freeze **Symptom**: System stops responding **Debug**: 1. Check if interrupts are enabled: `sti` called? 2. Look for infinite loops 3. Check if waiting for hardware that never responds **GDB Method**: ```bash # Attach GDB to running instance gdb dist/AMD64/mdsys.sqr (gdb) target remote localhost:1234 (gdb) interrupt (gdb) backtrace ``` ### Memory Corruption **Symptom**: Random crashes, data corruption **Debug**: 1. Check for buffer overflows 2. Verify malloc/free pairs 3. Check stack alignment 4. Use memory debugging **Add Memory Guards**: ```c #define GUARD_VALUE 0xDEADBEEF void *kmalloc_debug(size_t size) { void *ptr = kmalloc(size + 8); *(uint32_t*)ptr = GUARD_VALUE; *(uint32_t*)(ptr + 4 + size) = GUARD_VALUE; return ptr + 4; } void kfree_debug(void *ptr) { ptr -= 4; assert(*(uint32_t*)ptr == GUARD_VALUE); kfree(ptr); } ``` ### Page Fault **Error Code Bits**: - Bit 0: Page present (0=not present, 1=protection) - Bit 1: Write access (0=read, 1=write) - Bit 2: User mode (0=kernel, 1=user) - Bit 3: Reserved bit violation - Bit 4: Instruction fetch **Handler**: ```c void page_fault_handler(registers_t *regs) { uint64_t cr2; __asm__ volatile("mov %%cr2, %0" : "=r"(cr2)); uint32_t err = regs->err_code; COM_LOG_ERROR(COM1_PORT, "Page Fault at 0x%llx", cr2); COM_LOG_ERROR(COM1_PORT, "Present: %d", err & 1); COM_LOG_ERROR(COM1_PORT, "Write: %d", (err >> 1) & 1); COM_LOG_ERROR(COM1_PORT, "User: %d", (err >> 2) & 1); } ``` ## Debugging Techniques ### Printf Debugging **Simple but effective**: ```c COM_LOG_DEBUG(COM1_PORT, "Reached checkpoint 1"); COM_LOG_DEBUG(COM1_PORT, "Variable x = %d", x); ``` ### Binary Search **For isolating bugs**: 1. Add debug print at middle of suspicious code 2. If bug occurs before print, search first half 3. If bug occurs after print, search second half 4. Repeat until bug location found ### Assertion **Catch invalid states early**: ```c #define assert(expr) \ if (!(expr)) panic("Assertion failed: " #expr) assert(ptr != NULL); assert(size > 0); assert(index < array_length); ``` ### Memory Dump **Inspect memory contents**: ```c void hexdump(void *addr, size_t len) { uint8_t *ptr = (uint8_t*)addr; for (size_t i = 0; i < len; i += 16) { COM_LOG_DEBUG(COM1_PORT, "%p: ", ptr + i); for (size_t j = 0; j < 16 && i + j < len; j++) { COM_LOG_DEBUG(COM1_PORT, "%02x ", ptr[i + j]); } COM_LOG_DEBUG(COM1_PORT, "\n"); } } ``` ## QEMU Debugging Flags ### Enhanced Debug Output ```bash qemu-system-x86_64 \ -cdrom kernel.iso \ -d int,cpu_reset,guest_errors \ -D qemu.log ``` **Options**: - `-d int`: Log all interrupts - `-d cpu_reset`: Log CPU resets (triple faults) - `-d guest_errors`: Log guest errors - `-D qemu.log`: Output to file ### No Reboot ```bash qemu-system-x86_64 -cdrom kernel.iso -no-reboot ``` Prevents automatic restart on triple fault, shows error state. ## Stack Traces ### Manual Stack Walk ```c void print_stack_trace(void) { uint64_t *rbp; __asm__ volatile("mov %%rbp, %0" : "=r"(rbp)); for (int i = 0; i < 10 && rbp; i++) { uint64_t rip = *(rbp + 1); COM_LOG_DEBUG(COM1_PORT, " [%d] RIP: 0x%llx", i, rip); rbp = (uint64_t*)*rbp; } } ``` ### Objdump Symbols ```bash # Find function containing address objdump -d dist/AMD64/mdsys.sqr | grep -B5 address # Disassemble specific function objdump -d dist/AMD64/mdsys.sqr | grep -A20 "kernel_main:" ``` ## Performance Debugging ### Timing Code ```c uint64_t rdtsc(void) { uint32_t lo, hi; __asm__ volatile("rdtsc" : "=a"(lo), "=d"(hi)); return ((uint64_t)hi << 32) | lo; } uint64_t start = rdtsc(); // ... code to measure ... uint64_t end = rdtsc(); COM_LOG_DEBUG(COM1_PORT, "Cycles: %llu", end - start); ``` ## Best Practices 1. **Add logging early**: Easier to remove than to add later 2. **Use descriptive messages**: Include context and values 3. **Check return values**: Don't ignore errors 4. **Validate inputs**: Assert preconditions 5. **Use version control**: Git bisect to find regressions 6. **Test incrementally**: Small changes are easier to debug 7. **Keep backups**: Known-good versions to compare against ## Next Steps - [Building ModuOS](Building-ModuOS.md) - Build with debug symbols - [Running and Testing](Running-and-Testing.md) - Testing strategies - [Contributing](Contributing.md) - Submitting bug fixes