1/* Part of SWI-Prolog 2 3 Author: Jan Wielemaker 4 E-mail: J.Wielemaker@vu.nl 5 WWW: http://www.swi-prolog.org 6 Copyright (c) 2002-2016, University of Amsterdam 7 VU University Amsterdam 8 All rights reserved. 9 10 Redistribution and use in source and binary forms, with or without 11 modification, are permitted provided that the following conditions 12 are met: 13 14 1. Redistributions of source code must retain the above copyright 15 notice, this list of conditions and the following disclaimer. 16 17 2. Redistributions in binary form must reproduce the above copyright 18 notice, this list of conditions and the following disclaimer in 19 the documentation and/or other materials provided with the 20 distribution. 21 22 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 23 "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 24 LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 25 FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 26 COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 27 INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 28 BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 29 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 30 CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 31 LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 32 ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 33 POSSIBILITY OF SUCH DAMAGE. 34*/ 35 36:- module(prolog_debug, 37 [ debug/3, % +Topic, +Format, :Args 38 debug/1, % +Topic 39 nodebug/1, % +Topic 40 debugging/1, % ?Topic 41 debugging/2, % ?Topic, ?Bool 42 list_debug_topics/0, 43 debug_message_context/1, % (+|-)What 44 45 assertion/1 % :Goal 46 ]). 47:- use_module(library(error)). 48:- use_module(library(lists)). 49:- set_prolog_flag(generate_debug_info, false). 50 51:- meta_predicate 52 assertion( ), 53 debug( , , ). 54 55:- multifile prolog:assertion_failed/2. 56:- dynamic prolog:assertion_failed/2. 57 58/*:- use_module(library(prolog_stack)).*/ % We use the autoloader if needed 59 60%:- set_prolog_flag(generate_debug_info, false). 61 62:- dynamic 63 debugging/3, % Topic, Enabled, To 64 debug_context/1. 65 66debug_context(thread).
debugging(+Topic)
may be used to
perform more complex debugging tasks. A typical usage skeleton
is:
( debugging(mytopic) -> <perform debugging actions> ; true ), ...
The other two calls are intended to examine existing and enabled debugging tokens and are typically not used in user programs.
107debugging(Topic) :- 108 debugging(Topic, true, _To). 109 110debugging(Topic, Bool) :- 111 debugging(Topic, Bool, _To).
nodebug(_)
removes all
topics. Gives a warning if the topic is not defined unless it is
used from a directive. The latter allows placing debug topics at
the start of a (load-)file without warnings.
For debug/1, Topic can be a term Topic > Out, where Out is either a stream or stream-alias or a filename (atom). This redirects debug information on this topic to the given output.
125debug(Topic) :- 126 debug(Topic, true). 127nodebug(Topic) :- 128 debug(Topic, false). 129 130debug(Spec, Val) :- 131 debug_target(Spec, Topic, Out), 132 ( ( retract(debugging(Topic, Enabled0, To0)) 133 *-> update_debug(Enabled0, To0, Val, Out, Enabled, To), 134 assert(debugging(Topic, Enabled, To)), 135 fail 136 ; ( prolog_load_context(file, _) 137 -> true 138 ; print_message(warning, debug_no_topic(Topic)) 139 ), 140 update_debug(false, [], Val, Out, Enabled, To), 141 assert(debugging(Topic, Enabled, To)) 142 ) 143 -> true 144 ; true 145 ). 146 147debug_target(Spec, Topic, To) :- 148 nonvar(Spec), 149 Spec = (Topic > To), 150 !. 151debug_target(Topic, Topic, -). 152 153update_debug(_, To0, true, -, true, To) :- 154 !, 155 ensure_output(To0, To). 156update_debug(true, To0, true, Out, true, Output) :- 157 !, 158 ( memberchk(Out, To0) 159 -> Output = To0 160 ; append(To0, [Out], Output) 161 ). 162update_debug(false, _, true, Out, true, [Out]) :- !. 163update_debug(_, _, false, -, false, []) :- !. 164update_debug(true, [Out], false, Out, false, []) :- !. 165update_debug(true, To0, false, Out, true, Output) :- 166 !, 167 delete(To0, Out, Output). 168 169ensure_output([], [user_error]) :- !. 170ensure_output(List, List).
177debug_topic(Topic) :-
178 ( debugging(Registered, _, _),
179 Registered =@= Topic
180 -> true
181 ; assert(debugging(Topic, false, []))
182 ).
188list_debug_topics :-
189 format(user_error, '~`-t~45|~n', []),
190 format(user_error, '~w~t ~w~35| ~w~n',
191 ['Debug Topic', 'Activated', 'To']),
192 format(user_error, '~`-t~45|~n', []),
193 ( debugging(Topic, Value, To),
194 format(user_error, '~w~t ~w~35| ~w~n', [Topic, Value, To]),
195 fail
196 ; true
197 ).
thread
, time
or
time(Format)
, where Format is a format specification for
format_time/3 (default is %T.%3f
). Initially, debug/3 shows
only thread information.207debug_message_context(+Topic) :- 208 !, 209 valid_topic(Topic, Del, Add), 210 retractall(debug_context(Del)), 211 assert(debug_context(Add)). 212debug_message_context(-Topic) :- 213 !, 214 valid_topic(Topic, Del, _), 215 retractall(debug_context(Del)). 216debug_message_context(Term) :- 217 type_error(debug_message_context, Term). 218 219valid_topic(thread, thread, thread) :- !. 220valid_topic(time, time(_), time('%T.%3f')) :- !. 221valid_topic(time(Format), time(_), time(Format)) :- !. 222valid_topic(X, _, _) :- 223 domain_error(debug_message_context, X).
user_error
, but only prints if Topic is activated through
debug/1. Args is a meta-argument to deal with goal for the
@-command. Output is first handed to the hook
prolog:debug_print_hook/3. If this fails, Format+Args is
translated to text using the message-translation (see
print_message/2) for the term debug(Format, Args)
and then
printed to every matching destination (controlled by debug/1)
using print_message_lines/3.
The message is preceded by '% ' and terminated with a newline.
242debug(Topic, Format, Args) :- 243 debugging(Topic, true, To), 244 !, 245 print_debug(Topic, To, Format, Args). 246debug(_, _, _).
?- prolog_ide(debug_monitor).
258:- multifile 259 prolog:debug_print_hook/3. 260 261print_debug(Topic, _To, Format, Args) :- 262 prolog:debug_print_hook(Topic, Format, Args), 263 !. 264print_debug(_, [], _, _) :- !. 265print_debug(Topic, To, Format, Args) :- 266 phrase('$messages':translate_message(debug(Format, Args)), Lines), 267 ( member(T, To), 268 debug_output(T, Stream), 269 with_output_to( 270 Stream, 271 print_message_lines(current_output, kind(debug(Topic)), Lines)), 272 fail 273 ; true 274 ). 275 276 277debug_output(user, user_error) :- !. 278debug_output(Stream, Stream) :- 279 is_stream(Stream), 280 !. 281debug_output(File, Stream) :- 282 open(File, append, Stream, 283 [ close_on_abort(false), 284 alias(File), 285 buffer(line) 286 ]). 287 288 289 /******************************* 290 * ASSERTION * 291 *******************************/
assert()
macro. It has no effect if Goal
succeeds. If Goal fails or throws an exception, the following
steps are taken:
error(assertion_error(Reason, G),_)
where
Reason is one of fail
or the exception raised.307assertion(G) :- 308 \+ \+ catch(, 309 Error, 310 assertion_failed(Error, G)), 311 312 !. 313assertion(G) :- 314 assertion_failed(fail, G), 315 assertion_failed. % prevent last call optimization. 316 317assertion_failed(Reason, G) :- 318 prolog:assertion_failed(Reason, G), 319 !. 320assertion_failed(Reason, _) :- 321 assertion_rethrow(Reason), 322 !, 323 throw(Reason). 324assertion_failed(Reason, G) :- 325 print_message(error, assertion_failed(Reason, G)), 326 backtrace(10), 327 ( current_prolog_flag(break_level, _) % interactive thread 328 -> trace 329 ; throw(error(assertion_error(Reason, G), _)) 330 ). 331 332assertion_failed. 333 334assertion_rethrow(time_limit_exceeded). 335assertion_rethrow('$aborted').
assert()
macro. It has no effect of Goal
succeeds. If Goal fails it prints a message, a stack-trace
and finally traps the debugger.
345 /******************************* 346 * EXPANSION * 347 *******************************/ 348 349% The optimise_debug flag defines whether Prolog optimizes 350% away assertions and debug/3 statements. Values are =true= 351% (debug is optimized away), =false= (debug is retained) and 352% =default= (debug optimization is dependent on the optimise 353% flag). 354 355optimise_debug :- 356 ( current_prolog_flag(optimise_debug, true) 357 -> true 358 ; current_prolog_flag(optimise_debug, default), 359 current_prolog_flag(optimise, true) 360 -> true 361 ). 362 363:- multifile 364 system:goal_expansion/2. 365 366systemgoal_expansion(debug(Topic,_,_), true) :- 367 ( optimise_debug 368 -> true 369 ; debug_topic(Topic), 370 fail 371 ). 372systemgoal_expansion(debugging(Topic), fail) :- 373 ( optimise_debug 374 -> true 375 ; debug_topic(Topic), 376 fail 377 ). 378systemgoal_expansion(assertion(_), true) :- 379 optimise_debug. 380systemgoal_expansion(assume(_), true) :- 381 print_message(informational, 382 compatibility(renamed(assume/1, assertion/1))), 383 optimise_debug. 384 385 386 /******************************* 387 * MESSAGES * 388 *******************************/ 389 390:- multifile 391 prolog:message/3. 392 393prologmessage(assertion_failed(_, G)) --> 394 [ 'Assertion failed: ~q'-[G] ]. 395prologmessage(debug(Fmt, Args)) --> 396 show_thread_context, 397 show_time_context, 398 [ Fmt-Args ]. 399prologmessage(debug_no_topic(Topic)) --> 400 [ '~q: no matching debug topic (yet)'-[Topic] ]. 401 402show_thread_context --> 403 { debug_context(thread), 404 thread_self(Me), 405 report_as(Me, Name) 406 }, 407 [ '[Thread ~w] '-[Name] ]. 408show_thread_context --> 409 []. 410 411report_as(main, _) :- 412 !, 413 fail. 414report_as(Alias, Alias) :- 415 atom(Alias), 416 !. 417report_as(Handle, Id) :- 418 catch(thread_property(Handle, id(Id)), _, fail), 419 !. 420report_as(Thread, Thread). 421 422show_time_context --> 423 { debug_context(time(Format)), 424 get_time(Now), 425 format_time(string(S), Format, Now) 426 }, 427 [ '[~w] '-[S] ]. 428show_time_context --> 429 []. 430 431 /******************************* 432 * HOOKS * 433 *******************************/
fail
if Goal simply failed or an exception
call otherwise. If this hook fails, the default behaviour is
activated. If the hooks throws an exception it will be
propagated into the caller of assertion/1.444 /******************************* 445 * SANDBOX * 446 *******************************/ 447 448:- multifile sandbox:safe_meta/2. 449 450sandbox:safe_meta(prolog_debug:assertion(X), [X])
Print debug messages and test assertions
This library is a replacement for format/3 for printing debug messages. Messages are assigned a topic. By dynamically enabling or disabling topics the user can select desired messages. Debug statements are removed when the code is compiled for optimization.
See manual for details. With XPCE, you can use the call below to start a graphical monitoring tool.
Using the predicate assertion/1 you can make assumptions about your program explicit, trapping the debugger if the condition does not hold.