I'm posting up a “getting started“ style document that previous members of the Rotor team cooked up, it illustrates some notes for debugging under the GDB environment (FreeBSD and MacOS). Enjoy.
Debugging a new instance of an application
Run "gdb app_name", then at the "(gdb)" prompt, enter "run [arguments]" to spawn the new instance and pass along optional command-line arguments to it. For example:
(gdb) run ~/rotor/tests/bvt/short/hello.exe
This will launch a new clix process, passing along the path to a managed exe to run.
Attaching gdb to a running process
Run "gdb app_name", where app_name is the name of the running process. At the "(gdb)" prompt, type "attach pid", where "pid" is the process ID of the process to attach to. Specifying the app name on GDB's command line allows GDB to load symbols for the process.
GDB cannot set breakpoints in .so files that are not currently loaded, though it does keep the .so's symbols loaded even if the .so unloads. So in order to set a breakpoint in a .so, you must wait until the .so is actually loaded. For example, to set breakpoints in libsscoree.so within a clix process, you must:
Breakpoints may be set in the PAL once main() has been called.
Restarting the Debugging Session
Since GDB cannot set breakpoints in not-loaded .so files, and restarting the process unloads the .so files, GDB doesn't support breakpoints set in .so files when restarting.
So before restarting a debugging session, use "info break" to show the list of currently-set breakpoints, then use "dis n [ n...]" to disable all of the breakpoints set outside of the application itself. Multiple breakpoints can be disabled by one "dis" command - the list to disable is space-separated.
Once those breakpoints are disabled, the session can be restarted by typing "run [arguments]". If no arguments are specified, then gdb uses the arguments from the previous "run" command, which saves you from having to retype long arguments on each restart. GDB will prompt before restarting, to kill the current session.
Callstacks and Local Variables
Note that "step" and "next" may occasionally fail to step as expected, and will break with a message like:
0x28074f60 in _init () from /home/barrybo/rotor/build/librotor_pal.so
When this happens, enter "fin" once and you should return back into the code you're debugging. This happens only on function calls within the PAL when calling another function within the PAL.
GDB has assembly-level debugging, but the source-level debugging features tend to interfere. For example, GDB does not automatically print out the CPU registers when single-stepping at the instruction level. Some hints:
Create or modify ~/.gdbinit to define some handy macros to make debugging easier:
define printwcall PAL_get_stderr()call (void) PAL_fprintf($, "%S", $arg0)echo \nend define duprintw $arg0end define pwprintw $arg0end define soscall (void)SOS("$arg0")echo \nend
The printw, du, and pw macros are all aliases for a command which prints a PAL Unicode string out as text. PAL Unicode characters are 16-bit, which are encoded differently than Unix Unicode characters, which are 32-bit. The macros print the string to the debuggee's stderr.
The sos macro can be used to load and call a .so file which contains postmortem debugging tools for managed code, called SOS. For more information on this, see clr/src/vm/ceemain.cpp's SOS() function, and also the contents of clr/src/tools/sos.