Node:Storing User Information in the Backtrace, Next:Hooks Related to Breakpoints, Previous:Accessing Past Debugger States, Up:Advanced Debugging
The debugger allows the user to store some private information in the
backtrace, namely it allocates a Prolog variable in each break level and
in each invocation. The breakpoint test private(Priv)
unifies Priv with the private information associated with the
break level, while the test goal_private(GPriv)
unifies
GPriv with the Prolog variable stored in the invocation.
Both variables are initially unbound, and behave as if they were passed around the program being debugged in additional arguments. This implies that any variable assignments done within these variables are undone on backtracking.
The private
condition practically gives you access to a Prolog
variable shared by all invocations of a break level. This makes it
possible to remember a term and look at it later, in a possibly more
instantiated form, as shown by the following example.
memory(Term) :- execution_state(private(P)), memberchk(myterm(Term), P). | ?- trace, append([1,2,3,4], [5,6], L). 1 1 Call: append([1,2,3,4],[5,6],_514) ? @ | :- append(_,_,L)^memory(L). 1 1 Call: append([1,2,3,4],[5,6],_514) ? c 2 2 Call: append([2,3,4],[5,6],_2064) ? c 3 3 Call: append([3,4],[5,6],_2422) ? c 4 4 Call: append([4],[5,6],_2780) ? @ | :- memory(L), write(L), nl. [1,2,3|_2780] 4 4 Call: append([4],[5,6],_2780) ?
The predicate memory/1
receives the term to be remembered in its
argument. It gets hold of the private field associated with the break
level in variable P
, and calls memberchk/2
(see Lists), with the term to be remembered, wrapped in
myterm
, as the list element, and the private field, as the list.
Thus the latter, initially unbound variable, is used as an open-ended list.
For example, when memory/1
is called for the first time, the private
field gets instantiated to [myterm(Term)|_]
. If later you call
memory/1
with an uninstantiated argument, it will retrieve the
term remembered earlier and unify it with the argument.
The above trace excerpt shows how this utility predicate can be used to
remember an interesting Prolog term. Within invocation number 1 we call
memory/1
with the third, output argument of append/3
,
using the @
command (see Debug Commands). A few tracing
steps later, we retrieve the term remembered and print it, showing its
current instantiation. Being able to access the instantiation status of
some terms of interest can be very useful in debugging. In
library(debugger_examples)
we describe new debugger commands for
naming Prolog variables and providing name-based access to these
variables, based on the above technique.
In the above example we could have avoided the use of memberchk/2
by simply storing the term to be remembered in the private field itself
(memory(Term) :- execution_state(private(Term)).
). But this would
make the private field unusable for other purposes. For example,
the finite domain constraint debugger (see FDBG) would stop working,
as it relies on the private fields.
There is only a single private variable of both kinds within the given
scope. Therefore the convention of using an open list for storing
information in private fields, as shown in the above example, is very
much recommended. The different users of the private field are
distinguished by the wrapper they use (e.g. myterm/1
above,
fdbg/1
for the constraint debugger, etc.). Future SICStus Prolog
releases may enforce this convention by providing appropriate breakpoint
tests.
We now present an example of using the goal private field. Earlier we
have shown a spypoint definition which made a predicate invisible in the
sense that its ports are silently passed through and it is
automatically skipped over. However, with that earlier solution, execution
always continues in trace mode after skipping. We now improve the
spypoint definition: the mode in which the Call port was reached is
remembered in the goal private field, and the mode action variable is
set to this value at Exit ports.
mode_memory(Mode) :- execution_state(goal_private(GP)), memberchk(mymode(Mode), GP). | ?- spy(foo/2, mode(M), -[silent,proceed, true(mode_memory(MM)), ( call -> true(MM=M), inv(Inv), skip(Inv) ; exit -> mode(MM) ; true )]).
Above we first define an auxiliary predicate mode_memory/1
, which
uses the open list convention for storing information in the goal
private field, applying the mymode/1
wrapper. This predicate is
used in the action part of the spypoint, unifying the mode memory with the
MM
variable. We then branch in the action part: at Call ports the
uninstantiated MM
is unified with the current mode. At Exit ports
MM
holds the mode saved at the Call port, so by issuing the
mode(MM)
action, this mode is re-activated. At all other ports we
just silently proceed without changing the debugger mode.