NAME
docs/debug.pod - Debugging Parrot
ABSTRACT
This document describes how to debug various parts of Parrot.
VERSION
$Revision $
THE PARROT BINARY
Using a debugger
Per default the parrot
binary is being built with debugging symbols.
This means that you can run parrot
under an debugger like gdb
.
Debugging support can be explicitly enabled with:
shell> perl Configure.pl --debugging shell> make
For testing it might be a good idea to make test runs without debug support. So debugging can also be turned off with:
shell> perl Configure.pl --debugging=0 shell> make
Using a memory checker
You could, and should, also run the tests with a memory checker such as valgrind
. You can enable valgrind
, by running:
shell> make test VALGRIND="valgrind --log-file=/tmp/grind"
Another possibility is to use Electric Fence, or ...
MEMORY MANAGEMENT
Some of the more frequent and exasperating parrot
bugs are related to memory management in general, and garbage collection in particular.
Infant mortality
See docs/dev/infant.pod for details of one frequent problem: infant mortality. Infant mortality is when you create a Parrot object, but the garbage collector runs before you put it into a Parrot register or in something else that is itself within a Parrot register.
To help in resolving these issues, the parrot binary accepts a --gc-debug
flag. This flag makes garbage collection occur as frequently as possible, which maximizes the probability that any newborn objects will run afoul of the garbage collector.
Within the --gc-debug
mode, there is another tool to help narrow down the problem. You can edit include/parrot/gc_api.h and #define
the GC_VERBOSE
flag to 1. After recompiling parrot
, the garbage collector will perform additional checks. After the garbage collector has traced all objects to find which ones are still alive, it will scan through all of the dead objects to see if any of them believe they are alive (which will happen for infants, since they come into existence marked live.) If it finds any, it will print them out. You can then re-run the program with a breakpoint set on the routine that allocated the object (e.g. get_free_object
in src/gc/mark_sweep.c). You'll probably want to make the breakpoint conditional on the object having the version number that was reported, because the same memory location will probably hold many different objects over the lifetime of the program.
PIR AND PASM CODE
Let's say you have written (or generated) a huge .pasm or .pir file. It's not working. You'd like some help in figuring out why.
parrot_debugger
One possible tool is parrot_debugger
, the Parrot Debugger. See docs/debugger.pod for details on it.
stabs
If you are running on a jit-capable machine, you can also try using gdb
by having the JIT compiler generate stabs
metadata and then stepping through the code with gdb
as if it were any other language.
To use this, you'll want to use parrot
to generate your bytecode (.pbc file). It is not strictly necessary, but you'll get more information into the bytecode this way.
Let's say your file is named test.pasm
. (Note: these instructions will also work if you use test.pir
everywhere test.pasm
occurs.)
Step 1: Generate the .pbc file with extra debugging information.
shell> parrot -d -o test.pbc test.pasm
Step 2: Start up parrot
under gdb
% gdb parrot
or
% emacs & (in emacs) M-x gdb (in emacs) type "parrot" so it says "gdb parrot"
Step 3: Set a breakpoint on runops_jit
gdb> b runops_jit
Step 4: Run your program under gdb
with JIT and debugging on
gdb> run -R jit -D4 test.pbc
Step 5: gdb
will stop at the beginning of runops_jit. Step through the lines until just before the JITed code is executed (the line will be something like (jit_code)(interpreter,pc)
.
gdb> n gdb> n . . .
Step 6: load in the debugging information from the symbol file that the jit just generated.
gdb> add-symbol-file test.o 0
Step 7: Step into the JITed code
gdb> s
At this point, you can step through the instructions, or print out the various Parrot registers. FIXME: gdb
will know about I0-I31, N0-N31, S0-S31, and P0-P31.
WARNING: Stepping too far
One thing to watch out for is that gdb
gets confused when attempting to step over certain instructions. The only ones that I have noticed having problems is keyed operations. With my version of gdb
, if I do 'n' to step over the instruction, gdb
will start running and only stop when the entire parrot program has finished. To work around this, do 'si' twice just before executing any keyed op. For some reason, gdb
can then figure out when it's supposed to stop next. If you know of a better technique, please let the mailing list know (parrot-dev@lists.parrot.org
).
PIR CODE GENERATION
The parrot
binary has a bunch of debugging flags for spewing out information about various aspects of its processing. See running.pod for a list of flags. Or have a look at the information provided by:
shell> parrot --help
or
shell> parrot --help-debug
BACKTRACING
auto-magical
If Parrot is built on a system with GNU libc it is capable of automatically generating a backtrace on stderr
for debugging purposes. Currently these automatically backtraces are only generated by assertion failures but in the future they also be produced by other bad events (for example, SEGV
).
Here is an example of a what a backtrace might look like:
Backtrace - Obtained 15 stack frames (max trace depth is 32). (unknown) Parrot_confess Parrot_str_new_COW Parrot_String_get_string Parrot_set_s_p (unknown) (unknown) (unknown) (unknown) Parrot_runops_fromc_args Parrot_runcode (unknown) imcc_run (unknown) __libc_start_main (unknown)
It must be noted that glibc's backtraces are not without limitation. It's method depends completely on information that is available at run time.
- Functions marked as
static
can only be identified by address as they have no "symbol name" for dynamic linking in the executable's symbol table. Static functions will appears as(unknown)
. - There must be some means available for walking the stack at runtime. On x86(-64)? the "stack pointer" must be in
[re]sp
register. For example, thisgcc
compilation flag would break backtracing (except for functions that do dynamic allocation on the stack as this optimization can no be allied to them).perl Configure.pl --ccflags=-fomit-frame-pointer
- Some platforms may require extra linker flags in order to get all of the required symbols exported in the symbol table.
Configure.pl --ccflags=-rdynamic
- Any debugging information embedded in the object is not accessible. So file and line number can not be included as part of the backtrace information.
- Be warned that signals may cause incorrect backtraces!
gdb
On systems not equipped with libc, one will need to use an external debugger to get backtrace information. This method is actually more capable then the auto-magical approach as most debuggers will use debugging information if it's available in the object code (for example, if parrot was built with -g
).
Since the Parrot_confess
symbol is always compiled into parrot it can be used as a break point to obtain a backtrace. Here is an example of doing this with gdb and a version of parrot compiled with gcc
and the -g
flag.
$ gdb parrot GNU gdb 6.6 Copyright (C) 2006 Free Software Foundation, Inc. GDB is free software, covered by the GNU General Public License, and you are welcome to change it and/or distribute copies of it under certain conditions. Type "show copying" to see the conditions. There is absolutely no warranty for GDB. Type "show warranty" for details. This GDB was configured as "i686-pc-linux-gnu"... Using host libthread_db library "/lib/libthread_db.so.1". (gdb) b main Breakpoint 1 at 0x80488a0: file src/main.c, line 38. (gdb) r foo.pir Starting program: /home/moanui/jhoblitt/parrot/parrot foo.pir Failed to read a valid object file image from memory. [Thread debugging using libthread_db enabled] [New Thread -1213900128 (LWP 23148)] [Switching to Thread -1213900128 (LWP 23148)] Breakpoint 1, main (argc=-400292727, argv=0x159a0) at src/main.c:38 38 { (gdb) b Parrot_confess Breakpoint 2 at 0xb7c542a0: file src/exceptions.c, line 767. (gdb) c Continuing. [New Thread -1214039152 (LWP 23151)] [New Thread -1222431856 (LWP 23152)] 1..1 Breakpoint 2, Parrot_confess (cond=0xb7eeda65 "s", file=0xb7eeda58 "src/string.c", line=129) at src/exceptions.c:767 warning: Source file is more recent than executable. 767 { (gdb) bt full #0 Parrot_confess (cond=0xb7eeda65 "s", file=0xb7eeda58 "src/string.c", line=129) at src/exceptions.c:767 No locals. #1 0xb7c433b1 in Parrot_str_new_COW (interp=0x804e008, s=0x0) at src/string.c:129 d = (STRING *) 0x81c21b8 __PRETTY_FUNCTION__ = "Parrot_str_new_COW" #2 0xb7e40db3 in Parrot_String_get_string (interp=0x804e008, pmc=0x81c8578) at src/pmc/string.c:310 No locals. #3 0xb7cc7d41 in Parrot_set_s_p (cur_opcode=0x825d470, interp=0x804e008) at src/ops/set.ops:159 No locals. #4 0xb7c9da32 in runops_slow_core (interp=0x804e008, pc=0x825d470) at src/runops_cores.c:184 No locals. #5 0xb7c67acf in runops_int (interp=0x804e008, offset=0) at src/interpreter.c:816 pc = (opcode_t * const) 0x8239730 lo_var_ptr = 134537224 core = (opcode_t *(*)(Parrot_Interp, opcode_t *)) 0xb7c9d940 <runops_slow_core at src/runops_cores.c:169> #6 0xb7c6854e in runops (interp=0x804e008, offs=0) at src/call/ops.c:100 offset = 0 old_runloop_id = 0 our_runloop_level = 1 our_runloop_id = 1 #7 0xb7c687da in runops_args (interp=0x804e008, sub=0x8204d58, obj=0x80912d8, meth_unused=0x0, sig=0xb7eefca6 "vP", ap=0xbfec614c "@M \b�b��P�\222K\230�\004\b@\236\"\b@M \bXM \b\004����t��\b�\004\b\001") at src/call/ops.c:216 offset = 0 dest = (opcode_t *) 0x8239730 ctx = (Parrot_Context *) 0x822a3b0 new_sig = "" sig_p = 0xb7eefca7 "P" old_ctx = (Parrot_Context * const) 0x804e298 #8 0xb7c688fb in Parrot_runops_fromc_args (interp=0x804e008, sub=0x8204d58, sig=0xb7eefca6 "vP") at src/call/ops.c:293 args = 0xbfec614c "@M \b�b��P�\222K\230�\004\b@\236\"\b@M \bXM \b\004����t��\b�\004\b\001" ctx = (Parrot_Context *) 0xb7fa1548 #9 0xb7c50c51 in Parrot_runcode (interp=0x804e008, argc=1, argv=0xbfec62e8) at src/embed.c:783 userargv = (PMC *) 0x8204d40 main_sub = (PMC *) 0x8204d58 #10 0xb7ed74a1 in imcc_run_pbc (interp=0x804e008, obj_file=0, output_file=0x0, argc=1, argv=0xbfec62e8) at compilers/imcc/main.c:614 No locals. #11 0xb7ed7d90 in imcc_run (interp=0x804e008, sourcefile=0xbfec6e0a "foo.pir", argc=1, argv=0xbfec62e8) at compilers/imcc/main.c:815 obj_file = 0 yyscanner = (yyscan_t) 0x822a090 output_file = 0x0 #12 0x080489b7 in main (argc=136704448, argv=0x825f220) at src/main.c:62 sourcefile = 0xbfec6e0a "foo.pir" interp = (Interp *) 0x804e008 executable_name = (STRING *) 0x821b8e4 executable_name_pmc = (PMC *) 0x8204d70 status = 1267896320 (gdb)