5.6.3 Breakpoint Tests

This section gives a tour of the most important simple breakpoint tests. In all examples here the action part will be empty. Note that the examples are independent, so if you want to try out these you should get rid of the old breakpoints (e.g. using ?- nospyall.) before you enter a new one.

The goal(...) test is a generalization of the pred(...) test, as it allows us to check the arguments of the invocation. For example:

     | ?- add_breakpoint(goal(foo(1,_)), _).
     % Conditional spypoint for user:foo/2 added, BID=1

The goal(G) breakpoint test specifies that the breakpoint should be applied only if the current goal is an instance of G, i.e. G and the current goal can be unified without substituting any variables in the latter. This unification is then carried out. The goal(G) condition is thus equivalent to the subsumes(G,CurrentGoal) test (subsumes/2 is defined in library(terms), see lib-terms).

In the above example the debugger will stop if foo/2 is called with 1 as its first argument, but not if the first argument is, say, 2, nor if it is a variable.

You can use non-anonymous variables in the goal test, and then put further constraints on these variables using the true condition:

     | ?- add_breakpoint([goal(foo(X,_)),true(X>1)], _).
     % Conditional spypoint for user:foo/2 added, BID=1

Here the first test, goal, specifies that we are only interested in invocations of foo/2, and names the first argument of the goal as X. The second, the true/1 test, specifies a further condition stated as a Prolog goal: X is greater than 1 (we assume here that the argument is numeric). Thus this breakpoint will be applicable if and only if the first argument of foo/2 is greater than 1. Generally, an arbitrary Prolog goal can be placed inside the true test: the test will succeed if and only if the goal completes successfully.

Any variable instantiations in the test part will be undone before executing the action part, as the evaluation of the test part is enclosed in a double negation (\+ \+ (...)). This ensures that the test part has no effect on the variables of the current goal.

Both the pred and the goal tests may include a module name. In fact, the first argument of add_breakpoint is module name expanded, and the (explicit or implicit) module name of this argument is then inherited by default by the pred, goal, and true tests. Notice the module qualification inserted in front of the breakpoint spec of the last example, as shown in the output of the debugging/0 built-in predicate:

     | ?- debugging.
     (...)
     Breakpoints:
           1 *  user:foo/2 if user:[goal(foo(A,B)),true(A>1)]

As no explicit module qualifications were given in the tests, this breakpoint spec is transformed to the following form:

     [goal(user:foo(A,B)),true(user:(A>1))]

For exported predicates, a pred or goal test will be found applicable for all invocations of the predicate, irrespective of the module the call occurs in. When you add the breakpoint you can use the defining or an importing module name, but this information is not remembered: the module name is “normalized”, i.e. it is changed to the defining module. The example below shows this: although the spypoint is placed on user:append, the message and the breakpoint list both mention lists:append.

     | ?- use_module(library(lists)).
     (...)
     % module lists imported into user
     (...)
     | ?- spy user:append.
     % Plain spypoint for lists:append/3 added, BID=1
     | ?- debugging.
     (...)
     Breakpoints:
           1 +  lists:append/3

Note that the debugger does not stop inside a library predicate when doing an exhaustive trace. This is because the library modules are declared hidden (see ref-mod), and no trace is produced for calls inside hidden modules that invoke predicates defined in hidden modules. However, a spypoint is always shown in the trace, even if it occurs in a hidden module:

      +      1      1 Call: append([1,2],[3,4],_531) ? <RET>
      +      2      2 Call: lists:append([2],[3,4],_1182) ? <RET>
      +      3      3 Call: lists:append([],[3,4],_1670) ? <RET>
      +      3      3 Exit: lists:append([],[3,4],[3,4]) ? <RET>
     (...)

You can narrow a breakpoint to calls from within a particular module by using the module test, e.g.

     | ?- add_breakpoint([pred(append/3),module(user)], _).
     % The debugger will first zip -- showing spypoints (zip)
     % Conditional spypoint for lists:append/3 added, BID=1
     % zip
     | ?- append([1,2], [3,4], L).
      *      1      1 Call: append([1,2],[3,4],_531) ? <RET>
      *      1      1 Exit: append([1,2],[3,4],[1,2,3,4]) ? <RET>
     L = [1,2,3,4]

With this spypoint, the debugger will only stop at the invocations of append/3 from the user module.

Note that calling module information is not kept by the compiler for the built-in predicates, therefore the module test will always unify its argument with prolog in case of compiled calls to built-in predicates.

