The debugger allows the user to store some private information in the
backtrace. It allocates a Prolog variable in each break
level and in each invocation. The breakpoint test
) unifies Priv with the private
information associated with the break level, while the test
) 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.
In practice, the
private condition 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(,[5,6],_2780) ? @ | :- memory(L), write(L), nl. [1,2,3|_2780] 4 4 Call: append(,[5,6],_2780) ?
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 lib-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
[myterm(Term)|_]. If later you call
memory/1 with an uninstantiated argument, it will
retrieve the term remembered earlier and unify it with the
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
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
describe new debugger commands for naming Prolog variables and
providing name-based access to these variables, based on the above
We could have avoided the use of
memberchk/2 in the example by
simply storing the term to be remembered in the private field
memory(Term) :- execution_state(private(Term)).). But
this would have made the private field unusable for other purposes. For
example, the finite domain constraint debugger (see lib-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 ended 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.
fdbg/1 for the constraint debugger, etc.). Future 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 that 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 reset to this value at the Exit port.
mode_memory(Mode) :- execution_state(goal_private(GP)), memberchk(mymode(Mode), GP). | ?- spy(foo/2, -[silent,proceed, true(mode_memory(MM)), ( call -> get(mode(MM)), inv(Inv), skip(Inv) ; exit -> mode(MM) ; true )]).
Here, we first define an auxiliary predicate
which uses the open list convention for storing information in the
goal private field, applying the
mymode/1 wrapper. We then
create a spypoint for
foo/2, whose action part first sets
command action variables. Next, the
mode_memory/1 predicate is called, unifying the mode
memory with the
MM variable. We then branch in the action
part: at Call ports the uninstantiated
unified with the current mode, and a
skip command is
issued. At Exit ports
MM holds the mode saved at the Call
port, so the
mode(MM) action re-activates this mode. At all
other ports we just silently proceed without changing the debugger