Node:Breakpoint Actions, Next:Advice-points, Previous:Specific and Generic Breakpoints, Up:Advanced Debugging
The action part of a breakpoint spec supplies information to the debugger as to what should be done when the breakpoint is activated. This is achieved by setting the three so called debugger action variables, listed below, together with their most important values.
show
variable prescribes how the debugged goal should be
displayed:
print
-- write the goal according to the debugger_print_options
Prolog flag.
silent
-- do not display the goal.
command
variable prescribes what should the debugger do:
ask
-- ask the user.
proceed
-- continue the execution without stopping, creating a procedure
box for the current goal at the Call port,
flit
-- continue the execution without stopping, without creating a procedure
box for the current goal at the Call port.
mode
variable prescribes in what mode the debugger should
continue the execution:
trace
-- creeping.
debug
-- leaping.
zip
-- zipping.
off
-- without debugging.
For example, the breakpoint below specifies that whenever the Exit port
of foo/2
is reached, no trace message should be output, no
interaction should take place and the debugger should be switched off.
| ?- add_breakpoint([pred(foo/2),port(exit)]- [show(silent),command(proceed),mode(off)], _).
Here, the action part consists of three actions, setting the three
action variables. This breakpoint spec can be simplified by omitting
the wrappers around the variable values, as the sets of possible values
of the variables are all disjoint. If we use spy/2
then the
pred
wrapper goes away, too, resulting in a much more concise,
equivalent formulation of the above breakpoint:
| ?- spy(foo/2,exit-[silent,proceed,off])
Let us now revisit the process of breakpoint selection. When the
debugger arrives at a port it first initializes the action variables
according to the current debugging and leashing modes, as shown below:
debugging leashing | Action variables mode mode | show command mode --------------------------------|------------------------------- trace at leashed port | print ask trace | trace at unleashed port | print proceed trace | debug - | silent proceed debug | zip - | silent flit zip
It then considers each breakpoint, most recent first, until it finds a
breakpoint whose test part succeeds. If such a breakpoint is found, its
action part is evaluated, normally changing the action variable
settings. A failure of the action part is ignored, in the sense that the
breakpoint is still treated as the selected one. However, as a side
effect, a procedure box will always be built in such cases. More
precisely, the failure of the action part causes the flit
command
value to be changed to proceed
, all other command values being
left unchanged. This is to facilitate the creation of breakpoints which
stop at non-Call ports (see below for an example).
If no applicable breakpoint is found, then the action variables remain unchanged.
The debugger then executes the actions specified by the action variables. This process, referred to as the action execution, means the following:
mode
action variable.
show
variable.
command
variable.
Specifically, if command
is ask
, then the user is prompted
for a debugger command, which in turn is converted to new assignments to
the action variables. The debugger will then repeat the action execution
process, described above. For example, the c
(creep) interactive
command is converted to [silent,proceed,trace]
, the d
(display) command to [display,ask]
(when command is ask
,
the mode is irrelevant), etc.
The default values of the action variables correspond to the standard debugger behavior described in Basic Debug. For example, when an unleashed port is reached in trace mode, then a trace message is printed and the execution proceeds in trace mode, without stopping. In zip mode, no trace message is shown, and execution continues in zip mode, without building procedure boxes at Call ports.
If a spypoint is found applicable, but it does not change the
action variables, then the debugger will behave as if no breakpoint was
found. For example:
| ?- spy(foo/2, parent_pred(P)-true(format('Called from:~w~n',[P]))).
This spypoint will produce some output at ports of foo/2
called
from within an interpreted clause, but otherwise will not influence the
debugger. We see here a parent_pred
condition with a variable
argument, placed in the test part and a true
condition appearing
in the action part. Goals with side effects should only be called from
the action part, because the test part may be evaluated multiple times
for a single port -- that is why this true
condition appears in
the action part. On the other hand parent_pred
can fail (if the
current goal is invoked from compiled code), so it should normally be
put in the test part.
Note that an action part which is []
or omitted is actually
treated as [print,ask]
. Again, this is the standard behavior of
spypoints, as described in Basic Debug.
Let us look at some simple examples of what other effects can be achieved
by appropriate action variable settings:
| ?- spy(foo/2, -[print,proceed]). % Conditional spypoint for user:foo/2 added, BID=1 yes | ?- debugging. (...) Breakpoints: 1 * user:foo/2 if []-[print,proceed]
This is an example of an unleashed spypoint: it will print a trace
message passing each port of foo/2
, but will not stop there. Note
that because of the proceed
command a procedure box will be
built, even in zip mode, and so the debugger will be activated at
non-Call ports of foo/2
. Also notice that a breakpoint spec with
an empty test part can be written -
Actions.
The next example is a variant of the above:
| ?- spy(foo/2, -[print,flit]).
This will print a trace message at the Call port of foo/2
and
will then continue the execution in the current debugging mode, without
building a procedure box for this call. This means that the debugger
will not be able to notice any other ports of foo/2
.
Now let us address the task of stopping at a specific non-Call port of a
predicate. For this to work in zip mode, one has to ensure that a
procedure box is built at the Call port. In the following example, the
first spypoint causes a box to be built for each call of foo/2
,
while the second one makes the debugger stop when the Fail port of
foo/2
is reached.
| ?- spy(foo/2, call-proceed), spy(foo/2, fail). % Conditional spypoint for user:foo/2 added, BID=1 % Conditional spypoint for user:foo/2 added, BID=2
You can achieve the same effect with a single spypoint, by putting the
port
condition in the action part, rather than in the
test part.
| ?- spy(foo/2, -[fail,print,ask]).
Here, when the execution reaches the Call port of foo/2
, the test
part (which contains the pred(foo/2)
condition only) succeeds, so
the breakpoint is found applicable. However, the action part fails at
the Call port. This has a side effect in zip mode, as the default
flit
command value is changed to proceed
. In other modes
the action variables are unaffected. The net result is that a procedure
box is always built for foo/2
, which means that the debugger will
actually reach the Fail port of this predicate. When this happens, the
action part succeeds, and executing the actions print,ask
will
cause the debugger to stop.
Note that we have to explicitly mention the print,ask
actions
here, because the action part is otherwise nonempty (contains the
fail
condition). It is only the empty or missing action part,
which is replaced by the default [print,ask]
. If you want to
include a condition in the action part, you have to explicitly mention
all action variable settings you need.
To make this simpler, the debugger handles breakpoint condition macros,
which expand to other conditions. For example leash
is a macro
which expands to [print,ask]
. Consequently, the last example can
be simplified to:
| ?- spy(foo/2, -[fail,leash]).
Similarly, the macro unleash
expands to [print,proceed]
,
while hide
to [silent,proceed]
.
We now briefly describe further possible settings to the action variables.
The mode
variable can be assigned the values
skip(Inv)
and qskip(Inv)
, meaning skipping and
quasi-skipping until a port is reached whose invocation number is less
or equal to Inv. When the debugger arrives at this port it sets
the mode
variable to trace
.
It may be surprising that skip(...)
is a mode, rather than a
command. This is because commands are executed and immediately
forgotten, but skipping has a lasting effect: the program is to be run
with no debugging until a specific point, without creating new procedure
boxes, and ignoring the existing ones in the meantime.
Here is an example using the skip
mode:
| ?- spy(foo/2,call-[print,proceed,inv(Inv),skip(Inv)]).
This breakpoint will be found applicable at Call ports of
foo/2
. It will print a trace message there and will skip over
to the Exit or Fail port without stopping. Notice that the number of the
current invocation is obtained in the action part, using the inv
condition with a variable argument. A variant of this example follows:
| ?- spy(foo/2,-[silent,proceed, ( call -> inv(Inv), skip(Inv) ; true )]).
This spypoint makes foo/2
completely invisible in the output of
the debugger: at all ports we silently proceed (i.e. display nothing
and do not stop). Furthermore, at the Call port we perform a skip, so
neither foo/2
itself, nor any predicate called within it will
ever be shown by the debugger.
Notice the use of the true/0
test in the above conditional!
This is a breakpoint test which always succeeds. The debugger also
recognizes false
as a test which always fails. Note that in
breakpoint conditions fail
and false
are completely
different, as the first one is an abbreviation of port(fail)
!
The show
variable has four additional value patterns. Setting it
to display
, write
, or write_term(Options)
will result in the debugged goal G being shown using
display(G)
, writeq(G)
, or
write_term(G, Options)
, respectively. The fourth
pattern, Method-Sel
, can be used for replacing the
goal in the trace message by one of its subterms, the one pointed to by
the selector Sel.
For example, the following spypoint instructs the debugger to stop at
each port of foo/2
, and to only display the first argument of
foo/2
in the trace message, instead of the complete goal.
| ?- spy(foo/2, -[print-[1],ask]). % Conditional spypoint for user:foo/2 added, BID=1 yes | ?- foo(5,X). * 1 1 Call: ^1 5 ?
The command
variable has several further value patterns. At a
Call port this variable can be set to
proceed(OldGoal,NewGoal)
. This instructs the debugger
to first build a procedure box for the current goal, then to unify it
with OldGoal and finally execute NewGoal in its place (cf.
the u
(unify) interactive debugger command). A variant of this
setting, flit(OldGoal,NewGoal)
, has the same effect,
but does not build a procedure box for OldGoal.
We now just briefly list further command values (for the details, see
Action Variables). Setting command
to
exception(E)
will raise an exception E, abort
will abort the execution. The values retry(Inv)
,
reexit(Inv)
, redo(Inv)
, fail(Inv)
will cause the debugger to go back to an earlier Call, Exit, Redo, or
Fail port with invocation number Inv (cf. the j
(jump)
interactive debugger command).
Finally, let us mention that the conditions show
, command
,
and mode
can also occur in the test part. In such cases they will
read, rather than set, the action variables. For example:
| ?- spy(foo/2, mode(trace)-show(print-[1])).
This spypoint will be found applicable only in trace mode (and will
cause the first argument of foo/2
to appear in the trace
message). (The mode
and show
wrappers can be omitted in
the above example, they are used only to help interpreting the
breakpoint spec.)