Repository navigation
Debugging
cupid-os provides extensive debugging facilities through serial logging, memory safety checks, and introspection commands.
All kernel debug output is sent to COM1 at 115200 baud, making it visible in QEMU's terminal or any serial capture tool.
| Parameter | Value |
|---|---|
| Port | COM1 (0x3F8) |
| Baud rate | 115200 |
| Data bits | 8 |
| Stop bits | 1 |
| Parity | None |
qemu-system-i386 -serial stdio ...This prints all kernel debug output to your host terminal.
Four severity levels are available, defined in kernel/debug.h:
| Macro | Level | Prefix | Usage |
|---|---|---|---|
KDEBUG(fmt, ...) |
Verbose | [DEBUG] |
Detailed tracing |
KINFO(fmt, ...) |
Info | [INFO] |
Status messages |
KWARN(fmt, ...) |
Warning | [WARN] |
Non-critical issues |
KERROR(fmt, ...) |
Error | [ERROR] |
Serious problems |
KDEBUG("Process %u switching to %u", old_pid, new_pid);
KINFO("FAT16 filesystem mounted, %u clusters", total_clusters);
KWARN("Block cache full, evicting LRU entry");
KERROR("Double free detected at %p", ptr);The runtime log level can be changed with the loglevel shell command:
> loglevel 0 # Show all messages (DEBUG and above)
> loglevel 2 # Show WARN and ERROR only
> loglevel 3 # Show ERROR only
The kernel maintains a circular log buffer in memory. Use logdump to view recent log messages:
> logdump
[INFO] VGA initialized: 320x200x256
[INFO] PS/2 mouse initialized
[DEBUG] FAT16: Reading cluster 42
[WARN] Block cache eviction triggered
cupid-os implements multiple layers of memory protection to catch bugs early.
Every allocation from kmalloc() is wrapped with canary values:
┌─────────────┬──────────────────┬─────────────┐
│ HEAD CANARY │ User Data │ TAIL CANARY │
│ 0xDEADBEEF │ (requested sz) │ 0xBEEFDEAD │
└─────────────┴──────────────────┴─────────────┘
-
Head canary:
0xDEADBEEF- placed before user data -
Tail canary:
0xBEEFDEAD- placed after user data - Checked on every
kfree()call - Buffer overflows and underflows corrupt canaries → detected and reported
When memory is freed via kfree():
- The entire freed block is filled with
0xFEFEFEFE - Any use-after-free that reads this memory gets a recognizable poison pattern
- Helps identify stale pointer bugs
The allocator tracks:
- Total allocations made
- Total frees performed
- Currently active allocations
- Peak memory usage
| Command | Description |
|---|---|
memdump <addr> [len] |
Hex dump of memory at address |
memstats |
Show allocation statistics |
memleak |
Report suspected memory leaks |
memcheck |
Validate all active heap allocations |
> memdump 0x100000 64
00100000: 48 65 6C 6C 6F 20 57 6F 72 6C 64 00 00 00 00 00 Hello World.....
00100010: FE FE FE FE FE FE FE FE FE FE FE FE FE FE FE FE ................
> memstats
Heap Statistics:
Total allocations: 342
Total frees: 318
Active allocations: 24
Heap start: 0x00200000
Heap end: 0x00800000
Lists all active allocations that haven't been freed, helping identify leaks:
> memleak
Active allocations:
0x00201000 size=4096 (stack)
0x00202000 size=1024 (buffer)
...
Walks every active allocation and verifies canary integrity:
> memcheck
Checking all active allocations...
All 24 allocations OK
Or if corruption is detected:
> memcheck
CORRUPTION: allocation at 0x00205000 - tail canary damaged!
Every process stack has a canary at the bottom:
| Value | Location | Purpose |
|---|---|---|
0xDEADC0DE |
Bottom of stack | Detect stack overflow |
Checked on every context switch. If the canary is corrupted → kernel panic identifying the process.
The crashtest command deliberately triggers various failure modes for testing:
| Subcommand | What It Does |
|---|---|
crashtest divzero |
Division by zero (INT 0) |
crashtest nullptr |
Null pointer dereference |
crashtest overflow |
Stack overflow via infinite recursion |
crashtest gpf |
General protection fault |
crashtest assert |
Failed assertion |
crashtest panic |
Manual kernel panic |
> crashtest nullptr
*** KERNEL PANIC ***
Page Fault at address 0x00000000
EIP: 0x00102ABC
Process: desktop (PID 2)
System halted.
| Command | Description |
|---|---|
stacktrace |
Show current call stack |
registers |
Dump CPU register state |
sysinfo |
Show OS version, uptime, memory |
logdump |
Print log buffer contents |
loglevel <n> |
Set log verbosity (0–3) |
> registers
EAX=00000001 EBX=00103000 ECX=00000010 EDX=000003F8
ESI=00000000 EDI=00200000 EBP=001FFFF0 ESP=001FFFE0
EIP=00102345 EFLAGS=00000202
> sysinfo
cupid-os v0.1.0
Uptime: 42 seconds
Processes: 3/32
Memory: 24 active allocations
PIT frequency: 100 Hz
When an unrecoverable error occurs, the kernel panic handler:
- Disables interrupts (
CLI) - Prints panic message with location info
- Dumps register state
- Dumps stack trace (if available)
- Halts the CPU (
HLTin infinite loop)
Panics can be triggered by:
- Unhandled exceptions (page fault, GPF, divide-by-zero)
- Failed assertions (
KASSERT()macro) - Detected corruption (canary violation, stack overflow)
- Manual trigger (
kernel_panic())
KASSERT(condition, "message");
// Example:
KASSERT(ptr != NULL, "Null pointer in process_create");If the condition is false → kernel panic with file, line, and message.
- Architecture - System memory layout
- Process Management - Stack canaries and process table
- Shell Commands - Full command reference
Getting Started
Shell & Scripting
Compilers
- CupidC Compiler
- CupidC Language Reference
- CupidC 2D Graphics Library
- CupidASM Assembler
- Toolchain Bootstrap
- Floating Point
Programs & Docs
GUI
Kernel
Filesystems
Hardware