User:Qiang0/Debugging
Debugging Homebrew apps without Gecko USB
Prerequisites
- devkitPro-r19 with devkitPPC, Msys, and examples installed
- Homebrew channel on your wii console
- You must be able to compile and run/load the example code in
/c/devkitPro/examples/wii/devices/network/sockettest
- Optional: MinGW compiling environment
- Windows XP Pro 32-bit
- The wii_dev_debug.rar ( http://db.tt/HnoMt80 or http://qianglin.dyndns.org/wii_dev/wii_dev_debug.rar )
(the above link is hosted on a linux platform in my home, it might not available all the time; if you have a better place, please let me know; please verify the md5 checksum is 5c7edebc6cd49ce75c7cf4233201a49a )
Definitions
In order to make my writing simple and accurate, let's denote the following environment variables, host-name strings, and other things will be mentioned in this document frequently:
- Msys-window
- To start a Msys window, Start-->All Programs-->devkitPro-->Msys; all the file/folder path I am talking about in this document uses Msys path.
- $DEVKITPRO
- The location of devkitPro, default is "/c/devkitPro/"
- $DEVKITPPC
- The location of devkitPPC, default is "/c/devkitPro/devkitPPC"; make sure your $PATH contains $DEVKITPPC/bin directory.
- $WII_DEV_DEBUG
- The full path where the wii_dev_debug.rar is extracted to, such as "/c/wii_dev_debug".
- $EXAMPLE
- The full path to the example of "sockettest", should be "/c/devkitPro/examples/wii/devices/network/sockettest"
- wii.home.net
- The host-name of the wii-console that uses wireless DHCP connection; IP address is ok.
- $WIILOAD
- Required by "$DEVKITPPC/bin/wiiload.exe", should be something like "tcp:wii.home.net"
Use net_print utilty to trace apps
This utility provides the following functions:
int net_print_init(const char *rhost, unsigned short port); int net_print_string( const char* file, int line, const char* format, ...); int net_print_binary( int format, const void* binary, int len); // format: // 'c' : printable chars or hex for each byte // 'u' : hex for each byte // 'x : hex for each short // 'X': hex for each 32-bit integer
With those functions, you are able to print the trace messages over to the screen of a computer on the network.
Run the sockettest example
- open a Msys window
- "cd $EXAMPLE"
- "make"
- start Homebrew on your wii; make sure the Homebrew channel network-loaded service is running; if not, using your wii remote controller to point to the most right-bottom icon of the Homebrew screen, and press "A" button to active it.
- "wiiload sockettest.elf"
- the example should be running run your wii
- use IE to browse the URL http://wii.home.net
Compile/run the net_print server
- "cd $WII_DEV_DEBUG/net_print/server"
- IF you have MinGW compiling environment, run "make -f Makefile.win"
- IF you want to compile on Linux/Unix, copy the "server" folder to Linux/Unix, and run "make"
- IF you trust the $WII_DEV_DEBUG/net_print/server/win32bin/*.exe", you can go server/win32bin folder.
- make sure that the "np_server" is working
- run "./np_server.exe" or "./np_server"
- open another Msys window, change the current directory to where you start the "np_server"; run "./np_client.exe" or "./np_client"; there should be some messages printing on your "np_server" screen; optionally, you can run the "np_client <server-host>" on another machine to make sure your computer's firewall settings are right.
- retstart the "np_server", and open another Msys window, and change the directory to $WII_DEV_DEBUG/net_print/sockettest;
- change constant string NET_PRINT_DEFAULT_HOST defined in the header file "source/net_print.h" to point to the host where the "np_server" is running.
- "make"
- "wiiload.exe sockettest.elf" or "wiiload.exe sockettest.dol", and you should be seeing some messages printed to the screen of "np_server"
[ TIP: you can start the "np_server" by running "np_server | tee -a np_server.out", so that the output is also saved to the text file.]
Restrictions
- the net_print_init() function must be called after the network is ready.
- the net_print_init()/net_print_string()/net_print_binary() functions are not coded to thread safe; if you want to be thread-safe, you can add a mutex to those functions.
- using net_print_string(), if the size of a mesage is over 511, you may corrupt the C stack; also if you are running on a very tight stack size, you may want to reduce the constant in net_print.c.
Using gdb to debug over the network
The usage
#include <debug.h> ... //after your network is initialized using if_config() function, you can call DEBUG_Init(100, 5656); ... _break();
Steps
- Copy my libdb.a to libogc directory
- Save your $DEVKITPRO/libogc/lib/wii/libdb.a
- "cp $WII_DEV_DEBUG/gdb/libdb.a $DEVKITPRO/libogc/lib/wii/libdb.a"
- "cd $WII_DEV_DEBUG/gdb/sockettest"; in this folder, the "makefile" is changed to using the "-g" flag and linking the libdb.a library.
- Compile the program
- study the code of "source/sockteset.c" ; the program creates two threads one is "httpd()" while the other one is "debug_thread()"; the "httpd()" calls the "DEBUG_Init()" and "_break()".
- "make"
- run your application "wiiload.exe sockettest.elf" ( I tested that you can run the sockettest.dol file too)
- using a Msys window, "cd $WII_DEV_DEBUG/gdb/sockettest"
- start gdb by running:
$DEVKITPPC/bin/powerpc-eabi-gdb.exe sockettest.elf
- or, if you are a dbx person, you may want to try:
$DEVKITPPC/bin/powerpc-eabi-gdb.exe --dbx sockettest.elf
- in the gdb, type in:
target remote wii.home.net:5656
- you should see that the program breaks, and the screen looks like:
gdb sockettest.elf GNU gdb (GDB) 7.0 Copyright (C) 2009 Free Software Foundation, Inc. ...... Reading symbols from c:\wii_dev_debug\gdb\sockettest/sockettest.elf...done. (gdb) target remote wii.home.net:5656 Remote debugging using wii.home.net:5656 0x8002d3a0 in _break ()
Type in "where","break my_func1" and "break my_func2", and continue with "cont":
(gdb) where #0 0x8002d3a0 in _break () #1 0x80007194 in httpd (arg=0x800f1ce8) at c:/wii_dev_debug/gdb/sockettest/source/sockettest.c:148 #2 0x80016fa0 in __lwp_thread_handler () #3 0x80016f40 in __lwp_thread_exit () (gdb) break my_func1 Breakpoint 1 at 0x80006f20: file c:/wii_dev_debug/gdb/sockettest/source/sockettest.c, line 38. (gdb) break my_func2 Breakpoint 2 at 0x80006f58: file c:/wii_dev_debug/gdb/sockettest/source/sockettest.c, line 45. (gdb) cont Continuing. [New Thread 4] [Switching to Thread 4] Breakpoint 1, my_func1 (p=4) at c:/wii_dev_debug/gdb/sockettest/source/sockettest.c:38 38 p++; (gdb)
Debugging crashes
Debugging crashes becomes much easier with gdb. Let's take the original 'my_func2()' in the sockettest.c as an example to see how easy to diagnose crashes. The function is called by the httpd() thread right after it accepts a connection from the network. The original function reads like:
static int my_func2( int p) { p++; return p+1; }
Let's make some changes to the sockettest.c, so that the new my_func2() looks like:
static int my_func2( int p) { p++; *((int*)NULL) = 0; // this will force a SIGSEGV signal return p+1; }
Also we need to comment out the two lines in sockettest.c:
// DEBUG_Init(100,5656); ... // _break();
Recompile and run the program. Use the browser to access your wii console hostname/ip-address, we would for sure see a nice screen like:
Exception (DSI) occurred! GPR00 xxxxxxxx ..... GRP01 xxxxxxxx ..... ..... DAR xxxxxxxx STACK DUMP: xxxxxxxx --> xxxxxxxx --> xxxxxxxx CODE DUMP: xxxxxxxx: xxxxxxxx .... ......
Restore the DEBUG_Init() and _break() lines, and use gdb to debug the program. After the program breaks, type in "cont":
gdb sockettest.elf GNU gdb (GDB) 7.0 ...... Reading symbols from c:\wii_dev_debug\gdb\sockettest/sockettest.elf...done. (gdb) target remote wii.home.net:5656 Remote debugging using wii.home.net:5656 0x8002d3b0 in _break () (gdb) cont Continuing.
Now, use your browse to send a http request to the program to call my_func2(), and you shall see:
Program received signal SIGSEGV, Segmentation fault. 0x80006f70 in my_func2 (p=4) at c:/wii_dev_debug/gdb/sockettest/source/sockettest.c:47 47 *((int*)NULL)=p; (gdb)
As my experience on other platforms, even your code is not compiled with -g flag, you will still get the correct C stack upon unexpected exceptions/signals in most of the situations.
Notes, restrictions, and helps needed
- this is just the initial version so don't expect too much.
- the current version of DEBUG_Init(100, port) must be called after your network is ready.
- please only call once of DEBUG_Init()
- don't try to suspend all the threads, as one of the them is the helper thread for the gdb.
- if you know that how to make DEBUG_Init(GDBSTUB_DEVICE_TCP,port) working, please let me know. I did some researches for the libogc/libdb source files, but never get the DEBUG_Init(GDBSTUB_DEVICE_TCP,port) working; may be we need the USB-ethernet device ?
- you can load the .dol file and gdb the .elf file.
- you may want to compile selected source files you wish to debug with -g flag
- the network-gdb works slow for me; if you have a gecko-usb, please let me know the performance comparision.
- I have not used this to debug a real application; let me know if you have tried so.
The source code
Source code of the changes to libogc/libdb are in $WII_DEV_DEBUG/gdb/libogc/libdb. If you need to rebuild the libdb.a, you need to merge the debug.c using the debug.c.orig as the base to your debug.c. Also you need to change the "makefile" to include the "helper_tcpip.c". Again, this is just the initial version, I prabably will change it very soon.
Why I am doing this?
About two weeks ago, I installed Homebrew channel and two media players MPlayerCE and GeeXBox. I like both of them very much, especially MPlayerCE. But there are some thing wrong with my Chinese language file/folder name display/play/browse. So I decided to checkout the source code of MPlayerCE to have a look, after struggling several days of the compiling, I finally get my build up and running with the guidance of MPlayerCE project member.
The next thing I need is a means to debug the application conveniently. So I wrote the small but effective net_print utility, tracing why Chinese chars are not correctly handled by MPlayerCE (0.76 early source), fixed the problems in my environment.
While doing the tracing for the MPlayerCE, I also tried to find a way that I can remotely gdb the Homebrew applications without using the Gecko USB device. After reading the libogc/libdb source code, and some experimental tests. I finally modified the libdb.a to support gdb across network.
Bye the way, if you need a mplace-ce version that fully working (I hope so!) for Chinese language, please contact me. My changes have been submitted to the mplayer-ce team for reviewing.
Comments, suggestions, and your testing result?
You can put them here, or email qiang0@gmail.com