1 % (c) 2009-2015 Lehrstuhl fuer Softwaretechnik und Programmiersprachen,
2 % Heinrich Heine Universitaet Duesseldorf
3 % This software is licenced under EPL 1.0 (http://www.eclipse.org/org/documents/epl-v10.html)
4
5 /*
6 * This module allows to register predicates as "debugging predicates".
7 * When such a predicate is marked by the prefix operator ~~,
8 * during term expansion, the term is replaced by:
9 * - if remove_debug_calls is true:
10 * one of the term's arguments, namely the one that is marked
11 * by a star (*) during registration
12 * - if remove_debug_calls is false:
13 * only the prefix operator ~~ is removed, leaving the rest
14 * of the term unchanged.
15 *
16 * Example:
17 *
18 * :- register_debugging_call log(-,*).
19 * log(Info,X) :-
20 * print(Info),print(' enter: '),print(X),nl,
21 * call(X),
22 * print(Info),print(' leave: '),print(X),nl.
23 *
24 * :- enable_debugging_calls.
25 * p1 :- ~~log(p1, member(X,[a])).
26 * :- disable_debugging_calls.
27 * p2 :- ~~log(p2, member(X,[a])).
28 *
29 * a call of p1 prints:
30 * p1 enter: member(_123,[a])
31 * p1 leave: member(a,[a])
32 * while a call to p2 prints nothing
33 *
34 */
35
36 :- module(debugging_calls,[register_debugging_call/1,
37 enable_debugging_calls/0,
38 disable_debugging_calls/0,
39 remove_debugging_calls/4 % called by term expander
40 ]).
41
42 :- use_module(library(lists)).
43 :- use_module(library(codesio)).
44
45 :- use_module(module_information,[module_info/2]).
46
47 :- module_info(group,infrastructure).
48 :- module_info(description,'This module provides functionality to specify meta-calls that (like a must-not-fail call) that are short-circuited when compiling production code.').
49
50 :- op(1150, fx, register_debugging_call).
51 :- op(300, fy, ~~).
52
53 :- dynamic remove_debug_calls/0.
54 % remove_debug_calls.
55
56 :- dynamic is_debug_call/3.
57
58 safe_atom_codes(A,C) :-
59 on_exception(error(representation_error(max_atom_length),_),atom_codes(A,C),
60 (print(exception(max_atom_length)),nl,A='???')).
61
62 %avl_term(X) :- var(X),!,fail.
63 %avl_term(avl_set(_)).
64 %avl_term((A,B)):- (avl_term(A) -> true ; avl_term(B)).
65 %avl_term([H|_]) :- avl_term(H).
66
67 remove_debugging_calls(Layout,Term,Layout,Term) :- var(Term),!.
68 %remove_debugging_calls(Layout,nl,Layout,format(' line:~w~n',[Layout])) :- !.
69 % comment in to see nl (newline term_expansion) info with :- prolog_flag(source_info,_,on).
70 remove_debugging_calls(LayoutIn,when(Cond,Body),LayoutOut,ResTerm) :- !,
71 ResTerm = when(Cond,NewBody), % do not expand inside condition
72 (LayoutIn = []
73 -> LayoutBody = [],
74 LayoutOut = []
75 ; LayoutIn = [LayoutWhen,LayoutCond,LayoutBody],
76 LayoutOut = [LayoutWhen,LayoutCond,LayoutBodyOut]),
77 remove_debugging_calls(LayoutBody,Body,LayoutBodyOut,NewBody).
78 %remove_debugging_calls(Layout,ground(Term),Layout,Term2) :- !, Term2 = (nonvar(Term),(debugging_calls:avl_term(Term) -> print(ground_check(Term)),nl, trace ; true),ground(Term)). %% comment in to observe ground checks
79 remove_debugging_calls(LayoutIn,Term,Layout2,Term2) :-
80 functor(Term, ~~, 1),
81 arg(1,Term,Term1),
82 nonvar(Term1),!,functor(Term1,Functor,Arity),
83 ( LayoutIn = [] -> Layout1 = []
84 ; LayoutIn = [_,Layout1]),
85 ( is_debug_call(Functor,Arity,CallPos) ->
86 ( remove_debug_calls ->
87 safe_arg(CallPos,Term1,TermI),
88 ( Layout1 = [] -> LayoutI = []
89 ; Layout1 = [_|ArgLayout] ->
90 nth1(CallPos,ArgLayout,LayoutI)),
91 remove_debugging_calls(LayoutI,TermI,Layout2,Term2)
92 ; otherwise ->
93 remove_debugging_calls(Layout1,Term1,Layout2,Term2))
94 ; otherwise ->
95 write_to_codes(Functor/Arity,M1),
96 append(["encountered unregistered ",M1,
97 " tagged for debugging removal"],M3),
98 safe_atom_codes(Msg1,M3),
99 print_message(error,Msg1),
100 Term2 = Term1
101 ).
102 %remove_debugging_calls(LayoutIn,on_exception(E,Call,Code),LayoutOut,Term2) :- !,
103 % LayoutOut = LayoutIn,
104 % print(on_exception(E,Call,Code)),nl,
105 % Term2 = on_exception(E,Call,(format('~n**EXCEPTION = ~w~n~n',[E]),Code)).
106 %remove_debugging_calls(LayoutIn,time_out(Call,TO,Res),LayoutOut,Term2) :- !,
107 % LayoutOut = LayoutIn,
108 % print(time_out(Call,TO,Res)),nl,
109 % Term2 = ((Call = _:C -> true ; Call=C),
110 % functor(C,F,N),format('**TIMEOUT CALL ~w/~w = ~w ms~n',[F,N,TO]),
111 % time_out(Call,TO,Res),
112 % (Res=time_out -> format('**TIMEOUT OCCURED ~w/~w after ~w ms~n',[F,N,TO]) ; true)).
113 remove_debugging_calls(LayoutIn,Term1,LayoutOut,Term2) :-
114 !,functor(Term1,Functor,Arity),
115 functor(Term2,Functor,Arity),
116 ( LayoutIn = [] ->
117 LayoutOut = [],
118 remove_debugging_calls_args_nl(1,Arity,Term1,Term2)
119 ; LayoutIn = [Pos|Layout1] ->
120 LayoutOut = [Pos|Layout2],
121 remove_debugging_calls_args(Layout1,Term1,Layout2,Term2,1)
122 ; otherwise ->
123 LayoutIn = LayoutOut,
124 Term1 = Term2).
125
126 remove_debugging_calls_args([],_,[],_,_).
127 remove_debugging_calls_args([L1|Layout1],Term1,[L2|Layout2],Term2,N) :-
128 safe_arg(N,Term1,In), safe_arg(N,Term2,Out),
129 remove_debugging_calls(L1,In,L2,Out),
130 N2 is N+1,
131 remove_debugging_calls_args(Layout1,Term1,Layout2,Term2,N2).
132
133 safe_arg(N,Term,Arg) :- compound(Term), arg(N,Term,Arg).
134
135 remove_debugging_calls_args_nl(N,Max,_,_) :- N > Max,!.
136 remove_debugging_calls_args_nl(N,Max,Term1,Term2) :-
137 N =< Max,
138 safe_arg(N,Term1,In), safe_arg(N,Term2,Out),
139 remove_debugging_calls([],In,_,Out),
140 N2 is N+1,
141 remove_debugging_calls_args_nl(N2,Max,Term1,Term2).
142
143 register_debugging_call((Call1,Call2)) :-
144 !,register_debugging_call(Call1),
145 register_debugging_call(Call2).
146 register_debugging_call(Call) :-
147 functor(Call,Functor,Arity),
148 Call =.. [_|Args],
149 ? ( nth1(Pos,Args,*) ->
150 ? ( nth1(NPos,Args,*), NPos \= Pos ->
151 register_error_msg(Call)
152 ; otherwise ->
153 retractall(is_debug_call(Functor,Arity,_)),
154 assert(is_debug_call(Functor,Arity,Pos)))
155 ; otherwise ->
156 register_error_msg(Call)).
157 register_error_msg(Call) :-
158 write_to_codes(Call,M),
159 append("register_debug_call has wrong argument: ", M, Codes),
160 safe_atom_codes(Msg,Codes),
161 print_message(error,Msg).
162
163
164 enable_debugging_calls :-
165 retractall(remove_debug_calls).
166 disable_debugging_calls :-
167 (remove_debug_calls -> true; assert(remove_debug_calls)).
168
169 /*
170 * This code uses the term_expansion/6 hook, wich
171 * is a multifile-predicate. Due to a bug in SICStus Prolog,
172 * it is not possible to mix uncompiled and compiled code
173 * for such a predicate.
174 * So we always compile the term_expansion hook
175 */
176 :- prolog_flag(compiling,Pre,compactcode),
177 compile(debugging_calls_te),
178 prolog_flag(compiling,_,Pre).