5.8 The Processing of Breakpoints

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:

no debugging

if there are no advice-points and the debugger is either switched off, or doing a skip;

full debugging

if the debugger is in trace or debug mode (creeping or leaping), or there are any generic breakpoints;

selective debugging

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.

Send feedback on this subject.