|parrotcode: Debugging Parrot|
|Contents | Documentation|
docs/debug.pod - Debugging Parrot
This document describes how to debug various parts of Parrot.
Per default the
parrot binary is being built with debugging symbols.
This means that you can run
parrot under an debugger like
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
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 --logfile=/tmp/grind"
Another possibility is to use Electric Fence, or ...
Some of the more frequent and exasperating
parrot bugs are related to memory management in general, and garbage collection in particular.
See docs/dev/infant.dev 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.
--gc-debug mode, there is another tool to help narrow down the problem. You can edit src/dod.c and
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/smallobject.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.
Let's say you have written (or generated) a huge .pasm or .imc file. It's not working. You'd like some help in figuring out why.
One possible tool is
pdb, the Parrot Debugger. See docs/debugger.pod for details on it.
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
Step 1: Generate the .pbc file with extra debugging information.
shell> parrot -d -o test.pbc test.pasm
Step 2: Start up
% gdb parrot
% 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 -j -D4 test.pbc
gdb will stop at the beginning of runops_jit. Step through the lines until just before the JITted code is executed (the line will be something like
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 JITted code
At this point, you can step through the instructions, or print out the various Parrot registers.
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 binary has a bunch of debugging flags for spewing out information about various aspects of its processing. See imcc/docs/running.pod for a list of flags. Or have a look at the information provided by:
shell> parrot --help
shell> parrot --help-debug