There are two further interesting breakpoint tests related to invocations: inv(Inv) and depth(Depth). These unify their arguments with the invocation number and the depth, respectively (the two numbers shown at the beginning of each trace message). Such tests are most often used in more complex breakpoints, but there may be some simple cases when they are useful.

Assume you put a plain spypoint on foo/2, and start leaping through your program. After some time, you notice some inconsistency at an Exit port, but you cannot go back to the Call port for retrying this invocation, because of side-effects. So you would like to restart the whole top-level goal and get back to the Call port of the suspicious goal as fast as possible. Here is what you can do:

     | ?- spy foo/2.
     % Plain spypoint for user:foo/2 added, BID=1
     | ?- debug, foo(23, X).
     % The debugger will first leap -- showing spypoints (debug)
      +      1      1 Call: foo(23,_414) ? l
     (...)
      +     81     17 Call: foo(7,_9151) ? l
      +     86     18 Call: foo(6,_9651) ? l
      +     86     18 Exit: foo(6,8) ? -
     % Plain spypoint for user:foo/2, BID=1, removed (last)
            86     18 Exit: foo(6,8) ? *
     Placing spypoint on user:foo/2 with conditions: inv(86).
     % Conditional spypoint for user:foo/2 added, BID=1
      *     86     18 Exit: foo(6,8) ? a
     % Execution aborted
     % source_info
     | ?- debug, foo(23, X).
     % The debugger will first leap -- showing spypoints (debug)
      *     86     18 Call: foo(6,_2480) ? <RET>

When you reach the Exit port of the suspicious invocation (number 86), you remove the plain spypoint (via the - debugger command), and add a conditional one using the ‘*’ debugger command. This automatically includes pred(foo/2) among the conditions and displays the prompt ‘Placing spypoint ... with conditions:’, requesting further ones. You enter here the inv test with the invocation number in question, resulting in a breakpoint with the [pred(foo/2),inv(86)] conditions. If you restart the original top-level goal in debug mode, the debugger immediately positions you at the invocation with the specified number.

Note that when the debugger executes a skip or a zip command, no procedure boxes are built. Consequently, the invocation and depth counters are not incremented. If skip and/or zip commands were used during the first execution, the suspicious invocation gets an invocation number higher than 86 in the second run. Therefore it is better to supply the inv(I),true(I>=86) condition to the ‘*’ debugger command, which will bring you to the first call of foo/2 at, or after invocation number 86 (which still might not be the suspicious invocation).

In the examples, the inv test was used both with a numeric and a variable argument (inv(86) and inv(I)). This is possible because the debugger unifies the given feature with the argument of the test. This holds for most tests, we will mention the exceptions.

Another similar example: if you suspect that a given predicate goes into an infinite recursion, and would like the execution to stop when entering this predicate somewhere inside the recursion, you can do the following:

     | ?- add_breakpoint([pred(foo/2),depth(_D),true(_D>=100)], _).
     % Conditional spypoint for user:foo/2 added, BID=1
     % zip,source_info
     | ?- debug, foo(200, X).
     % The debugger will first leap -- showing spypoints (debug)
      *    496    100 Call: foo(101,_12156) ?

The above breakpoint spec will cause the debugger to stop at the first invocation of foo/2 at depth 100 or greater. Note again that debug mode has to be entered for this to work (in zip mode no debugging information is kept, so the depth does not change).

We now continue with tests that restrict the breakpoint to an invocation at a specific place in the code.

Assume file /home/bob/myprog.pl contains the following Prolog program:

                      
% /home/bob/myprog.pl
p(X, U) :- % line 1 q(X, Y), % line 2 q(Y, Z), % line 3 ( \+ q(Z, _) % line 4 -> q(Z+1, U) % line 5 ; q(Z+2, U) % line 6 ). % ... q(X, Y) :- X < 10, !, Y is X+1. % line 10 q(X, Y) :- Y is X+2. % line 12

If you are interested only in the last invocation of q/2 within p/2, you can use the following breakpoint:

     | ?- add_breakpoint([pred(q/2),line('/home/bob/myprog.pl',6)], _).
     % Conditional spypoint for user:q/2 added, BID=1

Generally, the test line(File,Line) holds if the current invocation was in line number Line of a file whose absolute name is File. This test (as well as the line/1 and file/1 tests; see below) require the presence of source information: the file in question had to be consulted or compiled with the source_info Prolog flag switched on (i.e. set to on or emacs). If e.g. q/2 is called only from a single file, the file name need not be mentioned and a line/1 test suffices: line(6). On the other hand, if we are interested in all invocations of a predicate within a file, we can omit the line number and use the file(File) test.

