refactor: extract melonDS GDB debugging procedures into melonds-gdb-debugger skill
Create the workspace skill .agents/skills/melonds-gdb-debugger/ with SKILL.md and diagnose_melonds.sh diagnostic automation script. Streamline AGENTS.md to link to the new skill to keep it short and concise. Co-authored-by: Antigravity <antigravity@google.com>
This commit is contained in:
1 parent
a819c712dc
commit
f4c77b32d1
3 files changed
+173
-6
No files matched your search
@@ -0,0 +1,90 @@
|
|||||||
|
---
|
||||||
|
name: melonds-gdb-debugger
|
||||||
|
description: >-
|
||||||
|
Attach to and debug Nintendo DS homebrew applications running in melonDS using arm-none-eabi-gdb.
|
||||||
|
Use when diagnosing crashes, freezes, infinite loops, data aborts (__arm_excpt_dabt, __excpt_entry),
|
||||||
|
stack corruption, or setting breakpoints and inspecting registers/memory on the ARM9 target.
|
||||||
|
---
|
||||||
|
|
||||||
|
# melonDS GDB Debugger Access & Diagnostics
|
||||||
|
|
||||||
|
This skill provides step-by-step procedures and recipes for debugging Nintendo DS homebrew applications running inside the melonDS emulator using `arm-none-eabi-gdb`.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1. Prerequisites & Target Setup
|
||||||
|
|
||||||
|
- **Toolchain GDB:** `${DEVKITPRO}/devkitARM/bin/arm-none-eabi-gdb` (or system `arm-none-eabi-gdb`).
|
||||||
|
- **GDB Server Port:** `localhost:3333` (configured in melonDS when GDB stub is enabled in `melonDS.ini`).
|
||||||
|
- **ELF Symbol File:** [`snakePlus.elf`](../../../snakePlus.elf) or [`build-debug/snakePlus.elf`](../../../build-debug/snakePlus.elf).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2. Quick Connection & Non-Interactive Inspection
|
||||||
|
|
||||||
|
To inspect a running or frozen melonDS instance without an interactive prompt:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
arm-none-eabi-gdb snakePlus.elf --batch \
|
||||||
|
-ex "target remote localhost:3333" \
|
||||||
|
-ex "info registers" \
|
||||||
|
-ex "detach" \
|
||||||
|
-ex "quit"
|
||||||
|
```
|
||||||
|
|
||||||
|
To run a diagnosis using the bundled helper script:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
bash .agents/skills/melonds-gdb-debugger/scripts/diagnose_melonds.sh snakePlus.elf
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3. Diagnosing Crashes, Freezes & Data Aborts
|
||||||
|
|
||||||
|
### 3.1 Exception Vectors in Calico / devkitARM
|
||||||
|
When an unhandled ARM exception occurs, the hardware branches to vector handlers in ROM/BIOS or Calico runtime:
|
||||||
|
- **`0x01ff8560` (`__arm_excpt_dabt`):** Data Abort exception handler.
|
||||||
|
- **`0x01ff8594` (`__excpt_entry`):** Common exception entry point executing an infinite loop (`b .`).
|
||||||
|
- **`0x01ff84ec` (`armWaitForIrq + 4`):** Low-power halt state while waiting for VBlank or Timer IRQ. **Normal running state.**
|
||||||
|
|
||||||
|
If melonDS freezes, appears unresponsive, or outputs `attempt to restore CPSR under bad mode`, attach GDB and inspect `$pc`. If `$pc` is at `0x01ff8594`, the CPU hit a fatal exception.
|
||||||
|
|
||||||
|
### 3.2 Extracting the Faulted Instruction & SPSR
|
||||||
|
In Calico's `__excpt_entry`, the exception frame is pushed to memory at `0x02fffd80`:
|
||||||
|
- **Faulted PC (`lr_abt - 8`):** Stored at `0x02fffd98`.
|
||||||
|
- **Faulted SPSR:** Stored at `0x02fffd90`.
|
||||||
|
|
||||||
|
Run the following commands in GDB to identify the offending source line:
|
||||||
|
|
||||||
|
```gdb
|
||||||
|
(gdb) target remote localhost:3333
|
||||||
|
(gdb) x/x 0x02fffd98
|
||||||
|
0x2fffd98: 0x02004284
|
||||||
|
(gdb) info line *0x02004284
|
||||||
|
Line 123 of "/path/to/source/game_input.cpp" starts at address 0x2004280 ...
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3.3 Diagnosing Stack Overflows & Stack Smash
|
||||||
|
In `calico/lib/ds9.ld`, the ARM9 IRQ stack has a total budget of only 256 bytes (`__sp_irq = __sp_usr + 0x100`), growing downward into user stack (`__sp_usr`).
|
||||||
|
- If an interrupt handler (such as `handle_vblank`) calls deep functions or formatting routines (`iprintf`), it clobbers `__sp_usr`.
|
||||||
|
- **Symptom:** In GDB, `$sp` is corrupt or misaligned (e.g. `sp = 0x02fffd8f`, with bit 0 set), or functions crash immediately on register restore / return (`ldmia sp!`).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4. Interactive Debugging Session
|
||||||
|
|
||||||
|
1. **Launch melonDS with ROM:**
|
||||||
|
```bash
|
||||||
|
melonDS snakePlus.nds
|
||||||
|
```
|
||||||
|
2. **Attach GDB:**
|
||||||
|
```bash
|
||||||
|
arm-none-eabi-gdb snakePlus.elf -ex "target remote localhost:3333"
|
||||||
|
```
|
||||||
|
3. **Common GDB Actions:**
|
||||||
|
- **Set Breakpoint:** `b update_frame` or `b source/game_input.cpp:50`
|
||||||
|
- **Continue Execution:** `c`
|
||||||
|
- **Step Instruction / Source:** `step` (or `next`)
|
||||||
|
- **Read Memory / Registers:** `info registers`, `x/16xw $sp`
|
||||||
|
- **Detach Safely (keeps emulator running):** `detach`, then `quit`
|
||||||
@@ -0,0 +1,76 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# diagnose_melonds.sh - Automated diagnostics for melonDS via GDB
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
ELF_FILE="${1:-snakePlus.elf}"
|
||||||
|
|
||||||
|
if [ ! -f "$ELF_FILE" ]; then
|
||||||
|
if [ -f "build-debug/snakePlus.elf" ]; then
|
||||||
|
ELF_FILE="build-debug/snakePlus.elf"
|
||||||
|
elif [ -f "build/snakePlus.elf" ]; then
|
||||||
|
ELF_FILE="build/snakePlus.elf"
|
||||||
|
else
|
||||||
|
echo "[-] Error: ELF file not found: $ELF_FILE"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Locate arm-none-eabi-gdb
|
||||||
|
GDB_BIN="arm-none-eabi-gdb"
|
||||||
|
if [ -n "${DEVKITPRO:-}" ] && [ -x "${DEVKITPRO}/devkitARM/bin/arm-none-eabi-gdb" ]; then
|
||||||
|
GDB_BIN="${DEVKITPRO}/devkitARM/bin/arm-none-eabi-gdb"
|
||||||
|
elif ! command -v "$GDB_BIN" &>/dev/null; then
|
||||||
|
echo "[-] Error: arm-none-eabi-gdb not found on PATH or DEVKITPRO."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "[*] Connecting to melonDS on localhost:3333 using $ELF_FILE..."
|
||||||
|
|
||||||
|
# Run GDB in batch mode to gather CPU state
|
||||||
|
GDB_OUTPUT=$("$GDB_BIN" "$ELF_FILE" --batch \
|
||||||
|
-ex "set confirm off" \
|
||||||
|
-ex "set pagination off" \
|
||||||
|
-ex "target remote localhost:3333" \
|
||||||
|
-ex "info registers" \
|
||||||
|
-ex "x/x 0x02fffd98" \
|
||||||
|
-ex "x/x 0x02fffd90" \
|
||||||
|
-ex "detach" \
|
||||||
|
-ex "quit" 2>&1 || true)
|
||||||
|
|
||||||
|
if echo "$GDB_OUTPUT" | grep -q "Connection refused"; then
|
||||||
|
echo "[-] Could not connect to localhost:3333. Ensure melonDS is running with GDB enabled."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "=== Registers ==="
|
||||||
|
echo "$GDB_OUTPUT" | grep -E "^(r0|r4|r8|r12|sp|lr|pc|cpsr)" || true
|
||||||
|
|
||||||
|
PC=$(echo "$GDB_OUTPUT" | awk '/^pc/ {print $2}')
|
||||||
|
SP=$(echo "$GDB_OUTPUT" | awk '/^sp/ {print $2}')
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "=== CPU Analysis ==="
|
||||||
|
echo "Current PC: $PC"
|
||||||
|
echo "Current SP: $SP"
|
||||||
|
|
||||||
|
if [ "$PC" = "0x1ff8594" ] || [ "$PC" = "0x01ff8594" ]; then
|
||||||
|
echo "[!] CPU is trapped in __excpt_entry (Fatal Exception / Data Abort / Prefetch Abort)!"
|
||||||
|
FAULT_ADDR=$(echo "$GDB_OUTPUT" | awk '/0x2fffd98:/ {print $2}')
|
||||||
|
FAULT_SPSR=$(echo "$GDB_OUTPUT" | awk '/0x2fffd90:/ {print $2}')
|
||||||
|
echo " Faulted Instruction Address (lr_abt - 8): $FAULT_ADDR"
|
||||||
|
echo " Faulted SPSR: $FAULT_SPSR"
|
||||||
|
|
||||||
|
if [ -n "$FAULT_ADDR" ] && [ "$FAULT_ADDR" != "0x0" ]; then
|
||||||
|
echo "=== Source Location ==="
|
||||||
|
"$GDB_BIN" "$ELF_FILE" --batch \
|
||||||
|
-ex "info line *$FAULT_ADDR" \
|
||||||
|
-ex "quit" 2>&1 | grep -v "^(gdb)" || true
|
||||||
|
fi
|
||||||
|
elif [ "$PC" = "0x1ff84ec" ] || [ "$PC" = "0x01ff84ec" ]; then
|
||||||
|
echo "[+] CPU is sleeping in armWaitForIrq (Normal running state waiting for VBlank/Timer IRQ)."
|
||||||
|
else
|
||||||
|
echo "[*] CPU is running user code at $PC."
|
||||||
|
"$GDB_BIN" "$ELF_FILE" --batch \
|
||||||
|
-ex "info line *$PC" \
|
||||||
|
-ex "quit" 2>&1 | grep -v "^(gdb)" || true
|
||||||
|
fi
|
||||||
@@ -61,6 +61,10 @@ snakePlus/
|
|||||||
├── tools/ # Tooling & automation scripts
|
├── tools/ # Tooling & automation scripts
|
||||||
│ └── clang_tidy_watch.sh # File watcher script running clang-tidy on file change
|
│ └── clang_tidy_watch.sh # File watcher script running clang-tidy on file change
|
||||||
│
|
│
|
||||||
|
├── .agents/ # Workspace agent customizations
|
||||||
|
│ └── skills/
|
||||||
|
│ └── melonds-gdb-debugger/ # GDB debugging runbook & diagnostic script
|
||||||
|
│
|
||||||
├── .vscode/ # VS Code configuration
|
├── .vscode/ # VS Code configuration
|
||||||
│ ├── launch.json # GDB debugger launch for melonDS (port 3333)
|
│ ├── launch.json # GDB debugger launch for melonDS (port 3333)
|
||||||
│ ├── settings.json # clangd and editor settings
|
│ ├── settings.json # clangd and editor settings
|
||||||
@@ -238,12 +242,9 @@ The repository includes preconfigured tasks in [`.vscode/tasks.json`](.vscode/ta
|
|||||||
- **Input Scanning Frequency & Bus Contention:** `scanKeys()` must be called once per frame synchronized with `swiWaitForVBlank()`. Calling `scanKeys()` in an unthrottled loop (spinning at 66 MHz) saturates ARM7 IPC queues (`touchRead`, `keypadGetState`), resets key-debounce edge detectors within microseconds, and leads to dropped inputs.
|
- **Input Scanning Frequency & Bus Contention:** `scanKeys()` must be called once per frame synchronized with `swiWaitForVBlank()`. Calling `scanKeys()` in an unthrottled loop (spinning at 66 MHz) saturates ARM7 IPC queues (`touchRead`, `keypadGetState`), resets key-debounce edge detectors within microseconds, and leads to dropped inputs.
|
||||||
- **Boot Subsystem Sequencing:** Always complete hardware configuration, console initialization, and data structure setup (`setup()`) before enabling the VBlank interrupt (`irqSet(IRQ_VBLANK, handle_vblank)`). Enabling the ISR beforehand causes VBlank interrupts to fire while `consoleLoadFont` is streaming font tiles to VRAM, triggering Data Aborts at startup.
|
- **Boot Subsystem Sequencing:** Always complete hardware configuration, console initialization, and data structure setup (`setup()`) before enabling the VBlank interrupt (`irqSet(IRQ_VBLANK, handle_vblank)`). Enabling the ISR beforehand causes VBlank interrupts to fire while `consoleLoadFont` is streaming font tiles to VRAM, triggering Data Aborts at startup.
|
||||||
- **Safe State Re-initialization:** In `init_game()`, call `timerStop(0)` before resetting game state or clearing containers, and only call `timerStart(0, ...)` at the very end when all state members and HUD caches are cleanly initialized.
|
- **Safe State Re-initialization:** In `init_game()`, call `timerStop(0)` before resetting game state or clearing containers, and only call `timerStart(0, ...)` at the very end when all state members and HUD caches are cleanly initialized.
|
||||||
3. **Debugging Traps & Data Abort Diagnosis with GDB:**
|
3. **Debugging with GDB & melonDS:**
|
||||||
- In Calico/devkitARM, unhandled Data Aborts route to `__arm_excpt_dabt` (`0x01ff8560`) and trap into an infinite loop at `__excpt_entry` (`0x01ff8594`).
|
- melonDS exposes a GDB stub on `localhost:3333`.
|
||||||
- If melonDS appears unresponsive or logs `attempt to restore CPSR under bad mode`, attach `arm-none-eabi-gdb` to port 3333 (`target remote localhost:3333`):
|
- For step-by-step instructions, exception vector addresses (`__arm_excpt_dabt`, `__excpt_entry`), stack-smash diagnosis, and automated diagnostic scripts, refer to the [`melonds-gdb-debugger`](.agents/skills/melonds-gdb-debugger/SKILL.md) skill.
|
||||||
- In `__excpt_entry`, inspect memory at `0x02fffd80`: the faulted instruction's address (`lr_abt - 8`) is pushed to `0x02fffd98`, and the faulted `SPSR` is at `0x02fffd90`.
|
|
||||||
- An unaligned/odd stack pointer (e.g. `sp = 0x02fffd8f`) indicates memory corruption caused by an `sp_irq` stack overflow clobbering `__sp_usr`.
|
|
||||||
- Use `info line *0x...` in GDB on the faulted PC to instantly locate the offending C++ line.
|
|
||||||
4. **Hardware Registers & Memory Banking:**
|
4. **Hardware Registers & Memory Banking:**
|
||||||
- Graphic tiles and palettes must be transferred using DMA (`dmaCopy`) or direct memory copies into allocated VRAM / OAM memory banks.
|
- Graphic tiles and palettes must be transferred using DMA (`dmaCopy`) or direct memory copies into allocated VRAM / OAM memory banks.
|
||||||
- Do not modify VRAM allocation configurations without ensuring compatibility with console text generators and OAM sub-engines.
|
- Do not modify VRAM allocation configurations without ensuring compatibility with console text generators and OAM sub-engines.
|
||||||
|
|||||||
Reference in new issue
Block a user