Debugging in Yacas
Introduction
When writing a code segment, it is generally a good idea to separate the
problem into many small functions. Not only can you then reuse these
functions on other problems, but it makes debugging easier too.
For debugging a faulty function, in addition to the usual trial-and-error method and the "print everything" method, Yacas offers some trace facilities. You can try to trace applications of rules during evaluation of the function (TraceRule(), TraceExp()) or see the stack after an error has occurred (TraceStack()). Finally, you may want to run a debugging version of Yacas.
The trace facilities
The trace facilities are:
- TraceExp : traces the full expression, showing all calls to user- or system-defined functions, their arguments, and the return values. For complex functions this can become a long list of function calls.
- TraceRule : traces one single user-defined function (rule). It shows each invocation, the arguments passed in, and the returned values. This is useful for tracking the behaviour of that function in the environment it is intended to be used in.
- TraceStack : shows a few last function calls before an error has occurred.
The online manual pages (e.g. ?TraceStack) have more information about the use of these functions.
An example invocation of TraceRule is
In> TraceRule(x+y)2+3*5+4;
|
Which should then show something to the effect of
TrEnter(2+3*5+4);
TrEnter(2+3*5);
TrArg(2,2);
TrArg(3*5,15);
TrLeave(2+3*5,17);
TrArg(2+3*5,17);
TrArg(4,4);
TrLeave(2+3*5+4,21);
Out> 21;
|
The Yacas debugger program
A debugging version of yacas comes with the
distribution. The yacas debugger consists of:
- ./yacasdebug, a separate program
- libyacasdebug.a, a static library that can be linked in
The debug version has to be built separately from the "production"
version of Yacas (all source files have to be recompiled).
Why introduce a debug version?
The reason for introducing a debug version is that for a debugger
it is often necessary to introduce features that make the interpreter
slower. For the main kernel this is unacceptable, but for a debugging
version this is defendable. It is good for testing small programs,
to see where a calculation breaks. Having certain features only
in the debug version keeps the release executable can be kept lean
and mean, while still offering advanced debug features.
How to build yacasdebug
The yacas debugging components can be built by the command
or, alternatively, by going into the
src/ directory and using the file makefile.debug to build
it:
cd src/
make -f makefile.debug
|
Note the object files generated are called *-d.o and are suited for the
normal Yacas executable. The static library libyacasdebug.a is built instead
of the normal libyacas.a.
The file makefile.debug was written to compile under Linux, but
should work on other platforms with little tweaks.
After the build the debug version of Yacas can be found in the src/ directory.
To invoke the debugger, type
./yacasdebug --rootdir ../scripts/
|
The Yacas debugger assumes the current directory for scripts,
so the --rootdir command line option is required.
What does yacasdebug offer?
The Yacas debugger is in development still, but already has
some useful features.
When you build yacasdebug and run a command, it will:
- keep track of the memory allocated and freed, and show any memory leaks
when you quit the program.
- show which files are loaded to read function definitions and when.
- keep a file name and line number for each object loaded from file, for
debugging purposes.
- show you the stack trace when evaluation goes into an infinite recursion
(equivalent of always using TraceStack) and print file names and line numbers
for all rules.
The libyacasdebug.a library is used by proteusdebugger, a graphical
front-end for debugging which is also still in development.
Future versions will have the ability to step through code and to
watch local and global variables while executing, modifying them
on the fly.