User:Qiang0/Debugging

From WiiBrew
Jump to: navigation, search

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

(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

  1. this is just the initial version so don't expect too much.
  2. the current version of DEBUG_Init(100, port) must be called after your network is ready.
  3. please only call once of DEBUG_Init()
  4. don't try to suspend all the threads, as one of the them is the helper thread for the gdb.
  5. 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 ?
  6. you can load the .dol file and gdb the .elf file.
  7. you may want to compile selected source files you wish to debug with -g flag
  8. the network-gdb works slow for me; if you have a gecko-usb, please let me know the performance comparision.
  9. 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