Debugging: Tracing the Execution of a Specific Goal

Hello,

How do I trace the evaluation of a predicate only when a specified set of arguments is passed to this predicate?

So far I have been using spy(my_predicate) to creep through the evaluation of my_predicate during program execution. However, this has recently become tedious as there are numerous calls to my_predicate, but I am interested in tracing only those where my_predicate is passed the argument MyArgList.

In Sicstus, it is possible to use something like add_breakpoint(goal(my_predicate(MyArgList), _) to only trace execution when the pair (my_predicate, MyArgList) is invoked.

How can I achieve a similar result in SWI Prolog?

Thanks.

Not sure if the SWI-Prolog graphical debugger supports such functionality…?

In case your Prolog code can be wrapped by a Logtalk object (or in case it’s a module that Logtalk is able to compile as an object), you may be able to use the Logtalk debugger context spy points to only spy a specific predicate call template. E.g.

?- debugger::spy(_, _, _, foo(_, bar, _)).

That isn’t really provided. There are two alternatives. If you can point at a specific place in the code you can use the built-in editor facilities to place a break point inside some clause. So, if there is a predicate like this:

p(X) :-
    var(X),
    do_something(X).
9(X) :-
   ...

You can put the cursor on do_something and use the menu Prolog/break at

If that doesn’t help, you simple add this to your code at the place where you want a conditional break and run make/0.

    (some_condition -> gtrace ; true),

That may seem crude, but it works quite ok, especially if you use version control so you can easily check which debug statements you have added and/or reset the source.

With the recently added wrap_predicate/4 we could add conditional spy points easily. Not sure what it is worth though.

1 Like

Thank you @pmoura :slight_smile:
I will look into using this option once I have exhausted (in vain) all SWI-specific debug features to solve my issue.

Thank you @jan,

I have used the conditional tracing as you kindly suggested:
(condition_on_values_of_MyArg -> trace; true)

So now I am able to get directly to the desired predicate with the desired arguments.

Now, can I “leap” to the next evaluation of my_predicate with arguments MyArg? Adding a spy point for my_predicate allows me to leap to the next evaluation of the predicate but with different arguments though.

Is there another trick up your sleeve?

I try to collect them in Wiki: Bug hunting toolbox

2 Likes

Just use n(odebug) (c(ontinue) in the non-GUI tracer) or l(eap)? The next time it traps gtrace it will restart tracing. Note that leap implies debug mode. The debug mode pushes a dummy choice point for each otherwise deterministic predicate such that you can retry a goal. Unfortunately the result can be much slower and run out of memory. In nodebug mode this is no problem, but after the tracer is trapped a lot of parent frames may be missing due to last call optimization and many variables may be reset to <garbage_collected>.

2 Likes

Thanks everybody.

I often find myself adding a test inside a predicate, e.g.

my_pred(A,B,C) :-
    ( some_test(A) -> trace ; true ),  % added for debugging
    % the rest of the predicate ... 

and it would be nice if I could instead set the “trace point” without changing the source code (this is especially true if there are multiple clauses for the predicate).

But I don’t have a nice syntax to suggest; some_test is sometimes a structural test such asA=[_|_], sometimes a predicate such as ground(A), sometimes more complicated,

There’s also prolog_trace_interception which probably could be packaged in a friendlier way for specific action at a goal. My guess is that something like this should work (untested):

prolog_trace_interception(call, Frame, _Choice, Action) :-
    prolog_frame_attribute(Frame, goal, Module:Goal),
    (  Goal = my_pred(A,_B, _C),
       some_test(A)
    -> fail  % default behaviour from tracer
    ;  Action = skip
    ).

To some extend that can work. One of the problems is that it will cause a huge slowdown by forcing debug mode and going through this hook at practically all steps. The skip is probably wrong as this would prevent a conditional debug in a deeper nested call.

There is wrap_predicate/4 which makes wakeup much more selective. Still, you can only act on one of the ports. Then there are break points. These are implemented by replacing a call VMI instruction with a break instruction and thus come at zero cost unless triggered. If the break point is inactive it will simply continue the call. Otherwise it calls an (undocumented :frowning: ) hook prolog:break_hook that is passed the code that will be called and can act accordingly. This was added by Matt Lilley from SecuritEase. That could act as a building block as clause_info/5 can provide the variable names. So, in theory you can set a breakpoint (through PceEmacs or set_breakpoint/4) and associate a condition. That could be used to implement e.g.

?- set_breakpoint(myfile, 42, Id).
Id = 1.
?- break_condition(1, MyVar > 100).
true.

Or, in the editor point at a place to break and enter the condition through some UI binding. Might be nice to have, but I wonder whether it is really worth the trouble. Unlike most other languages you can modify the source while staying in the same debug session and thus the only disadvantage is polluting the source code. With an SCM this is quite easy to manage.