15.1 The Box Model
The ECLiPSe debugger is based on a port model which is an extension
of the classical Box Model commonly used in Prolog debugging.
A procedure invocation (or goal) is represented by a box with entry
and exit ports. Each time a procedure is invoked, a box is created
and given a unique invocation number. The invocations of subgoals of
this procedure are seen as boxes inside this procedure box.
Figure 15.1: The box model |
Tracing the flow of the execution consists in tracing the crossing of
the execution flow through any of the port of the box.
The five basic ports of the box model of ECLiPSe are the CALL, EXIT,
REDO, FAIL and NEXT ports, the suspension facilities are traced through
the DELAY and RESUME ports, and the exceptional exit is indicated by LEAVE.
- CALL:
- When a procedure is invoked, the flow of the execution
enters the procedure box by its CALL port and enters the first clause
box which could (since not all clauses are tried, some of them being
sure to fail, i.e., indexing is shown) unify with the goal.
It may happen that a procedure is
called with arguments that make it sure to fail (because of
indexing). In such cases, the flow does not enter any clause box.
For each CALL port a new procedure box is created and is given:
-
an invocation number that is one higher than that given for
the most recent CALL port. This allows to uniquely identify a
procedure invocation and all its corresponding ports.
- a level that is one higher than that of its parent goal.
The displayed variable instantiations are the ones at call time,
i.e., before the head unification of any clause.
- EXIT:
- When a clause of a predicate succeeds (i.e., unification
succeeded and all procedures called by the clause succeeded),
the flow gets out of the box by the EXIT port of both boxes (only
the EXIT port of the procedure box is traced).
When a procedure exits non-deterministically (and there are still
other clauses to try on that procedure or one of its children goals
has alternatives which could be resatisfied), the EXIT port is traced
with an asterisk (*EXIT). When the last possibly matching clause of a
procedure is exited, the exit is traced without asterisk. This means
that this procedure box will never be retried as there is no other
untried alternative.
The instantiations shown in the EXIT port are the ones at exit time,
they result from the (successful) execution of the procedure.
- FAIL:
- When a clause of a procedure fails (because head unification
failed or because a sub-goal failed), the flow of the execution exits
the clause box and leaves the procedure box via the FAIL port.
Note that the debugger cannot display any argument information at
FAIL ports (an ellipsis
...
is displayed instead for each argument). - NEXT:
- If a clause fails and there is another possibly
matching clause to try, then that one is tried for unification.
The flow of the execution from the failure of one clause to the
head unification of a following clause is traced as a NEXT port.
The displayed variable instantiations are the same as those of the
corresponding CALL or REDO port.
- ELSE:
-
This is similar to the NEXT port, but indicates that the next branch of a
disjunction (;/2)
it tried after the previous branch failed.
The predicate that gets displayed with the port is the predicate
which contains the disjunction (the immediate ancestor).
- REDO:
- When a procedure box is exited trough an *EXIT port,
the box can be retried later to get a new solution. This will happen when a
later goal fails. The backtracking will cause failing of all
procedures that do not have any alternative, then the execution flow
will enter a
procedure box that an contains alternative through a REDO port.
Two situations may occur: either the last tried clause has called a
procedure that has left a choice point (it has exited through an
*EXIT port). In that case the nested procedure box is re-entered
though another REDO-port.
Otherwise, if the last clause tried does not contain any
nondeterministically exited boxes, but there are other untried clauses
in the procedure box, the next possibly matching clause will be tried.
The last REDO port in such a sequence is the one which contains the
actual alternative that is tried. The variable instantiations for all
REDO ports in such a sequence are the ones corresponding to the call
time of the last one.
- LEAVE:
-
This port allows to trace the execution of exceptions. Exceptions
are either raised implicitly by built-in predicates (in which case the
built-in itself exits via the LEAVE port), or explicitly through a call to
throw/1
(or exit_block/1).
All ancestors of the predicate that raised the exception will subsequently
exit via a LEAVE port, until a
catch/3
(or block/3)
is found, whose second argument matches the exception.
This invocation of
catch/3
then passes a NEXT port (at which point the exception has been caught),
and then execution continues via a normal call of the recovery goal
(the third argument of the
catch/3).
As with the FAIL port, no argument values are displayed in the LEAVE port.
- DELAY:
-
The displayed goal becomes suspended. This is a singleton port, it does
not enter or leave a box. However, a new invocation number is assigned
to the delayed goal, and this number will be used in the matching RESUME port.
The DELAY port is caused by one of the built-in predicates
suspend/3,
suspend/4,
make_suspension/3
or a delay clause.
The port is displayed just after the delayed goal has been created.
- RESUME:
- When a waking condition causes the resuming of
a delayed goal, the procedure box is entered through its RESUME
port. The box then behaves as if it had been entered through its CALL
port. The invocation number is the same as in its previous DELAY port.
which makes it easy to identify corresponding delay and resume events.
However the depth level of the RESUME corresponds to the waking situation.
It is traced like a subgoal of the goal which has caused the waking.
In the rest of this chapter the user interface to the debugger is
described, including the commands available in the debugger itself as
well as built-in predicates which influence it. Some of the debugger
commands are explained using an excerpt of a debugger session. In
these examples, the user input is always underlined (it is in fact
not always output as typed) to distinguish it from the computer output.
15.1.1 Breakpoints
Breakpoints can be set on specific calls to a predicate, i.e., on a specific
body goal in the source, so that the
debugger will stop only at a CALL port only when that specific body goal is
executed. A breakpoint is specify by giving the source file and the line
number where the body goal is.
For example, if the following predicate is in a file called newtop,
with the following line numbers:
243 check_words([],[]).
244 check_words([Word|Words],[RevWord|RevWords]) :-
245 check_words(Words,RevWords).
The breakpoint for the body goal check_words(Words, RevWords) would
be newtop:245. Note that the file name must be sufficiently specified
for ECLiPSe to find the file from your current working directory.
For a call that has a breakpoint set, the execution will stop when the call
is made, i.e., at the CALL port for that specific body goal.