Remote Debugging with GDB
devkitPro provides gdb, the gnu debugger, which has been configured to allow remote debugging over the USB Gecko. You compile a stub of code into your wii program and run the debugger on your pc. Firstly include the code which establishes the connection:
You can then manually insert breakpoint into your code with the line:
Your wii code must be compiled with the option -g linked with the option -ldb, included before other libraries such as -logc.
Start your wii code using normal means such as the homebrew channel. There are three debugging tools available from devkitPro which are:
- powerpc-gekko-gdb: the basic text version of gdb driven from the command line
- powerpc-gekko-gdbtui: a version of gdb which includes a text window showing the source code of your application
- powerpc-gekko-insight: a custom version of Insight, a full GUI built on top of gdb, available as a separate package from devkitPro
Assuming that you have started the text based gdb, at the command prompt type:
target remote /dev/ttyUSB0
On windows you need to install the Virtual COM Port driver for USBGecko, this will provide an additional COM port. You can find a guide to install the VCP driver on the USBGecko site here
target remote COM5
replace COM5 with the port installed by the USBGecko VCP driver.
Tell gdb where to find symbol information by typing
You can then insert other break points manually by typing for example (assuming you are in the same directory as the source file):
If sources are in another directory, you can tell gdb where with the command:
You can then continue running the program by typing:
Particularly if you have hardcoded a breakpoint with the _break() function, it is useful to be able to run until the current function exits with the command:
Examine the value of variables by typing
The above examples are confirmed to work under linux but may require some modification to work under windows/msys (particularly /dev/ttyUSB0).
Graphical debugger options for Linux can be found on the linux set-up page
Debugging with Dolphin emulator
It is often easier to test out a homebrew app using dolphin emulator than using an actual Wii console. This is because for an actual wii, you have to put the sd card in, turn it on, and so on (especially if your wii is not next to your computer). Luckily the most recent version of the homebrew channel is dolphin compatible. It is best ran on the most recent version on dolphin (currently 5.0-13178), so update your emulator.
To set up the homebrew channel, first get a WAD of the homebrew channel. Get it here: https://wiidatabase.de/downloads/kanaele/homebrewkanal-open-source-edition/, then extract the zip to the Dolphin game directory.
(Setting the game directory: go to options -> configuration -> paths -> add, then choose a directory)
It is optional to install a wii menu. To do that, either install the NUS downloader (click download on here: https://wiibrew.org/wiki/NUS_Downloader), or scroll down to RVL downloader here: http://wehackwii.pbworks.com/w/page/8844411/IOS%20and%20RVL%20Downloads.
--To use the NUS downloader: go to Database -> System -> System menu and choose any version/any region. Check "pack WAD" then click "start download". --To use the website: just click download on the desired version, then unzip the zip file to your game directory (see above).
On dolphin you will now see either just the Homebrew channel or the homebrew channel and system menu depending on whether or not you downloaded the system menu. Go to tools -> system update -> current region. After that go right click on the Homebrew channel and click "install to the NAND". The homebrew channel should work now, but we need the actual app to be openable. You need to set up a virtual SD card. This guide works really well to set it up: https://wiki.dolphin-emu.org/index.php?title=Virtual_SD_Card_Guide.
To edit the sd card go to wherever sd.raw is loacated. Right click on the file, then click mount with ImDisk. Check "removable media" then click OK. A new drive should be visible in file explorer. Similar to an actual wii sd card, create a folder called "apps" on the root.
When you have compiled your app, just move the new boot.dol into a folder for your app. Put a meta.xml file in that subfolder also, so the homebrew channel can read it properly. Then to test on dolphin just go into the Homebrew channel and open the app. This process should be much faster than just loading it on the wii.
Experimental: Remote Debugging with GDB over Network
When faced with a crash in your Homebrew, often you'll see a code dump with an address and some machine code. Here's my trick to track that back to a line of C++ code.
For example if your homebrew game crashes it might show something like this:
CODE DUMP: 800084ac: 809F0020 2F840000 ... 800084bc: ... 800084cc: ...
The 800084ac is the memory address in hex of where the crash occurred. 809F0020 is the machine code for the offending instruction.
Using gdb, you can find out where the relevant code is with the command:
gdb info line *0x800084ac
This should work even if you don't have a USB gecko, as it is only using the symbol information stored in the .elf file.
On OSX, enter the gdb command line like this:
You will get a (gdb) prompt where you can type:
info line *0x800084ac
Alternative Method 1
A simpler way to get the line/file from an address is to use addr2line:
powerpc-gekko-addr2line -e <elf file> <address>
Alternative Method 2
- Step 1:
In your makefile change the CXXFLAGS line to the following:
CXXFLAGS = -save-temps -Xassembler -aln=$@.lst $(CFLAGS)
The "-save-temps" will save the assembly language file, which can be interesting. The "-Xassembler -aln=$@.lst" creates a list file which contains the assembly and the machine code. Now recompile your entire project. Note, this just affects C++ code.
- Step 2:
Look at the map file that was built. The mapfile is on by default in the Wii template makefile. Typically it's in the build subdirectory and called something.map. Look in that mapfile for the nearest memory address that doesn't go over the one found in the CODE DUMP. Here is an example:
This tells me that the crash was 72 bytes into the ShooterView::Render() function. Now to find the line number in Render()
- Step 3:
Look at the list file for the relevant function. Here's an example:
473 .globl _ZN11ShooterView6RenderER17BibGraphicsDevice 474 .type _ZN11ShooterView6RenderER17BibGraphicsDevice, @function 475 _ZN11ShooterView6RenderER17BibGraphicsDevice: 476 .LFB1465: 477 .loc 1 158 0 478 .LVL20: 479 02d0 9421FF00 stwu 1,-256(1)
The function names are mangled because this is C++ code. See http://en.wikipedia.org/wiki/Name_mangling#Name_mangling_in_C.2B.2B The address of the first instruction of Render() is at 02d0. This is also line 158 in the file (".loc 1 158 0"). To find the error location, just look at 0x2d0 + 72 = 0x318. See below:
.loc 1 168 0 528 0314 809F0020 lwz 4,32(31) 529 0318 2F840000 cmpwi 7,4,0
This shows machine address 0x318 has the proper machine code and the nearest .loc statement says the problem is at line 168 of the ShooterView.cpp. For more info on the assembler output see the manual here: http://sourceware.org/binutils/docs-2.18/as/index.html
Alternative Method 3
If you are getting crashes and you dont want to hunt through assembly code, or rendered-c you can put exit(0) in parts of your code you think it might crash in - like before function calls, or variable assignments.
If you compile different versions with the exit(0) in different places then when you run a version of your program that crashes instead of returning to the HBC then you know you are close to the source of the problem.
It is a dirty way of finding your problem, but a lot easier to understand than the other methods shown.