View source with raw comments or as raw
    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(0),
   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).

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.

?- prolog_ide(debug_monitor).

Using the predicate assertion/1 you can make assumptions about your program explicit, trapping the debugger if the condition does not hold.

author
- Jan Wielemaker */
 debugging(+Topic) is semidet
debugging(-Topic) is nondet
 debugging(?Topic, ?Bool) is nondet
Examine debug topics. The form 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).
 debug(+Topic) is det
 nodebug(+Topic) is det
Add/remove a topic from being printed. 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).
 debug_topic(+Topic) is det
Declare a topic for debugging. This can be used to find all topics available for debugging.
  177debug_topic(Topic) :-
  178    (   debugging(Registered, _, _),
  179        Registered =@= Topic
  180    ->  true
  181    ;   assert(debugging(Topic, false, []))
  182    ).
 list_debug_topics is det
List currently known debug topics and their setting.
  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    ).
 debug_message_context(+What) is det
Specify additional context for debug messages. What is one of +Context or -Context, and Context is one of 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).
 debug(+Topic, +Format, :Args) is det
Format a message if debug topic is enabled. Similar to format/3 to 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.

See also
- format/3.
  242debug(Topic, Format, Args) :-
  243    debugging(Topic, true, To),
  244    !,
  245    print_debug(Topic, To, Format, Args).
  246debug(_, _, _).
 prolog:debug_print_hook(+Topic, +Format, +Args) is semidet
Hook called by debug/3. This hook is used by the graphical frontend that can be activated using prolog_ide/1:
?- 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                 *******************************/
 assertion(:Goal) is det
Acts similar to C assert() macro. It has no effect if Goal succeeds. If Goal fails or throws an exception, the following steps are taken:
  307assertion(G) :-
  308    \+ \+ catch(G,
  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').
 assume(:Goal) is det
Acts similar to C assert() macro. It has no effect of Goal succeeds. If Goal fails it prints a message, a stack-trace and finally traps the debugger.
deprecated
- Use assertion/1 in new code.
  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
  366system:goal_expansion(debug(Topic,_,_), true) :-
  367    (   optimise_debug
  368    ->  true
  369    ;   debug_topic(Topic),
  370        fail
  371    ).
  372system:goal_expansion(debugging(Topic), fail) :-
  373    (   optimise_debug
  374    ->  true
  375    ;   debug_topic(Topic),
  376        fail
  377    ).
  378system:goal_expansion(assertion(_), true) :-
  379    optimise_debug.
  380system:goal_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
  393prolog:message(assertion_failed(_, G)) -->
  394    [ 'Assertion failed: ~q'-[G] ].
  395prolog:message(debug(Fmt, Args)) -->
  396    show_thread_context,
  397    show_time_context,
  398    [ Fmt-Args ].
  399prolog:message(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                 *******************************/
 prolog:assertion_failed(+Reason, +Goal) is semidet
This hook is called if the Goal of assertion/1 fails. Reason is unified with either 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])