1 | % (c) 2009-2019 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 | :- module(debug, | |
6 | [(~~)/1, | |
7 | global_debug_flag/0, | |
8 | debug_print/1, debug_nl/0, | |
9 | debug_print/2, debug_nl/1, /* extra argument with urgency of message 0: not urgent */ | |
10 | debug_println/2, debug_println/1, | |
11 | debug_format/3, | |
12 | debug_format_flush/3, % same with flushing output | |
13 | println/2, println/1, % allow easy commenting in of debug_println statements | |
14 | log/1, | |
15 | debug_stats/1, print_debug_stats/0, | |
16 | ||
17 | tcltk_turn_debugging_on/0, tcltk_turn_debugging_on/1, | |
18 | tcltk_turn_debugging_off/0, | |
19 | debug_mode/1, | |
20 | debug_level/1, | |
21 | debug_level_active_for/1, | |
22 | ||
23 | silent_mode/1, set_silent_mode/1, | |
24 | printsilent/1, nls/0, println_silent/1, | |
25 | formatsilent/2, formatsilent/3, printsilent_message/1, | |
26 | ||
27 | print_quoted/1, print_quoted_with_max_depth/2, | |
28 | time/1, % call with timing information, time_call | |
29 | time_if_debug/1, | |
30 | watch/1, watch/2, watch_det/2, det_check/1, det_check/2, | |
31 | nl_time/0, debug_nl_time/1, | |
32 | hit_counter/1, | |
33 | ||
34 | new_pp/2, new_sol/2, reset_pp/1, | |
35 | if_det_check/3, | |
36 | ||
37 | trace_in_debug_mode/0, | |
38 | ||
39 | (timer_call)/1, (timer_call)/2, timer_statistics/0, | |
40 | ||
41 | ||
42 | bisect/2 | |
43 | ]). | |
44 | ||
45 | :- use_module(module_information,[module_info/2]). | |
46 | :- module_info(group,infrastructure). | |
47 | :- module_info(description,'This module provides predicates to output debugging information when needed.'). | |
48 | ||
49 | :- meta_predicate ~~(0). | |
50 | ~~(G) :- call(G). | |
51 | ||
52 | :- load_files(library(system), [when(compile_time), imports([environ/2])]). | |
53 | :- if(environ(prob_debug_flag,true)). | |
54 | global_debug_flag. | |
55 | :- else. | |
56 | global_debug_flag :- /* can be used to control debugging using if pre-processor primitives */ | |
57 | %% true. %% | |
58 | fail. %% | |
59 | :- endif. | |
60 | ||
61 | use_timer :- | |
62 | %% fail true. %% | |
63 | fail. %% | |
64 | ||
65 | :- if(debug:global_debug_flag). | |
66 | % A useful definition that ensures that all standard error exceptions causes the debugger to enter trace mode, is as follows: | |
67 | :- multifile user:error_exception/1. | |
68 | user:error_exception(error(_,_)). % then use ?- debug,go. e.g. for Tcl/Tk Version | |
69 | :- endif. | |
70 | ||
71 | :- if(debug:use_timer). | |
72 | :- use_module(extension('timer/timer'),[timer_call/1,timer_call/2,timer_init/0,timer_statistics/0]). | |
73 | :- timer_init. | |
74 | :- print('Microsecond timer initialised. Use debug:timer_statistics. to obtain statistics.'),nl. | |
75 | :- else. | |
76 | :- op(300,fx,timer_call). | |
77 | :- meta_predicate timer_call(0). | |
78 | :- meta_predicate timer_call(-,0). | |
79 | timer_call(X) :- call(X). | |
80 | timer_call(_PP,Call) :- call(Call). | |
81 | timer_statistics :- print('*** timer_statistics NOT available; set enable_timer in debug.pl to true'),nl. | |
82 | :- endif. | |
83 | ||
84 | ||
85 | debug_println(X) :- debug_print(X), debug_nl. | |
86 | debug_println(P,X) :- debug_print(P,X), debug_nl(P). | |
87 | println(_,X) :- print_with_max_depth(X,10),nl. | |
88 | println(X) :- print_with_max_depth(X,10),nl. | |
89 | ||
90 | debug_print(X) :- debug_print(6,X). | |
91 | debug_nl :- debug_nl(6). | |
92 | ||
93 | :- dynamic debug_print/2, debug_format/3, debug_nl/1. | |
94 | ||
95 | debug_print(_,_X). | |
96 | debug_format(_,_,_). | |
97 | debug_nl(_). | |
98 | ||
99 | :- dynamic debug_mode/1. | |
100 | debug_mode(off). | |
101 | ||
102 | % additional flag that can be checked : any non-essential prints should not be printed in silent mode | |
103 | :- dynamic silent_mode/1. | |
104 | silent_mode(off). | |
105 | ||
106 | set_silent_mode(X) :- retractall(silent_mode(_)),assert(silent_mode(X)). | |
107 | ||
108 | ||
109 | printsilent(S) :- silent_mode(off) -> print(S) ; true. % print_silent | |
110 | println_silent(S) :- silent_mode(off) -> print(S),nl ; true. | |
111 | printsilent_message(S) :- silent_mode(off) -> format(user_output,'~w~n',[S]) ; true. | |
112 | nls :- silent_mode(off) -> nl ; true. | |
113 | formatsilent(FS,Args) :- silent_mode(off) -> format(FS,Args) ; true. | |
114 | formatsilent(Stream,FS,Args) :- silent_mode(off) -> format(Stream,FS,Args) ; true. | |
115 | ||
116 | debug_format_flush(Level,Msg,Args) :- | |
117 | (debug_mode(on) -> debug_format(Level,Msg,Args), flush_output(user_output) | |
118 | ; true). | |
119 | ||
120 | :- dynamic debug_level/1. | |
121 | ||
122 | debug_level(5). /* only messages with priority of 5 or higher are printed */ | |
123 | ||
124 | % does the debug_level print messages of level X. | |
125 | debug_level_active_for(X) :- debug_level(L), X >= L. | |
126 | ||
127 | tcltk_turn_debugging_on :- debug_level(X),tcltk_turn_debugging_on(X). | |
128 | ||
129 | tcltk_turn_debugging_on(Level) :- atomic(Level), number(Level), | |
130 | retract(debug_level(_)), | |
131 | assert(debug_level(Level)), | |
132 | retract(debug_mode(_)), !, assert(debug_mode(on)), | |
133 | retractall(debug_nl(_)), | |
134 | assert((debug_nl(U) :- (U < Level -> true ; nl))), | |
135 | retractall(debug_print(_,_)), | |
136 | assert((debug_print(U,X) :- (U < Level -> true ; print_with_max_depth(X,10)))), | |
137 | retractall(debug_format(_,_,_)), | |
138 | assert((debug_format(U,A,V) :- (U < Level -> true ; format(A,V)))), | |
139 | print('Debugging mode: On :'), print(Level),nl. | |
140 | tcltk_turn_debugging_on(_). | |
141 | ||
142 | tcltk_turn_debugging_off :- | |
143 | retract(debug_mode(on)), !, assert(debug_mode(off)), | |
144 | retractall(debug_nl(_)),assert(debug_nl(_)), | |
145 | retractall(debug_print(_,_)), assert(debug_print(_,_)), | |
146 | retractall(debug_format(_,_,_)), assert(debug_format(_,_,_)), | |
147 | print('Debugging mode: Off'),nl. | |
148 | tcltk_turn_debugging_off. | |
149 | ||
150 | trace_in_debug_mode :- (debug_mode(off) -> true ; trace). | |
151 | ||
152 | print_with_max_depth(X,Max) :- write_term(X, | |
153 | [max_depth(Max),portrayed(true)]). | |
154 | print_quoted_with_max_depth(X,Max) :- write_term(X, | |
155 | [quoted(true),max_depth(Max),portrayed(true)]). | |
156 | print_quoted(X) :- write_term(X, | |
157 | [quoted(true),max_depth(0), | |
158 | numbervars(true),portrayed(true)]). | |
159 | ||
160 | ||
161 | debug_stats(Info) :- debug_mode(on),!,print(Info),print_debug_stats. | |
162 | debug_stats(_). | |
163 | ||
164 | print_debug_stats :- | |
165 | tools:print_memory_used_wo_gc, | |
166 | statistics(runtime,[RT,_]), print(' run/wall = '),print(RT), | |
167 | statistics(walltime,[WT,_]), print('/'),print(WT),print(' ms'),nl. | |
168 | ||
169 | :- meta_predicate time_if_debug(0). | |
170 | :- meta_predicate time(0). | |
171 | time_if_debug(Call) :- debug_mode(off),!,Call. | |
172 | time_if_debug(Call) :- time(Call). | |
173 | ||
174 | :- use_module(tools_printing,[print_term_summary/1]). | |
175 | time(Call) :- | |
176 | nl,print('calling: '),print_term_summary(Call), | |
177 | statistics(runtime,[Start,_]), | |
178 | statistics(total_runtime,[StartT,_]), | |
179 | statistics(walltime,[StartW,_]), | |
180 | call(Call), | |
181 | statistics(runtime,[End,_]), | |
182 | statistics(total_runtime,[EndT,_]), | |
183 | statistics(walltime,[EndW,_]), | |
184 | print('exit: '), print_term_summary(Call), | |
185 | Tot is End-Start, | |
186 | print('Runtime: '), print(Tot), print(' ms'), | |
187 | TotT is EndT-StartT, | |
188 | print(' Total Runtime: '), print(TotT), print(' ms'), | |
189 | TotW is EndW-StartW, | |
190 | print(' Walltime: '), print(TotW), print(' ms'),nl. | |
191 | ||
192 | :- volatile sol_found/2. | |
193 | :- dynamic det_id/1, sol_found/2. | |
194 | det_id(0). | |
195 | gen_det_id(X) :- retract(det_id(X)), X1 is X+1, | |
196 | assert(det_id(X1)). | |
197 | ||
198 | :- meta_predicate watch_det(*,0). | |
199 | :- meta_predicate watch(0). | |
200 | :- meta_predicate watch(*,0). | |
201 | watch_det(Limit,Call) :- | |
202 | gen_det_id(ID), | |
203 | watch(Limit,Call), | |
204 | (retract(sol_found(ID,Nr)) | |
205 | -> nl,print('*** '), print_term_summary(Call), | |
206 | print('*** NON-DETERMINATE SOLUTION #'), | |
207 | N1 is Nr+1, print(N1),nl,nl, | |
208 | assert(sol_found(ID,N1)) %,trace | |
209 | ; assert(sol_found(ID,1)) | |
210 | ). | |
211 | ||
212 | :- meta_predicate det_check(0). | |
213 | :- meta_predicate det_check(0,0). | |
214 | det_check(Call) :- det_check(Call,true). | |
215 | ||
216 | det_check(Call,ErrCode) :- | |
217 | gen_det_id(ID), | |
218 | call(Call), %tools:print_bt_message(det_check_sol(ID,Call)), | |
219 | (retract(sol_found(ID,Nr)) | |
220 | -> nl,print('*** '), print_term_summary(Call), | |
221 | print('*** NON-DETERMINATE SOLUTION #'), | |
222 | N1 is Nr+1, print(N1),nl, | |
223 | assert(sol_found(ID,N1)), | |
224 | call(ErrCode), % trace, | |
225 | nl | |
226 | ; assert(sol_found(ID,1)) | |
227 | ). | |
228 | ||
229 | watch(Call) :- watch(30,Call). | |
230 | ||
231 | watch(Limit,Call) :- | |
232 | statistics(runtime,[Start,_]), | |
233 | call(Call), | |
234 | statistics(runtime,[End,_]), | |
235 | Tot is End-Start, | |
236 | (Tot>Limit -> nl, %nl,print(Call),nl, | |
237 | print('*** '),print_term_summary(Call), | |
238 | print('*** exceeded limit: '), print(Tot), print(' ms'),nl | |
239 | ; true). | |
240 | ||
241 | % print new line with time info | |
242 | nl_time :- statistics(runtime,[Start,SinceLast]), statistics(walltime,[WStart,WSinceLast]), | |
243 | format(' [~w ms total, ~w ms delta (wall: ~w ms, delta ~w ms)]~n',[Start,SinceLast,WStart,WSinceLast]). | |
244 | ||
245 | debug_nl_time(Msg) :- (debug_mode(on) -> print(Msg), nl_time ; true). | |
246 | ||
247 | % ------------------------ | |
248 | % utility similar to det_check; but allows to set spy point for tracing and checks redo | |
249 | ||
250 | :- volatile pp_nr/1, spy_pp/1, pp_goal/2. | |
251 | :- dynamic pp_nr/1, spy_pp/1, pp_goal/2. | |
252 | % debug:reset_pp(XX). | |
253 | pp_nr(0). pp_goal(0,true). | |
254 | %spy_pp(115). | |
255 | reset_pp(Spy) :- retract(pp_nr(_)), assert(pp_nr(0)), | |
256 | retractall(pp_goal(_,_)), assert(spy_pp(Spy)). | |
257 | :- volatile sol_found/1. | |
258 | :- dynamic sol_found/1. | |
259 | new_pp(C,Nr) :- retract(pp_nr(X)), Nr is X+1, assert(pp_nr(Nr)), | |
260 | assert(pp_goal(Nr,C)), | |
261 | (spy_pp(Nr) -> trace ; true). | |
262 | new_sol(S,Nr) :- new_sol_no_redo(S,Nr). | |
263 | new_sol(S,Nr) :- %spy_pp(Nr), | |
264 | print('### REDO PP: '), print(Nr),nl, | |
265 | print('### SOL: '), print(S),nl,spy_trace(Nr), | |
266 | fail. | |
267 | ||
268 | new_sol_no_redo(S,Nr) :- | |
269 | (sol_found(Nr) -> nl,print('### Non-Deterministic Program Point !'),nl, | |
270 | print('### '), print(Nr),nl, | |
271 | print('### GOAL: '), pp_goal(Nr,G), print(G),nl, | |
272 | print('### SOL: '), print(S),nl, | |
273 | nl,trace | |
274 | ; assert(sol_found(Nr)), % print(sol_found(Nr,S)),nl,nl, | |
275 | spy_trace(Nr) | |
276 | ). | |
277 | ||
278 | spy_trace(Nr) :- (spy_pp(Nr) -> trace ; true). | |
279 | ||
280 | % an if predicate which checks if the Test part is deterministic | |
281 | :- meta_predicate if_det_check(0,0,0). | |
282 | :- meta_predicate if_det_check_pp(0,0,0,*). | |
283 | if_det_check(Test,Then,Else) :- new_pp(if_det_check_test(Test),PP), | |
284 | if_det_check_pp(Test,Then,Else,PP). | |
285 | if_det_check_pp(Test,Then,_Else,PP) :- | |
286 | Test, | |
287 | new_sol_no_redo(Test,PP), | |
288 | Then. | |
289 | if_det_check_pp(_Test,_Then,Else,PP) :- \+ sol_found(PP), Else. | |
290 | ||
291 | % ------------------------------------------- | |
292 | :- dynamic hit_counter_fact/1. | |
293 | hit_counter_fact(1). | |
294 | ||
295 | % call to get and increase hit_counter; can be used to set trace spy points | |
296 | hit_counter(X) :- retract(hit_counter_fact(X)), X1 is X+1, assert(hit_counter_fact(X1)). | |
297 | ||
298 | % ------------------------------------------- | |
299 | ||
300 | ||
301 | :- dynamic tdepth/1. | |
302 | tdepth(0). | |
303 | :- public trace_point/1. | |
304 | trace_point(E) :- retract(tdepth(T)), T1 is T+1, assert(tdepth(T1)), | |
305 | print_tab(T), print('> ENTER '),print(T), print(' : '),print_term_summary(E),nl. | |
306 | trace_point(E) :- retract(tdepth(T1)), T is T1-1, assert(tdepth(T)), | |
307 | print_tab(T), print('> exit '), print(T), print(' : '), print_term_summary(E),nl,fail. | |
308 | :- public print_tabs/0. | |
309 | print_tabs :- tdepth(T), print_tab(T). | |
310 | print_tab(0) :- !, print(' '). | |
311 | print_tab(N) :- N>0, print('|-+-'), N1 is N-1, print_tab(N1). | |
312 | ||
313 | % ------------------------------------------- | |
314 | ||
315 | % use to log terms into a file (alternative to writeln_log) | |
316 | log(Term) :- F='~/problog.pl', | |
317 | open(F,append,S), | |
318 | write_term(S,Term,[quoted(true)]), write(S,'.'),nl(S), | |
319 | close(S). | |
320 | ||
321 | ||
322 | % ------------------------------------------- | |
323 | :- use_module(library(terms),[term_hash/2]). | |
324 | % succeeds or fails depending on Term and on bisect_list (a list of 0s and 1s) | |
325 | bisect(Term,List) :- term_hash(Term,Hash), | |
326 | (mods_match(List,Hash) -> print(match(List,Hash)),nl ; print(no_match(List,Hash)),nl,fail). | |
327 | ||
328 | mods_match([],_). | |
329 | mods_match(['*'|T],Hash) :- !, Hash2 is Hash//2, mods_match(T,Hash2). | |
330 | mods_match([H|T],Hash) :- H is Hash mod 2, | |
331 | Hash2 is Hash//2, mods_match(T,Hash2). | |
332 | ||
333 |