Node:Hooks Related to Breakpoints, Next:, Previous:Storing User Information in the Backtrace, Up:Advanced Debugging



Hooks Related to Breakpoints

There are two hooks related to breakpoints.

The hook breakpoint_expansion(Macro,Body) makes it possible for the user to extend the set of allowed conditions. If this hook is defined, it is called with each simple test or action in the Macro argument. If the hook succeeds, then the term returned in the Body argument is substituted for the original test or action. Note that Body can not span both the test and the action part, i.e. it cannot contain the - /2 operator. The whole Body will be interpreted either as a test or as an action, depending on the context of the original condition.

We now give a few examples for breakpoint macros. These culminate in the last example which actually implements the condition making a predicate invisible, a reformulation of the spypoint example of the previous subsection.

     :- multifile user:breakpoint_expansion/2.
     user:breakpoint_expansion(
                 skip, [inv(I),skip(I)]).
     
     user:breakpoint_expansion(
                 gpriv(Value),
                 [goal_private(GP),true(memberchk(Value,GP))]).
     
     user:breakpoint_expansion(
                 get_mode(M), true(execution_state(mode(M)))).
     
     user:breakpoint_expansion(
                 invisible,
                 [silent,proceed,
                     (   call -> get_mode(M), gpriv(mymode(M)), skip
                     ;   exit -> gpriv(mymode(MM)), mode(MM)
                     ;   true
                     )]).
     
     | ?- spy(foo/2, -invisible).
     

We first define the skip macro, instructing the debugger to skip the current invocation. This macro is only meaningful in the action part.

The second clause defines the gpriv/2 macro, a generalization of the earlier mode_memory/1 predicate. For example, gpriv(mymode(M)) expands to goal_private(GP),true(memberchk(mymode(M),GP)). This embodies the convention of using open-ended lists for the goal private field.

The third clause defines the get_mode/1 macro for accessing the current mode from within the action part. It uses the trick of calling execution_state/1, which always interprets its argument in the test part context. This is needed because of the restriction that a macro cannot span both the test and the action part.

Finally, the last clause implements the action macro invisible/0, which makes the predicate in question completely hidden. The last line shows how this macro can be used to make foo/2 invisible.

Although macros are very convenient, they are executed less efficiently than plain Prolog code. Therefore, if efficiency is of concern, the following variant of invisible should be used.

     user:breakpoint_expansion(invisible,
             [true(invisible(NewMode)),mode(NewMode),proceed,silent]).
     
     invisible(NewMode) :-
             execution_state([mode(M),port(P),inv(Inv),goal_private(GP)]),
             memberchk(mymode(MM), GP),
             (   P == call -> MM = M, NewMode = skip(Inv)
             ;   P = exit(_) -> NewMode = MM
             ;   NewMode = M
             ).
     

The second hook related to breakpoints is debugger_command_hook(DCommand, Actions). This hook serves for customizing the behavior of the interactive debugger, i.e. for introducing new interactive debugger commands. The hook is called for each debugger command read in by the debugger. DCommand contains the abstract format of the debugger command read in, as returned by the query facility (see Query Processing). If the hook succeeds, it should return in Actions an action part to be evaluated as the result of the command.

If you want to redefine an existing debugger command, you should study library('SU_messages') to learn the abstract format of this command, as returned by the query facility. If you want to add a new command, it suffices to know that unrecognized debugger commands are returned as unknown(Line,Warning). Here, Line is the list of character codes typed in, with any leading layout removed, and Warning is a warning message.

The following example defines the S interactive debugger command to behave as skip at Call and Redo ports, and as creep otherwise:

     :- multifile user:debugger_command_hook/2.
     user:debugger_command_hook(unknown([0'S|_],_), Actions) :-
             execution_state([port(P),inv(I)]),
             Actions = [Mode,proceed,silent],
             (   P = call -> Mode = skip(I)
             ;   P = redo -> Mode = skip(I)
             ;   Mode = trace
             ).
     

Note that the silent action is needed above; otherwise, the trace message will be printed a second time, before continuing the execution.

library(debugger_examples) contains some of the above hooks, as well as several others.