Node:Advice-points, Next:, Previous:Breakpoint Actions, Up:Advanced Debugging



Advice-points

As mentioned earlier, there are two kinds of breakpoints: spypoints and advice-points. The main purpose of spypoints is to support interactive debugging. In contrast with this, advice-points can help you to perform non-interactive debugging activities. For example, the following advice-point will check a program invariant: whether the condition Y-X<3 always holds at exit from foo(X,Y).

| ?- add_breakpoint([goal(foo(X,Y)),advice]
                    -[exit,\+true(Y-X<3),trace], _).
% Conditional advice point for user:foo/2 added, BID=1
true ?
yes
% advice
| ?- foo(4, X).
X = 3 ?
yes
% advice
| ?- foo(9, X).
        3      3 Exit: foo(7,13) ? n
        2      2 Exit: foo(8,21) ?

The test part of the above breakpoint contains a goal test, and the advice condition, making it an advice-point. (You can also include the debugger condition in spypoint specs, although this is the default interpretation.)

The action part starts with the exit port condition. Because of this the rest of the action part is evaluated only at Exit ports. By placing the port condition in the action part, we ensure the creation of a procedure box at the Call port, as explained earlier.

The second condition in the action part, \+true(Y-X<3), checks if the invariant is violated. If this happens, the third condition sets the mode action variable to trace, switching on the interactive debugger.

Following the add_breakpoint/2 call the above example shows two top-level calls to foo/2. The invariant holds within the first goal, but is violated within the second. Notice that the advice mechanism works with the interactive debugger switched off.

You can ask the question, why do we need advice-points? The same task could be implemented using a spypoint. For example:

| ?- add_breakpoint(goal(foo(X,Y))-[exit,\+true(Y-X<3),leash], _).
% The debugger will first zip -- showing spypoints (zip)
% Conditional spypoint for user:foo/2 added, BID=1
true ?
yes
% zip
| ?- foo(4, X).
X = 3 ?
yes
% zip
| ?- foo(9, X).
 *      3      3 Exit: foo(7,13) ? z
 *      2      2 Exit: foo(8,21) ?

The main reason to have a separate advice mechanism is to be able to perform checks independently of the interactive debugging. With the second solution, if you happen to start some interactive debugging, you cannot be sure that the invariant is always checked. For example, no spypoints will be activated during a skip. In contrast with this, the advice mechanism is watching the program execution all the time, independently of the debugging mode.

Advice-points are handled in very much the same way as spypoints are. When arriving at a port, advice-point selection takes place first, followed by spypoint selection. This can be viewed as the debugger making two passes over the current breakpoints, considering advice-points only in the first pass, and spypoints only in the second.

In both passes the debugger tries to find a breakpoint which can be activated, checking the test and action parts, as described earlier. However, there are some differences between the two passes:

Having performed advice processing, the debugger inspects the command variable. The command values different from proceed and flit are called divertive, as they alter the normal flow of control (e.g. proceed(...,...)), or involve user interaction (ask). If the command value is divertive, then the prescribed action is performed immediately, without executing the spypoint selection process. Otherwise, if command = proceed, it is noted that the advice part requests the building of a procedure box. Next, the second, spypoint processing pass is carried out, and possible user interaction takes place, as described earlier. A procedure box is built if either the advice-point or the spypoint search requests this.

Finally, let us show another example, a generic advice point for collecting branch coverage information:

| ?- add_breakpoint([advice,goal(_:_),call,line(F,L)]
                    -[true(assert(line_reached(F,L))),flit], _).
% Generic advice point added, BID=1
true ?
yes
% advice,source_info
| ?- foo(4,X).
X = 3 ? ;
no
% advice,source_info
| ?- setof(X, line_reached(F,X), S).
F = '/home/bob/myprog.pl',
S = [31,33,34,35,36] ?

The advice-point will only be applicable at Call ports for which source information is available. It will then assert a fact with the file name and the line number. Finally, it will set the command variable to flit. This reflects the fact that the advice-point does not request the building of a procedure box.

It is important to note that the recording of the line numbers reached is performed independently of the interactive debugging.

Further examples of advice-points are available in library(debugger_examples).