This section describes in detail how the debugger handles the breakpoints. For the purpose of this section disabled breakpoints are not taken into account: whenever we refer to the existence of some breakpoint(s), we always mean the existence of enabled breakpoint(s).
The Prolog engine can be in one of the following three states with respect to the debugger:
if there are no advice-points and the debugger is either switched off, or doing a skip;
if the debugger is in trace or debug mode (creeping or leaping), or there are any generic breakpoints;
in all other cases.
In the selective debugging state only those predicate invocations are examined, for which there exists a specific breakpoint. In the full debugging state all invocations are examined, except those calling a predicate of a hidden module (but even these will be examined, if there is a specific breakpoint for them). In the no debugging state the debugger is not entered at predicate invocations.
Now we describe what the debugger does when examining an invocation of a predicate, i.e. executing its Call port. The debugger activities can be divided into three stages: advice-point processing, spypoint processing and interaction with the user. The last stage may be repeated several times before program execution continues.
The first two stages are similar, as they both search for an applicable breakpoint (spypoint or advice-point). This common breakpoint search is carried out as follows. The debugger considers all breakpoints of the given type, most recent first. For each breakpoint, the test part of the spec is evaluated, until one successful is found. Any variable bindings created in this successful evaluation are then discarded (this is implemented by enclosing it in double negation). The first breakpoint, for which the evaluation of the test part succeeds is selected. If such a breakpoint can be found, then the breakpoint search is said to have completed successfully, otherwise it is said to have failed.
If a breakpoint has been selected, then its action part is
evaluated, normally setting some debugger action variables. If the
action part fails, then as a side-effect, it is ensured that a
procedure box will be built. This is achieved by changing the
value of the command
action variable from flit
to
proceed
.
Having described the common breakpoint search, let us look at the
details of the first stage, advice-point processing. This stage is
executed only if there are any advice-points set. First, the
debugger action variables are initialized: mode
is set to the
current debugger mode, command
to proceed
and show
to silent
. Next, advice-point search takes place. If this
fails, then command
is set to flit
, otherwise its value is
unchanged.
After completing the advice-point search the command
variable is examined. If its value is divertive, i.e. different from
proceed
and flit
, then the spypoint search stage is
omitted, and the debugger continues with the third stage. Otherwise, it
is noted that the advice-point processing has requested the building
of a procedure box (i.e. command = proceed
), and the
debugger continues with the second stage.
The second stage is spypoint processing. This stage is skipped if
the debugger is switched off or doing a skip (mode
is off
or skip(_)
). First the show
and command
variables are re-assigned, based on the hiddenness of the
predicate being invoked, the debugger mode, and the leashing
status of the port. If the predicate is both defined in, and
called from a hidden module, then their values will be
silent
and flit
. An example of this is when a
built-in predicate is called from a hidden module, e.g.
from a library. Otherwise, in trace mode, their values are
print
and ask
for leashed ports, and
print
and proceed
for unleashed ports. In
debug mode, the variables are set to silent
and
proceed
, while in zip mode to silent
and flit
(Breakpoint Actions contains a tabulated listing of these
initialization values).
Having initialized the debugger action variables, the spypoint
search phase is performed. If an empty action part has been selected in
a successful search, then show
and command
are set to
print
and ask
. The failure of the search is ignored.
The third stage is the interactive part. First, the goal in
question is displayed according to the value of show
. Next, the
value of command
is checked: if it is other than ask
, then
the interactive stage ends. Otherwise, (it is ask
), the variable
show
is re-initialized to print
, or to
print-Sel
, if its value was of form
Method-Sel
. Next, the debugger prompts the user for a
command, which is interpreted either in the standard way, or through
user:debugger_command_hook/2
. In both cases the debugger action
variables are modified as requested, and the interactive part is
repeated.
After the debugger went through all the three stages, it decides whether
to build a procedure box. This will happen if either the
advice-point stage or the other two stages require it. The latter
is decided by checking the command
variable: if that is
flit
or flit(Old,New)
, then no procedure
box is required by the spypoint part. If the advice-point
does require the building of a procedure box, then the above
command
values are replaced by proceed
and
proceed(Old,New)
, respectively.
At the end of the process the value of mode
will be the new
debugging mode, and command
will determine what the debugger will
do; see Action Variables.
A similar three-stage process is carried out when the debugger arrives
at a non-Call port of a predicate. The only difference is
that the building of a procedure box is not considered
(flit
is equivalent to proceed
), and the hiddenness of the
predicate is not taken into account.
While the Prolog system is executing the above three-stage process for any of the ports, it is said to be inside the debugger. This is relevant, because some of the conditions can only be evaluated in this context.