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-2023, University of Amsterdam 7 VU University Amsterdam 8 CWI, Amsterdam 9 SWI-Prolog Solutions b.v. 10 All rights reserved. 11 12 Redistribution and use in source and binary forms, with or without 13 modification, are permitted provided that the following conditions 14 are met: 15 16 1. Redistributions of source code must retain the above copyright 17 notice, this list of conditions and the following disclaimer. 18 19 2. Redistributions in binary form must reproduce the above copyright 20 notice, this list of conditions and the following disclaimer in 21 the documentation and/or other materials provided with the 22 distribution. 23 24 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 25 "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 26 LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 27 FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 28 COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 29 INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 30 BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 31 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 32 CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 33 LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 34 ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 35 POSSIBILITY OF SUCH DAMAGE. 36*/ 37 38:- module(prolog_debug, 39 [ debug/3, % +Topic, +Format, :Args 40 debug/1, % +Topic 41 nodebug/1, % +Topic 42 debugging/1, % ?Topic 43 debugging/2, % ?Topic, ?Bool 44 list_debug_topics/0, 45 list_debug_topics/1, % +Options 46 debug_message_context/1, % (+|-)What 47 48 assertion/1 % :Goal 49 ]). 50:- autoload(library(lists),[append/3,delete/3,selectchk/3,member/2]). 51:- autoload(library(prolog_stack),[backtrace/1]). 52:- autoload(library(option), [option/3, option/2]). 53 54:- set_prolog_flag(generate_debug_info, false). 55:- create_prolog_flag(optimise_debug, default, 56 [ keep(true), 57 type(oneof([default,false,true])) 58 ]). 59 60:- meta_predicate 61 assertion( ), 62 debug( , , ). 63 64:- multifile prolog:assertion_failed/2. 65:- dynamic prolog:assertion_failed/2. 66 67/*:- use_module(library(prolog_stack)).*/ % We use the autoloader if needed 68 69%:- set_prolog_flag(generate_debug_info, false). 70 71:- dynamic 72 debugging/3. % Topic, Enabled, To
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.
111debugging(Topic) :- 112 debugging(Topic, true, _To). 113 114debugging(Topic, Bool) :- 115 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 (an atom). This
redirects debug information on this topic to the given output. On
Linux systems redirection can be used to make the message appear,
even if the user_error
stream is redefined using
?- debug(Topic > '/proc/self/fd/2').
A platform independent way to get debug messages in the current
console (for example, a swipl-win
window, or login using ssh
to
Prolog running an SSH server from the libssh
pack) is to use:
?- stream_property(S, alias(user_error)), debug(Topic > S).
Do not forget to disable the debugging using nodebug/1 before quitting the console if Prolog must remain running.
143debug(Topic) :- 144 with_mutex(prolog_debug, debug(Topic, true)). 145nodebug(Topic) :- 146 with_mutex(prolog_debug, debug(Topic, false)). 147 148debug(Spec, Val) :- 149 debug_target(Spec, Topic, Out), 150 ( ( retract(debugging(Topic, Enabled0, To0)) 151 *-> update_debug(Enabled0, To0, Val, Out, Enabled, To), 152 assert(debugging(Topic, Enabled, To)), 153 fail 154 ; ( prolog_load_context(file, _) 155 -> true 156 ; print_message(warning, debug_no_topic(Topic)) 157 ), 158 update_debug(false, [], Val, Out, Enabled, To), 159 assert(debugging(Topic, Enabled, To)) 160 ) 161 -> true 162 ; true 163 ). 164 165debug_target(Spec, Topic, To) :- 166 nonvar(Spec), 167 Spec = (Topic > To), 168 !. 169debug_target(Topic, Topic, -). 170 171update_debug(_, To0, true, -, true, To) :- 172 !, 173 ensure_output(To0, To). 174update_debug(true, To0, true, Out, true, Output) :- 175 !, 176 ( memberchk(Out, To0) 177 -> Output = To0 178 ; append(To0, [Out], Output) 179 ). 180update_debug(false, _, true, Out, true, [Out]) :- !. 181update_debug(_, _, false, -, false, []) :- !. 182update_debug(true, [Out], false, Out, false, []) :- !. 183update_debug(true, To0, false, Out, true, Output) :- 184 !, 185 delete(To0, Out, Output). 186 187ensure_output([], [user_error]) :- !. 188ensure_output(List, List).
195debug_topic(Topic) :-
196 ( debugging(Registered, _, _),
197 Registered =@= Topic
198 -> true
199 ; assert(debugging(Topic, false, []))
200 ).
[search(String)]
or a normal option list. Defined options are:
true
) or inactive
(false
).219list_debug_topics :- 220 list_debug_topics([]). 221 222list_debug_topics(Options) :- 223 ( atom(Options) 224 ; string(Options) 225 ), 226 !, 227 list_debug_topics([search(Options)]). 228list_debug_topics(Options) :- 229 print_message(information, debug_topics(header)), 230 option(active(Value), Options, _), 231 ( debugging(Topic, Value, To), 232 ( option(output(Stream), Options) 233 -> memberchk(Stream, To) 234 ; true 235 ), 236 numbervars(Topic, 0, _, [singletons(true)]), 237 term_string(Topic, String, [quoted(true), numbervars(true)]), 238 ( option(search(Search), Options) 239 -> sub_atom_icasechk(String, _, Search) 240 ; true 241 ), 242 print_message(information, debug_topic(Topic, String, Value, To)), 243 fail 244 ; true 245 ). 246 247:- multifile 248 prolog_debug_tools:debugging_hook/0. 249 250prolog_debug_toolsdebugging_hook :- 251 ( debugging(_, true) 252 -> list_debug_topics([active(true)]) 253 ).
263debug_message_context(+Topic) :- 264 current_prolog_flag(message_context, List), 265 ( memberchk(Topic, List) 266 -> true 267 ; append(List, [Topic], List2), 268 set_prolog_flag(message_context, List2) 269 ). 270debug_message_context(-Topic) :- 271 current_prolog_flag(message_context, List), 272 ( selectchk(Topic, List, Rest) 273 -> set_prolog_flag(message_context, Rest) 274 ; true 275 ).
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.
293debug(Topic, Format, Args) :- 294 debugging(Topic, true, To), 295 !, 296 print_debug(Topic, To, Format, Args). 297debug(_, _, _).
?- prolog_ide(debug_monitor).
309:- multifile 310 prolog:debug_print_hook/3. 311 312print_debug(_Topic, _To, _Format, _Args) :- 313 nb_current(prolog_debug_printing, true), 314 !. 315print_debug(Topic, To, Format, Args) :- 316 setup_call_cleanup( 317 nb_setval(prolog_debug_printing, true), 318 print_debug_guarded(Topic, To, Format, Args), 319 nb_delete(prolog_debug_printing)). 320 321print_debug_guarded(Topic, _To, Format, Args) :- 322 prolog:debug_print_hook(Topic, Format, Args), 323 !. 324print_debug_guarded(_, [], _, _) :- !. 325print_debug_guarded(Topic, To, Format, Args) :- 326 phrase('$messages':translate_message(debug(Format, Args)), Lines), 327 ( member(T, To), 328 debug_output(T, Stream), 329 with_output_to( 330 Stream, 331 print_message_lines(current_output, kind(debug(Topic)), Lines)), 332 fail 333 ; true 334 ). 335 336 337debug_output(user, user_error) :- !. 338debug_output(Stream, Stream) :- 339 is_stream(Stream), 340 !. 341debug_output(File, Stream) :- 342 open(File, append, Stream, 343 [ close_on_abort(false), 344 alias(File), 345 buffer(line) 346 ]). 347 348 349 /******************************* 350 * ASSERTION * 351 *******************************/
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.367assertion(G) :- 368 \+ \+ catch(G, 369 Error, 370 assertion_failed(Error, G)), 371 372 !. 373assertion(G) :- 374 assertion_failed(fail, G), 375 assertion_failed. % prevent last call optimization. 376 377assertion_failed(Reason, G) :- 378 prolog:assertion_failed(Reason, G), 379 !. 380assertion_failed(Reason, _) :- 381 assertion_rethrow(Reason), 382 !, 383 throw(Reason). 384assertion_failed(Reason, G) :- 385 print_message(error, assertion_failed(Reason, G)), 386 backtrace(10), 387 ( current_prolog_flag(break_level, _) % interactive thread 388 -> trace 389 ; throw(error(assertion_error(Reason, G), _)) 390 ). 391 392assertion_failed. 393 394assertion_rethrow(time_limit_exceeded). 395assertion_rethrow('$aborted'). 396 397 398 /******************************* 399 * EXPANSION * 400 *******************************/ 401 402% The optimise_debug flag defines whether Prolog optimizes 403% away assertions and debug/3 statements. Values are =true= 404% (debug is optimized away), =false= (debug is retained) and 405% =default= (debug optimization is dependent on the optimise 406% flag). 407 408optimise_debug :- 409 ( current_prolog_flag(optimise_debug, true) 410 -> true 411 ; current_prolog_flag(optimise_debug, default), 412 current_prolog_flag(optimise, true) 413 -> true 414 ). 415 416:- multifile 417 system:goal_expansion/2. 418 419systemgoal_expansion(debug(Topic,_,_), true) :- 420 ( optimise_debug 421 -> true 422 ; debug_topic(Topic), 423 fail 424 ). 425systemgoal_expansion(debugging(Topic), fail) :- 426 ( optimise_debug 427 -> true 428 ; debug_topic(Topic), 429 fail 430 ). 431systemgoal_expansion(assertion(_), true) :- 432 optimise_debug. 433systemgoal_expansion(assume(_), true) :- 434 print_message(informational, 435 compatibility(renamed(assume/1, assertion/1))), 436 optimise_debug. 437 438 439 /******************************* 440 * MESSAGES * 441 *******************************/ 442 443:- multifile 444 prolog:message/3. 445 446prologmessage(assertion_failed(_, G)) --> 447 [ 'Assertion failed: ~q'-[G] ]. 448prologmessage(debug(Fmt, Args)) --> 449 [ Fmt-Args ]. 450prologmessage(debug_no_topic(Topic)) --> 451 [ '~q: no matching debug topic (yet)'-[Topic] ]. 452prologmessage(debug_topics(header)) --> 453 [ ansi(bold, '~w~t ~w~35| ~w~n', ['Debug Topic', 'Activated', 'To']), 454 '~`\u2015t~48|' 455 ]. 456prologmessage(debug_topic(_, TopicString, true, [user_error])) --> 457 [ ansi(bold, '~s~t \u2714~35|', [TopicString]) ]. 458prologmessage(debug_topic(_, TopicString, true, To)) --> 459 [ ansi(bold, '~s~t \u2714~35| ~q', [TopicString, To]) ]. 460prologmessage(debug_topic(_, TopicString, false, _To)) --> 461 [ '~s~t -~35|'-[TopicString] ]. 462 463 464 /******************************* 465 * HOOKS * 466 *******************************/
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.477 /******************************* 478 * SANDBOX * 479 *******************************/ 480 481:- multifile sandbox:safe_meta/2. 482 483sandbox: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. Calls to debug/3 and assertion/1 are removed when the code is compiled for optimization unless the Prolog flag
optimise_debug
is set totrue
.Using the predicate assertion/1 you can make assumptions about your program explicit, trapping the debugger if the condition does not hold.
Output and actions by these predicates can be configured using hooks to fit your environment. With XPCE, you can use the call below to start a graphical monitoring tool.
*/