35
36:- module(prolog_trace,
37 [ trace/1, 38 trace/2, 39 tracing/2, 40 list_tracing/0,
41 notraceall/0
42 ]). 43:- autoload(library(apply),[maplist/2]). 44:- autoload(library(error),[instantiation_error/1]). 45:- autoload(library(prolog_wrap),[wrap_predicate/4]). 46:- autoload(library(prolog_code), [pi_head/2]). 47
48
57
58:- meta_predicate
59 trace(:),
60 trace(:, +),
61 tracing(:, -). 62
63:- dynamic tracing_mask/2 as volatile. 64:- dynamic trace_condition/3 as volatile. 65
123
124trace(Pred) :-
125 trace(Pred, +all).
126
127trace(Pred, Spec) :-
128 Pred = Ctx:_,
129 '$find_predicate'(Pred, Preds),
130 Preds \== [],
131 maplist(set_trace_pi(Spec, Pred, Ctx), Preds).
132
133set_trace_pi(Spec, PredSpec, Ctx, Pred) :-
134 pi_head(Pred, Head0),
135 resolve_predicate(Head0, Head),
136 bind_head(PredSpec, Head),
137 set_trace(Spec, Head, Ctx).
138
139bind_head(Head, Head) :- !.
140bind_head(_:Head, _:Head) :- !.
141bind_head(_, _).
142
143set_trace(Spec, Head, Ctx) :-
144 ( tracing_mask(Head, Spec0)
145 -> true
146 ; Spec0 = 0
147 ),
148 modify(Spec, Head, Spec0, Spec1, Ctx),
149 retractall(tracing_mask(Head, _)),
150 ( Spec1 == [] ; Spec1 == 0
151 -> true
152 ; asserta(tracing_mask(Head, Spec1))
153 ),
154 mask_ports(Spec1, Ports),
155 ( Spec1 == 0
156 -> unwrap_predicate(Head, trace),
157 print_message(informational, trace(Head, Ports))
158 ; wrapper(Spec1, Head, Wrapped, Wrapper),
159 wrap_predicate(Head, trace, Wrapped, Wrapper),
160 print_message(informational, trace(Head, Ports))
161 ).
162
163resolve_predicate(Head0, Head) :-
164 ( predicate_property(Head0, imported_from(M))
165 -> requalify(Head0, M, Head)
166 ; Head = Head0
167 ).
168
169requalify(Term, M, M:Plain) :-
170 strip_module(Term, _, Plain).
171
172modify(Var, _, _, _, _) :-
173 var(Var),
174 !,
175 instantiation_error(Var).
176modify([], _, Spec, Spec, _) :-
177 !.
178modify([H|T], Head, Spec0, Spec, Ctx) :-
179 !,
180 modify(H, Head, Spec0, Spec1, Ctx),
181 modify(T, Head, Spec1, Spec, Ctx).
182modify(+PortSpec, Head, Spec0, Spec, Ctx) :-
183 !,
184 port_spec(PortSpec, Head, Port, Ctx),
185 port_mask(Port, Mask),
186 Spec is Spec0 \/ Mask.
187modify(-Port, Head, Spec0, Spec, _) :-
188 !,
189 port_mask(Port, Mask),
190 remove_condition(Head, Mask),
191 Spec is Spec0 /\ \Mask.
192modify(Port, Head, Spec0, Spec, Ctx) :-
193 modify(+Port, Head, Spec0, Spec, Ctx).
194
195port_spec(Spec, _, Port, _), atom(Spec) =>
196 Port = Spec.
197port_spec(Spec, Head, Port, Ctx),
198 compound(Spec),
199 compound_name_arguments(Spec, Name, [Cond]) =>
200 Port = Name,
201 port_mask(Port, Mask),
202 strip_module(Ctx:Cond, M, PCond),
203 ( predicate_property(M:PCond, iso)
204 -> TheCond = PCond
205 ; TheCond = M:PCond
206 ),
207 asserta(trace_condition(Head, Mask, TheCond)).
208
209remove_condition(Head, Mask) :-
210 ( trace_condition(Head, TraceMask, TheCond),
211 Mask /\ TraceMask =:= TraceMask,
212 retractall(trace_condition(Head, TraceMask, TheCond)),
213 fail
214 ; true
215 ).
216
217port_mask(all, 0x0f).
218port_mask(call, 0x01).
219port_mask(exit, 0x02).
220port_mask(redo, 0x04).
221port_mask(fail, 0x08).
222
223mask_ports(0, []) :-
224 !.
225mask_ports(Pattern, [H|T]) :-
226 is_masked(Pattern, H, Pattern1),
227 mask_ports(Pattern1, T).
228
239
240wrapper(Ports, Head, Wrapped, Wrapper) :-
241 wrapper(Ports, Head,
242 #{frame:Frame, level:Level, start:Start},
243 Wrapped, Wrapped1),
244 Wrapper = ( prolog_current_frame(Frame),
245 prolog_frame_attribute(Frame, level, Level),
246 get_time(Start),
247 Wrapped1
248 ).
249
250wrapper(0, _, _, Wrapped, Wrapped) :-
251 !.
252wrapper(Pattern, Head, Id, Wrapped, Call) :-
253 is_masked(Pattern, call, Pattern1),
254 !,
255 wrapper(Pattern1, Head, Id, Wrapped, Call0),
256 Call = ( prolog_trace:on_port(call, Head, Id),
257 Call0
258 ).
259wrapper(Pattern, Head, Id, Wrapped, Call) :-
260 is_masked(Pattern, exit, Pattern1),
261 !,
262 wrapper(Pattern1, Head, Id, Wrapped, Call0),
263 Call = ( Call0,
264 prolog_trace:on_port(exit, Head, Id)
265 ).
266wrapper(Pattern, Head, Id, Wrapped, Call) :-
267 is_masked(Pattern, redo, Pattern1),
268 !,
269 wrapper(Pattern1, Head, Id, Wrapped, Call0),
270 Call = ( call_cleanup(Call0, Det = true),
271 ( Det == true
272 -> true
273 ; true
274 ; prolog_trace:on_port(redo, Head, Id),
275 fail
276 )
277 ).
278wrapper(Pattern, Head, Id, Wrapped, Call) :-
279 is_masked(Pattern, fail, Pattern1),
280 !,
281 wrapper(Pattern1, Head, Id, Wrapped, Call0),
282 Call = call(( call_cleanup(Call0, Det = true),
283 ( Det == true
284 -> !
285 ; true
286 )
287 ; prolog_trace:on_port(fail, Head, Id),
288 fail
289 )).
290
291is_masked(Pattern0, Port, Pattern) :-
292 port_mask(Port, Mask),
293 Pattern0 /\ Mask =:= Mask,
294 !,
295 Pattern is Pattern0 /\ \Mask.
296
301
302:- public on_port/3. 303on_port(Port, Head, Id) :-
304 ( do_trace(Port, Head)
305 -> print_message(debug, frame(Head, trace(Port, Id)))
306 ; true
307 ),
308 success_port(Port).
309
310do_trace(Port, Head) :-
311 forall(active_trace_condition(Port, Head, Cond),
312 Cond).
313
314active_trace_condition(Port, Head, Cond) :-
315 trace_condition(Head, Mask, Cond),
316 port_mask(Port, PortMask),
317 Mask /\ PortMask =\= 0.
318
319success_port(call). 320success_port(exit).
321
326
327tracing(Spec, Ports) :-
328 tracing_mask(Spec, Mask),
329 mask_ports(Mask, Ports0),
330 maplist(add_condition(Spec), Ports0, Ports).
331
332add_condition(Head, Port, PortCond) :-
333 trace_condition(Head, Mask, Cond),
334 port_mask(Port, PortMask),
335 Mask /\ PortMask =\= 0,
336 !,
337 PortCond =.. [Port,Cond].
338add_condition(_, Port, Port).
339
340
344
345list_tracing :-
346 Head = _:_,
347 findall(trace(Head, Ports), tracing(Head, Ports), Tracing),
348 print_message(informational, tracing(Tracing)).
349
350:- multifile
351 prolog_debug_tools:debugging_hook/0. 352
353prolog_debug_tools:debugging_hook :-
354 ( tracing(_:_, _)
355 -> list_tracing
356 ).
357
358
362
363notraceall :-
364 forall(tracing(M:Spec, _Ports),
365 trace(M:Spec, -all))