For Prolog programs that are interpreted (consulted or asserted), further positioning information can be obtained, even in the absence of source information. The test parent_pred(Pred) unifies the module name expanded Pred with a predicate spec (of form Module:PredName/Arity) identifying the predicate in which the current invocation resides. The test parent_pred(Pred,N) will additionally unify N with the serial number of the clause containing the current goal.

For example, assuming the above myprog.pl file is consulted, the breakpoint below will cause the execution to stop when the call of is/2 in the second clause of q/2 is reached:

     | ?- add_breakpoint([pred(is/2),parent_pred(q/2,2)], _).
     % Conditional spypoint for prolog:is/2 added, BID=1
     * Predicate prolog:is/2 compiled inline, breakable only in interpreted code
     % zip,source_info
     | ?- p(20, X).
     in scope of a goal at line 12 in /home/bob/myprog.pl
      *      1      1 Call: _579 is 20+2 ?

Notice the warning issued by add_breakpoint/2: there are some built-in predicates (e.g. arithmetic, functor/3, arg/3, etc.), for which the compiler generates specific inline translation, rather than the generic predicate invocation code. Therefore compiled calls to such predicates are not visible to the debugger.

More exact positioning information can be obtained for interpreted programs by using the parent_clause(Cl,Sel,I) test. This unifies Cl with the clause containing the current invocation, while Sel and I both identify the current invocation within the body of this clause. Sel is unified with a subterm selector, while I with the serial number of the call. This test has the variants parent_clause/[1,2], in which only the Cl argument, or the Cl,Sel arguments are present.

As an example, two further alternatives of putting a breakpoint on the last call of q/2 within myprog.pl (line 6) are shown below, together with a listing showing the selectors and call serial numbers for the body of p/2:

     | ?- add_breakpoint([pred(q/2),parent_clause((p(_,_):-_),[2,2,2])],_).
     
     | ?- add_breakpoint([pred(q/2),parent_clause((p(_,_):-_),_,5)],_).
     
     p(X, U) :-                 % line  % call no.  % subterm selector
             q(X, Y),           %  2        1       [1]
             q(Y, Z),           %  3        2       [2,1]
             (   \+ q(Z, _)     %  4        3       [2,2,1,1,1]
             ->  q(Z+1, U)      %  5        4       [2,2,1,2]
             ;   q(Z+2, U)      %  6        5       [2,2,2]
             ).                 %  7

Here, the first argument of the parent_clause test ensures that the current invocation is in (the only clause of) p/2. If p/2 had more clauses, we would have to use an additional test, say parent_pred(user:p/2,1), and then the first argument of parent_clause could be an anonymous variable.

In the examples so far the breakpoint tests referred only to the goal in question. Therefore, the breakpoint was found applicable at all ports of the procedure box of the predicate. We can distinguish between ports using the port breakpoint test:

     | ?- add_breakpoint([pred(foo/2),port(call)], _).

With this breakpoint, the debugger will stop at the Call port of foo/2, but not at other ports. Note that the port(call) test can be simplified to calladd_breakpoint/2 will recognize this as a port name, and treat it as if it were enclosed in a port/1 functor.

Here are two equivalent formulations for a breakpoint that will cause the debugger to stop only at the Call and Exit ports of foo/2:

     | ?- add_breakpoint([pred(foo/2),(call;exit)], _).
     
     | ?- add_breakpoint([pred(foo/2),port(P),true((P=call;P=exit(_)))], _).

In both cases we have to use disjunction. In the first example we have a disjunctive breakpoint condition of the two simple tests port(call) and port(exit) (with the port functor omitted). In the second case the disjunction is inside the Prolog test within the true test.

Notice that the two examples refer to the Exit port differently. When you use port(P), where P is a variable, then, at an exit port, P will be unified with either exit(nondet) or exit(det), depending on the determinacy of the exited predicate. However, for convenience, the test port(exit) will also succeed at Exit ports. So in the first example above, exit can be replaced by exit(_), but the exit(_) in the second cannot be replaced by exit.

Finally, there is a subtle point to note with respect to activating the debugger at non Call ports. Let us look at the following breakpoint:

     | ?- add_breakpoint([pred(foo/2),fail], _).

The intention here is to have the debugger stop at only the Fail port of foo/2. This is very useful if foo/2 is not supposed to fail, but we suspect that it does. The above breakpoint will behave as expected when the debugger is leaping, but not while zipping. This is because for the debugger to be able to stop at a non Call port, a procedure box has to be built at the Call port of the given invocation. However, no debugging information is collected in zip mode by default, i.e. procedure boxes are not built. Later we will show how to achieve the required effect, even in zip mode.


Send feedback on this subject.