I have been working on Zelda DS for over a year now, and have loved the experience of using the homebrew tools provided by devkitPro
. However, as the project grew, I quickly started to miss using a source-level debugger to help me debug and fix problems in the code. There is a debugger that you can download as part of devkitPro named Insight; but it's a fairly finicky application, and is a far cry from the powerful debugger I'm used to using on a daily basis: the Microsoft Visual Studio debugger.
Well, the good news is, after much research and playing around, I found a way to use the Visual Studio debugger to debug homebrew NDS roms! I've decided to share these steps with you in this tutorial:
You must have the following software installed:
- Visual Studio: A full copy of Visual Studio, not an Express edition, I use version 2008, but 2005 and 2010 should work fine. The reason it can't be the Express version of Visual Studio is that it does not support plugins, and the next requirement is a plugin...
- WinGDB: This excellent plugin allows you to debug gcc compiled programs with gdb within Visual Studio. It's not free, however before you decide whether to purchase it, you can download a fully functional 30-day trial.
- DeSmuME: The best DS emulator available, and the only one that implements a gdb stub that allows you to use gdb to debug an NDS rom. Thankfully, this piece of software is not only free, but open source as well!
Visual Studio Makefile Project
The first step is creating a Visual Studio Makefile project that simply invokes the makefile for your homebrew NDS application, and often includes the source files if you wish to use Visual Studio as an editor as well. There is an excellent tutorial on how to do exactly this right here:
The only difference in my setup is that I don't use no$gba as my emulator, but DeSmuME instead, which I discuss later. In any case, if you can at least build your rom from a Visual Studio project, you're good to go for the next step.
Compiling with Debug Symbols
In order to debug your NDS rom, it must be compiled with debug symbols. The devkitPro templates for NDS applications do not
enable debug symbols as this would both slow down the application, as well as increase the size of the rom.
To enable debug symbols effectively means to disable optimizations that are applied by gcc. To do this, open the Makefile, and find the line similar to:
CFLAGS := -g -Wall -O2\
To disable optimizations and get enough debugging information to debug your rom, you must minimally remove the -O2. Personally, I also remove the -fomit-frame-pointer and the -ffast-math to make sure I don't get wonky behaviour when stepping through code, but that may be overkill.
Once you've disabled optimizations, make sure to rebuild your rom. An incremental build will not work since no dependencies have been updated.
As a side note, you'll probably want to reenable optimizations for your final NDS rom, especially if you notice a performance hit from having disabled them. Having to do this by manually editing the Makefile every time can be tedious. Personally, I made many modifications to the Makefiles that allow me to pass in a configuration (DEBUG or RELEASE), which modifies not only the compiler flags, but also the output folder for object files, and the name of the rom itself. I may post a tutorial on how to do this someday.
Assuming you've installed Visual Studio and WinGDB correctly, you should see an menu entry for WinGDB in Visual Studio:
→ Preferences, General
tab, the only option you'll need to set is the Default debugger path
to the arm-eabi-gdb.exe in your devkitPro installation, which in my case is "C:\devkitPro\devkitARM\bin\arm-eabi-gdb.exe":
to close the dialog. Now we need to set the project-specific properties for WinGDB. To do this, in the Solution Explorer, right-click on your project and select WinGDB
Now set the following fields in the dialog that opens:
tab:, Target type:
Embedded Linux system (gdbserver)
here you must set the path to the arm9 elf file that gets built for your application. The arm9 elf file contains the code, data, and debug information for the arm9 processor, and is usually what you're interested in debugging. Note that if you used the arm9
template from devkitPro (i.e. c:\devkitPro\examples\nds\templates\arm9), there should be only one elf file that gets built next to your nds file. However, if you used the combined
template (i.e. c:\devkitPro\examples\nds\templates\combined), you'll find the elf file within the arm9 subfolder with extension .arm9.elf.
Stop in main(): yes (I find it useful, but it's really up to you)
Byte order: little endian
And finally, on the Target
tab, set Target specification
to: remote localhost:20000
What this value means is that we want gdb to connect to a remote gdb stub that is listening on the local machine's port 20000, which is what we'll set up DeSmuME to do.
Now click OK
to save your settings. For the curious, these settings are saved in your project's .suo (user options) file.
Now it's time to debug your rom! The first step is to launch the development version of DeSmuME with a specific argument telling it to enable the gdb stub code and to listen on localhost:20000. For example, I run the following command:
c:\emus\desmume\DeSmuME_dev.exe --arm9gdb=20000 C:\coding\ZeldaDS\ZeldaDS_d.nds
A few things to note:
- You must use the "_dev" executable
- We specify that we want to debug the arm9 processor. You can instead specify arm7gdb as well if you wish (and set WinGDB to debug the arm7 elf file instead).
- The port number, 20000, must match the one set in WinGDB. You can change this value as long as the two match.
This will launch DeSmuME with a console window, and if all went well, the console should report that your nds was loaded succesfully, Emulation unpaused, and the main DeSmuME window should not yet be running your game. DeSmuME is effectively waiting for gdb to connect to it.
Launch Visual Studio Debugger via WinGDB
With your project loaded in Visual Studio, from the menu select WinGDB → Start Debugging and if all goes well, Visual Studio should switch into debugging mode, and you should eventually break on the first line of main()!
From here, you can do pretty much anything you can normally do in the Visual Studio Debugger, which includes, but is not limited to:
- Setting breakpoints (conditional, tracepoints, etc.)
- Hovering over variables to see their values
- Using the autos, locals, watch, memory and threads windows
- Using the disassembly view to see your source code mixed with the assembly code that was generated
- Setting a breakpoint at arbitrary locations while your application is running (this has never worked for me with other debuggers like Insight or Eclipse).
Phew, that was a little longer than I thought it would be, but hopefully this post will help some of you out there debug your NDS applications more easily! If you have any problems, or if you feel certain parts of this tutorial could be more clear, don't hesitate to let me know.
Posted By Antonio Maiorano to Video Game Coding
at 1/18/2011 01:26:00 AM