Allows for debugging over a flashcart serial port using GDB. This library is designed to use threads and at the moment only supports libultra. It uses the same protocol as UNFLoader to communicate.
You need to include the debugger folder in your project and at the start of your program call.
enum GDBError gdbInitDebugger(OSPiHandle* handler, OSMesgQueue* dmaMessageQ, OSThread** forThreads, u32 forThreadsLen);
handler
should be the return value for osCartRomInit
. dmaMessageQ
is the message queue used to coordinate DMA access. forThreads
is an array of threads the debugger should connect to and forThreadsLen
is the length of forThreads
See the example included in this repo for a concrete example of usage.
Proxy.js translates commands sent to and from GDB to a format the serial interface can process. You will need to use it to connect the debugger. To run proxy.js you will need to install node.
To run it use
node proxy/proxy.js /dev/ttyUSB0 8080 -k
Where /dev/ttyUSB0
is the serial port the flash cart is connected to and 8080
is the port the proxy listens to for GDB connections. the -k
flag is used to indicate that the proxy should stay open even after GDB disconnects. This allows you to reuse the same proxy process instead of having to restart it after each run. You can also optionally add a -v
flag and proxy will print verbose information to diagnose connection problems.
If found I needed to use gdb-multiarch to get remote debugging to work.
sudo apt-get install gdb-multiarch
Before starting GDB you should first have the ROM you want to debug running on the target system and paused at gdbInitDebugger
waiting for the debugger to connect. You also need the proxy running. Once you have done that run gdb
gdb-multiarch -q debugger.out
Where debugger.out is the intermediate elf file generated by mild
or spicy
You should then be greeted by the GDB prompt
Reading symbols from debugger.out...
(gdb)
Next you need to connect to proxy. You do that by specifying a remote target to GDB.
(gdb) target remote localhost:8080
If things are working correctly you should see something like this
Remote debugging using localhost:8080
0x800083cc in osStopThread ()
(gdb)
At this point you can continue the program and use GDB as normal. Here are a few things to try.
(gdb) c
Continuing.
Program received signal SIGTRAP, Trace/breakpoint trap.
gdbInitDebugger (handler=0x80024178 <__CartRomHandle>, dmaMessageQ=0x8001c170 <n_dmaMessageQ>, forThreads=0x8002037c <mainThreadStack+8164>, forThreadsLen=1) at debugger/debugger.c:893
893 return GDBErrorNone;
(gdb) c
Continuing.
Breakpoint 1, mainproc (arg=0x0) at example/main.c:172
172 healthcheck = 0;
(gdb) print frame
$1 = 0
(gdb) del 1
(gdb) c
Continuing.
^C
Program received signal SIGINT, Interrupt.
0x80005568 in putchr (color=65535, curs_x=61, curs_y=24, c=32 ' ') at example/graph.c:99
99 *p = BGCOLOR;
(gdb)
I recommend this plugin for debugging
These are the targets I have in launch.json
to enable these plugins.
Where Run EV delay
is a task that uploads the rom build/debugger.n64
to the flash cart
and waits a short delay for the game to load be ready for the debugger to connect.
{
"type": "by-gdb",
"request": "launch",
"name": "Debug with EV",
"program": "${workspaceFolder}/build/debugger.elf",
"cwd": "${workspaceRoot}",
"debuggerPath": "gdb-multiarch",
"remote": {
"enabled": true,
"address": ":8080",
"mode": "remote",
"execfile": "${workspaceFolder}/build/debugger.elf"
},
"commandsBeforeExec": [
"set arch mips:4300",
],
"preLaunchTask": "Run EV delay",
},
If gdb gives you this error
(No debugging symbols found in debugger.out)
That means you either didn't compile debugging symbols in the compile step or they were discarded when linking. Be sure that you include the -g
flag when running mips64-elf-cc
in your build process. As for the linking stage I am working on my own fork of spicy that doesn't discard the debugging symbols in the link step. As far as I can tell the symbols are discarded in the final objcpy
step anyway so having the included in the intermediate debugger.out
file is fine.
Here is the code in question
if (primaryThread != NULL) {
osStopThread(primaryThread);
gdbBreak();
}
For some reason the Native Debug vscode plugin immediatly continues after connecting but thinks that the program is still paused. It is only upon reaching the second gdbBreak
breakpoint that it will pause and the debugger will work properly. You also may be wondering why even have the debugger pause at all and let it continue right after connecting. Well for some reason both vs code debugger plugins wont let me interrupt execution if I don't first stop at that breakpoint.
This debugger does work with a special fork of cen64 but a much better solution is to use cen64's built in gdb debugger. More details can be found here Debugging with GDB