7.6.4 Specific and Generic Breakpoints

In all the examples so far a breakpoint was put on a specific predicate, described by a goal or pred test. Such breakpoints are called specific, as opposed to generic ones.

Generic breakpoints are the ones that do not specify a concrete predicate. This can happen when the breakpoint spec does not contain goal or pred tests at all, or their argument is not sufficiently instantiated. Here are some examples of generic breakpoints:

     | ?- add_breakpoint(line('/home/bob/myprog.pl',6), _).
     % Generic spypoint added, BID=1
     | ?- add_breakpoint(pred(foo/_), _).
     % Generic spypoint added, BID=2
     | ?- add_breakpoint([goal(G),true((arg(1,G,X),X==bar))], _).
     % Generic spypoint added, BID=3

The first breakpoint will stop at all calls in line 6 of the given file, the second at all calls of a predicate foo, irrespective of the number of arguments, while the third one will stop at any predicate with bar as its first argument. However, there is an additional implicit condition: the module name expansion inserts the type-in module as the default module name in the goal and pred conditions. Consequently, the second and third breakpoint applies only to predicates in the type-in module (user by default). If you would like the breakpoint to cover all modules you have to include an anonymous module prefix in the argument of the goal or pred test:

     | ?- add_breakpoint(pred(_:foo/_), _).
     % Generic spypoint added, BID=1
     % zip
     | ?- add_breakpoint([goal(_:G),true((arg(1,G,X),X==bar))], _).
     % Generic spypoint added, BID=2

Generic breakpoints are very powerful, but there is a price to pay: the zip mode is slowed down considerably.

As said earlier, in principle the debugger is entered at each port of each procedure invocation. As an optimization, the debugger can request the underlying Prolog engine to run at full speed and invoke the debugger only when one of the specified predicates is called. This optimization is used in zip mode, provided there are no generic breakpoints. In the presence of generic breakpoints, however, the debugger has to be entered at each call, to check their applicability. Consequently, with generic breakpoints, zip mode execution will not give much speed-up over debug mode, although its space requirements will still be much lower.

It is therefore advisable to give preference to specific breakpoints over generic ones, whenever possible. For example, if your program includes predicates foo/2 and foo/3, then it is much better to create two specific breakpoints, rather than a single generic one with conditions [pred(foo/_),...].

spy/2 is a built-in predicate that will create specific breakpoints only. Its first argument is a generalized predicate spec, much like in spy/1, and the second argument is a breakpoint spec. spy/2 will expand the first argument to one or more predicate specs, and for each of these will create a breakpoint, with a pred condition added to the test part of the supplied breakpoint spec. For example, in the presence of predicates foo/2 and foo/3

     | ?- spy(foo/_, file(...))

is equivalent to:

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

Note that with spy/[1,2] it is not possible to put a breakpoint on a (yet) undefined predicate. On the other hand, add_breakpoint/2 is perfectly capable of creating such breakpoints.