View source with formatted 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)  2004-2018, 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_stack,
   37          [ get_prolog_backtrace/2,     % +MaxDepth, -Stack
   38            get_prolog_backtrace/3,     % +MaxDepth, -Stack, +Options
   39            prolog_stack_frame_property/2, % +Frame, ?Property
   40            print_prolog_backtrace/2,   % +Stream, +Stack
   41            print_prolog_backtrace/3,   % +Stream, +Stack, +Options
   42            backtrace/1                 % +MaxDepth
   43          ]).   44:- autoload(library(debug),[debug/3]).   45:- autoload(library(error),[must_be/2]).   46:- autoload(library(lists),[nth1/3,append/3]).   47:- autoload(library(option),[option/2,option/3,merge_options/3]).   48:- autoload(library(prolog_clause),
   49	    [clause_name/2,predicate_name/2,clause_info/4]).   50
   51
   52:- dynamic stack_guard/1.   53:- multifile stack_guard/1.   54
   55:- predicate_options(print_prolog_backtrace/3, 3,
   56                     [ subgoal_positions(boolean)
   57                     ]).   58
   59/** <module> Examine the Prolog stack
   60
   61This module defines  high-level  primitives   for  examining  the Prolog
   62stack,  primarily  intended  to  support   debugging.  It  provides  the
   63following functionality:
   64
   65    * get_prolog_backtrace/2 gets a Prolog representation of the
   66    Prolog stack.  This can be used for printing, but also to enrich
   67    exceptions using prolog_exception_hook/4.  Decorating exceptions
   68    is provided by this library and controlled by the hook
   69    stack_guard/1.
   70
   71    * print_prolog_backtrace/2 prints a backtrace as returned by
   72    get_prolog_backtrace/2
   73
   74    * The shorthand backtrace/1 fetches and prints a backtrace.
   75
   76This library may be enabled by default to improve interactive debugging,
   77for example by adding the lines   below  to your ``<config>/init.pl`` to
   78decorate uncaught exceptions:
   79
   80  ==
   81  :- use_module(library(prolog_stack)).
   82  ==
   83
   84@bug    Use of this library may negatively impact performance of
   85        applications that process (error-)exceptions frequently
   86        as part of their normal processing.
   87*/
   88
   89:- create_prolog_flag(backtrace,            true, [type(boolean), keep(true)]).   90:- create_prolog_flag(backtrace_depth,      20,   [type(integer), keep(true)]).   91:- create_prolog_flag(backtrace_goal_depth, 3,    [type(integer), keep(true)]).   92:- create_prolog_flag(backtrace_show_lines, true, [type(boolean), keep(true)]).   93
   94%!  get_prolog_backtrace(+MaxDepth, -Backtrace) is det.
   95%!  get_prolog_backtrace(+MaxDepth, -Backtrace, +Options) is det.
   96%
   97%   Obtain a backtrace from the current location. The backtrace is a
   98%   list of frames. Each  frame  is  an   opaque  term  that  can be
   99%   inspected using the predicate  prolog_stack_frame_property/2 can
  100%   be used to extract  information  from   these  frames.  Most use
  101%   scenarios will pass the stack   to print_prolog_backtrace/2. The
  102%   following options are provided:
  103%
  104%     * frame(+Frame)
  105%     Start at Frame instead of the current frame.
  106%     * goal_depth(+Depth)
  107%     If Depth > 0, include a shallow copy of the goal arguments
  108%     into the stack.  Default is set by the Prolog flag
  109%     `backtrace_goal_depth`, set to `2` initially, showing the
  110%     goal and toplevel of any argument.
  111%     * guard(+Guard)
  112%     Do not show stack frames above Guard.  See stack_guard/1.
  113%     * clause_references(+Bool)
  114%     Report locations as `Clause+PC` or as a location term that
  115%     does not use clause references, allowing the exception to
  116%     be printed safely in a different context.
  117%
  118%   @param Frame is the frame to start from. See prolog_current_frame/1.
  119%   @param MaxDepth defines the maximum number of frames returned.
  120%   @compat get_prolog_backtrace/3 used to have the parameters
  121%   +Frame, +MaxDepth, -Backtrace. A call that matches this
  122%   signature is mapped to get_prolog_backtrace(MaxDepth, Backtrace,
  123%   [frame(Frame)]).
  124
  125get_prolog_backtrace(MaxDepth, Stack) :-
  126    get_prolog_backtrace(MaxDepth, Stack, []).
  127
  128get_prolog_backtrace(Fr, MaxDepth, Stack) :-
  129    integer(Fr), integer(MaxDepth), var(Stack),
  130    !,
  131    get_prolog_backtrace_lc(MaxDepth, Stack, [frame(Fr)]),
  132    nlc.
  133get_prolog_backtrace(MaxDepth, Stack, Options) :-
  134    get_prolog_backtrace_lc(MaxDepth, Stack, Options),
  135    nlc.            % avoid last-call-optimization, such that
  136                        % the top of the stack is always a nice Prolog
  137                        % frame
  138
  139nlc.
  140
  141get_prolog_backtrace_lc(MaxDepth, Stack, Options) :-
  142    (   option(frame(Fr), Options)
  143    ->  PC = call
  144    ;   prolog_current_frame(Fr0),
  145        prolog_frame_attribute(Fr0, pc, PC),
  146        prolog_frame_attribute(Fr0, parent, Fr)
  147    ),
  148    (   option(goal_term_depth(GoalDepth), Options)
  149    ->  true
  150    ;   current_prolog_flag(backtrace_goal_depth, GoalDepth)
  151    ),
  152    option(guard(Guard), Options, none),
  153    (   def_no_clause_refs(Guard)
  154    ->  DefClauseRefs = false
  155    ;   DefClauseRefs = true
  156    ),
  157    option(clause_references(ClauseRefs), Options, DefClauseRefs),
  158    must_be(nonneg, GoalDepth),
  159    backtrace(MaxDepth, Fr, PC, GoalDepth, Guard, ClauseRefs, Stack, Options).
  160
  161def_no_clause_refs(system:catch_with_backtrace/3).
  162
  163backtrace(0, _, _, _, _, _, [], _) :- !.
  164backtrace(MaxDepth, Fr, PC, GoalDepth, Guard, ClauseRefs,
  165          [frame(Level, Where, Goal)|Stack], Options) :-
  166    prolog_frame_attribute(Fr, level, Level),
  167    (   PC == foreign
  168    ->  prolog_frame_attribute(Fr, predicate_indicator, Pred),
  169        Where = foreign(Pred)
  170    ;   PC == call
  171    ->  prolog_frame_attribute(Fr, predicate_indicator, Pred),
  172        Where = call(Pred)
  173    ;   prolog_frame_attribute(Fr, clause, Clause)
  174    ->  clause_where(ClauseRefs, Clause, PC, Where, Options)
  175    ;   Where = meta_call
  176    ),
  177    (   Where == meta_call
  178    ->  Goal = 0
  179    ;   copy_goal(GoalDepth, Fr, Goal)
  180    ),
  181    (   prolog_frame_attribute(Fr, pc, PC2)
  182    ->  true
  183    ;   PC2 = foreign
  184    ),
  185    (   prolog_frame_attribute(Fr, parent, Parent),
  186        prolog_frame_attribute(Parent, predicate_indicator, PI),
  187        PI == Guard                             % last frame
  188    ->  backtrace(1, Parent, PC2, GoalDepth, Guard, ClauseRefs, Stack, Options)
  189    ;   prolog_frame_attribute(Fr, parent, Parent),
  190        more_stack(Parent)
  191    ->  D2 is MaxDepth - 1,
  192        backtrace(D2, Parent, PC2, GoalDepth, Guard, ClauseRefs, Stack, Options)
  193    ;   Stack = []
  194    ).
  195
  196more_stack(Parent) :-
  197    prolog_frame_attribute(Parent, predicate_indicator, PI),
  198    \+ (   PI = ('$toplevel':G),
  199           G \== (toplevel_call/1)
  200       ),
  201    !.
  202more_stack(_) :-
  203    current_prolog_flag(break_level, Break),
  204    Break >= 1.
  205
  206%!  clause_where(+UseClauseRef, +Clause, +PC, -Where, +Options) is det.
  207%
  208%   Get a description of the frame source   location from Clause and the
  209%   PC inside clause. If UseClauseRef is  `true`,   this  is  the a term
  210%   clause(Clause,PC),  providing  all  abvailable  information  to  the
  211%   caller at low time overhead. If  however   the  exception need to be
  212%   printed in an environment where the   clause  references may differ,
  213%   for example because the program is not   loaded,  it is printed in a
  214%   different thread and contains references to dynamic predicates, etc,
  215%   it is better to use the information inside the clause here.
  216
  217clause_where(true, Clause, PC, clause(Clause, PC), _).
  218clause_where(false, Clause, PC, pred_line(PredName, File:Line), Options) :-
  219    option(subgoal_positions(true), Options, true),
  220    subgoal_position(Clause, PC, File, CharA, _CharZ),
  221    File \= @(_),                 % XPCE Object reference
  222    lineno(File, CharA, Line),
  223    clause_predicate_name(Clause, PredName),
  224    !.
  225clause_where(false, Clause, _PC, pred_line(PredName, File:Line), _Options) :-
  226    clause_property(Clause, file(File)),
  227    clause_property(Clause, line_count(Line)),
  228    clause_predicate_name(Clause, PredName),
  229    !.
  230clause_where(false, Clause, _PC, clause_name(ClauseName), _Options) :-
  231    clause_name(Clause, ClauseName).
  232
  233%!  copy_goal(+TermDepth, +Frame, -Goal) is det.
  234%
  235%   Create a shallow copy of the frame's  goal to help debugging. In
  236%   addition to shallow copying, high-arity   terms  are represented
  237%   as below.  Currently the 16 first arguments are hardcoded.
  238%
  239%     ==
  240%     name(A1, ..., A16, <skipped Skipped of Arity>, An)
  241%     ==
  242
  243copy_goal(0, _, 0) :- !.                        % 0 is not a valid goal
  244copy_goal(D, Fr, Goal) :-
  245    prolog_frame_attribute(Fr, goal, Goal0),
  246    (   Goal0 = Module:Goal1
  247    ->  copy_term_limit(D, Goal1, Goal2),
  248        (   hidden_module(Module)
  249        ->  Goal = Goal2
  250        ;   Goal = Module:Goal2
  251        )
  252    ;   copy_term_limit(D, Goal0, Goal)
  253    ).
  254
  255hidden_module(system).
  256hidden_module(user).
  257
  258copy_term_limit(0, In, '...') :-
  259    compound(In),
  260    !.
  261copy_term_limit(N, In, Out) :-
  262    is_dict(In),
  263    !,
  264    dict_pairs(In, Tag, PairsIn),
  265    N2 is N - 1,
  266    MaxArity = 16,
  267    copy_pairs(PairsIn, N2, MaxArity, PairsOut),
  268    dict_pairs(Out, Tag, PairsOut).
  269copy_term_limit(N, In, Out) :-
  270    compound(In),
  271    !,
  272    compound_name_arity(In, Functor, Arity),
  273    N2 is N - 1,
  274    MaxArity = 16,
  275    (   Arity =< MaxArity
  276    ->  compound_name_arity(Out, Functor, Arity),
  277        copy_term_args(0, Arity, N2, In, Out)
  278    ;   OutArity is MaxArity+2,
  279        compound_name_arity(Out, Functor, OutArity),
  280        copy_term_args(0, MaxArity, N2, In, Out),
  281        SkipArg is MaxArity+1,
  282        Skipped is Arity - MaxArity - 1,
  283        format(atom(Msg), '<skipped ~D of ~D>', [Skipped, Arity]),
  284        arg(SkipArg, Out, Msg),
  285        arg(Arity, In, InA),
  286        arg(OutArity, Out, OutA),
  287        copy_term_limit(N2, InA, OutA)
  288    ).
  289copy_term_limit(_, In, Out) :-
  290    copy_term_nat(In, Out).
  291
  292copy_term_args(I, Arity, Depth, In, Out) :-
  293    I < Arity,
  294    !,
  295    I2 is I + 1,
  296    arg(I2, In, InA),
  297    arg(I2, Out, OutA),
  298    copy_term_limit(Depth, InA, OutA),
  299    copy_term_args(I2, Arity, Depth, In, Out).
  300copy_term_args(_, _, _, _, _).
  301
  302copy_pairs([], _, _, []) :- !.
  303copy_pairs(Pairs, _, 0, ['<skipped>'-Skipped]) :-
  304    !,
  305    length(Pairs, Skipped).
  306copy_pairs([K-V0|T0], N, MaxArity, [K-V|T]) :-
  307    copy_term_limit(N, V0, V),
  308    MaxArity1 is MaxArity - 1,
  309    copy_pairs(T0, N, MaxArity1, T).
  310
  311
  312%!  prolog_stack_frame_property(+Frame, ?Property) is nondet.
  313%
  314%   True when Property is a property of   Frame. Frame is an element
  315%   of a stack-trace as produced by get_prolog_backtrace/2.  Defined
  316%   properties are:
  317%
  318%     * level(Level)
  319%     * predicate(PI)
  320%     * location(File:Line)
  321
  322prolog_stack_frame_property(frame(Level,_,_), level(Level)).
  323prolog_stack_frame_property(frame(_,Where,_), predicate(PI)) :-
  324    frame_predicate(Where, PI).
  325prolog_stack_frame_property(frame(_,clause(Clause,PC),_), location(File:Line)) :-
  326    subgoal_position(Clause, PC, File, CharA, _CharZ),
  327    File \= @(_),                   % XPCE Object reference
  328    lineno(File, CharA, Line).
  329prolog_stack_frame_property(frame(_,_,_,Goal), goal(Goal)) :-
  330    Goal \== 0.
  331
  332
  333frame_predicate(foreign(PI), PI).
  334frame_predicate(call(PI), PI).
  335frame_predicate(clause(Clause, _PC), PI) :-
  336    clause_property(Clause, PI).
  337
  338default_backtrace_options(Options) :-
  339    (   current_prolog_flag(backtrace_show_lines, true)
  340    ->  Options = []
  341    ;   Options = [subgoal_positions(false)]
  342    ).
  343
  344%!  print_prolog_backtrace(+Stream, +Backtrace) is det.
  345%!  print_prolog_backtrace(+Stream, +Backtrace, +Options) is det.
  346%
  347%   Print a stacktrace in human readable form to Stream.
  348%   Options is an option list that accepts:
  349%
  350%       * subgoal_positions(+Boolean)
  351%       If =true=, print subgoal line numbers.  The default depends
  352%       on the Prolog flag =backtrace_show_lines=.
  353%
  354%   @arg Backtrace is a list of frame(Depth,Location,Goal) terms.
  355
  356print_prolog_backtrace(Stream, Backtrace) :-
  357    print_prolog_backtrace(Stream, Backtrace, []).
  358
  359print_prolog_backtrace(Stream, Backtrace, Options) :-
  360    default_backtrace_options(DefOptions),
  361    merge_options(Options, DefOptions, FinalOptions),
  362    phrase(message(Backtrace, FinalOptions), Lines),
  363    print_message_lines(Stream, '', Lines).
  364
  365:- public                               % Called from some handlers
  366    message//1.  367
  368message(Backtrace) -->
  369    {default_backtrace_options(Options)},
  370    message(Backtrace, Options).
  371
  372message(Backtrace, Options) -->
  373    message_frames(Backtrace, Options),
  374    warn_nodebug(Backtrace).
  375
  376message_frames([], _) -->
  377    [].
  378message_frames([H|T], Options) -->
  379    message_frames(H, Options),
  380    (   {T == []}
  381    ->  []
  382    ;   [nl],
  383        message_frames(T, Options)
  384    ).
  385
  386message_frames(frame(Level, Where, 0), Options) -->
  387    !,
  388    level(Level),
  389    where_no_goal(Where, Options).
  390message_frames(frame(Level, _Where, '$toplevel':toplevel_call(_)), _) -->
  391    !,
  392    level(Level),
  393    [ '<user>'-[] ].
  394message_frames(frame(Level, Where, Goal), Options) -->
  395    level(Level),
  396    [ '~p'-[Goal] ],
  397    where_goal(Where, Options).
  398
  399where_no_goal(foreign(PI), _) -->
  400    [ '~w <foreign>'-[PI] ].
  401where_no_goal(call(PI), _) -->
  402    [ '~w'-[PI] ].
  403where_no_goal(pred_line(PredName, File:Line), _) -->
  404    !,
  405    [ '~w at ~w:~d'-[PredName, File, Line] ].
  406where_no_goal(clause_name(ClauseName), _) -->
  407    !,
  408    [ '~w <no source>'-[ClauseName] ].
  409where_no_goal(clause(Clause, PC), Options) -->
  410    { nonvar(Clause),
  411      !,
  412      clause_where(false, Clause, PC, Where, Options)
  413    },
  414    where_no_goal(Where, Options).
  415where_no_goal(meta_call, _) -->
  416    [ '<meta call>' ].
  417
  418where_goal(foreign(_), _) -->
  419    [ ' <foreign>'-[] ],
  420    !.
  421where_goal(pred_line(_PredName, File:Line), _) -->
  422    !,
  423    [ ' at ~w:~d'-[File, Line] ].
  424where_goal(clause_name(ClauseName), _) -->
  425    !,
  426    [ '~w <no source>'-[ClauseName] ].
  427where_goal(clause(Clause, PC), Options) -->
  428    { nonvar(Clause),
  429      !,
  430      clause_where(false, Clause, PC, Where, Options)
  431    },
  432    where_goal(Where, Options).
  433where_goal(clause(Clause, _PC), _) -->
  434    { clause_property(Clause, file(File)),
  435      clause_property(Clause, line_count(Line))
  436    },
  437    !,
  438    [ ' at ~w:~d'-[ File, Line] ].
  439where_goal(clause(Clause, _PC), _) -->
  440    { clause_name(Clause, ClauseName)
  441    },
  442    !,
  443    [ ' ~w <no source>'-[ClauseName] ].
  444where_goal(_, _) -->
  445    [].
  446
  447level(Level) -->
  448    [ '~|~t[~D]~6+ '-[Level] ].
  449
  450warn_nodebug(Backtrace) -->
  451    { contiguous(Backtrace) },
  452    !.
  453warn_nodebug(_Backtrace) -->
  454    [ nl,nl,
  455      'Note: some frames are missing due to last-call optimization.'-[], nl,
  456      'Re-run your program in debug mode (:- debug.) to get more detail.'-[]
  457    ].
  458
  459contiguous([frame(D0,_,_)|Frames]) :-
  460    contiguous(Frames, D0).
  461
  462contiguous([], _).
  463contiguous([frame(D1,_,_)|Frames], D0) :-
  464    D1 =:= D0-1,
  465    contiguous(Frames, D1).
  466
  467
  468%!  clause_predicate_name(+ClauseRef, -Predname) is det.
  469%
  470%   Produce a name (typically  Functor/Arity)   for  a  predicate to
  471%   which Clause belongs.
  472
  473clause_predicate_name(Clause, PredName) :-
  474    user:prolog_clause_name(Clause, PredName),
  475    !.
  476clause_predicate_name(Clause, PredName) :-
  477    nth_clause(Head, _N, Clause),
  478    !,
  479    predicate_name(user:Head, PredName).
  480
  481
  482%!  backtrace(+MaxDepth)
  483%
  484%   Get and print a stacktrace to the user_error stream.
  485
  486backtrace(MaxDepth) :-
  487    get_prolog_backtrace_lc(MaxDepth, Stack, []),
  488    print_prolog_backtrace(user_error, Stack).
  489
  490
  491subgoal_position(ClauseRef, PC, File, CharA, CharZ) :-
  492    debug(backtrace, 'Term-position in ~p at PC=~w:', [ClauseRef, PC]),
  493    clause_info(ClauseRef, File, TPos, _),
  494    '$clause_term_position'(ClauseRef, PC, List),
  495    debug(backtrace, '\t~p~n', [List]),
  496    find_subgoal(List, TPos, PosTerm),
  497    arg(1, PosTerm, CharA),
  498    arg(2, PosTerm, CharZ).
  499
  500find_subgoal([A|T], term_position(_, _, _, _, PosL), SPos) :-
  501    is_list(PosL),
  502    nth1(A, PosL, Pos),
  503    nonvar(Pos),
  504    !,
  505    find_subgoal(T, Pos, SPos).
  506find_subgoal([], Pos, Pos).
  507
  508
  509%!  lineno(+File, +Char, -Line)
  510%
  511%   Translate a character location to  a   line-number.  Note  that this
  512%   calls prolog_clause:try_open_source/2, but this is  always loaded as
  513%   we would not have clause info without.
  514
  515lineno(File, Char, Line) :-
  516    setup_call_cleanup(
  517        ( prolog_clause:try_open_source(File, Fd),
  518          set_stream(Fd, newline(detect))
  519        ),
  520        lineno_(Fd, Char, Line),
  521        close(Fd)).
  522
  523lineno_(Fd, Char, L) :-
  524    stream_property(Fd, position(Pos)),
  525    stream_position_data(char_count, Pos, C),
  526    C > Char,
  527    !,
  528    stream_position_data(line_count, Pos, L0),
  529    L is L0-1.
  530lineno_(Fd, Char, L) :-
  531    skip(Fd, 0'\n),
  532    lineno_(Fd, Char, L).
  533
  534
  535                 /*******************************
  536                 *        DECORATE ERRORS       *
  537                 *******************************/
  538
  539%!  prolog_stack:stack_guard(+PI) is semidet.
  540%
  541%   Dynamic multifile hook that is normally not defined. The hook is
  542%   called with PI equal to =none= if   the  exception is not caught
  543%   and with a fully qualified   (e.g., Module:Name/Arity) predicate
  544%   indicator of the predicate that called  catch/3 if the exception
  545%   is caught.
  546%
  547%   The exception is of the form error(Formal, ImplDef) and this
  548%   hook succeeds, ImplDef is unified to a term
  549%   context(prolog_stack(StackData), Message).  This context
  550%   information is used by the message printing system to print a
  551%   human readable representation of the stack when the exception
  552%   was raised.
  553%
  554%   For example, using a clause   stack_guard(none)  prints contexts
  555%   for uncaught exceptions only.  Using   a  clause  stack_guard(_)
  556%   prints a full  stack-trace  for  any   error  exception  if  the
  557%   exception   is   given    to     print_message/2.    See    also
  558%   library(http/http_error), which limits printing of exceptions to
  559%   exceptions in user-code called from the HTTP server library.
  560%
  561%   Details of the exception decoration is  controlled by two Prolog
  562%   flags:
  563%
  564%       * backtrace_depth
  565%       Integer that controls the maximum number of frames
  566%       collected.  Default is 20.  If a guard is specified, callers
  567%       of the guard are removed from the stack-trace.
  568%
  569%       * backtrace_show_lines
  570%       Boolean that indicates whether the library tries to find
  571%       line numbers for the calls.  Default is =true=.
  572
  573:- multifile
  574    user:prolog_exception_hook/4.  575:- dynamic
  576    user:prolog_exception_hook/4.  577
  578user:prolog_exception_hook(error(E, context(Ctx0,Msg)),
  579                           error(E, context(prolog_stack(Stack),Msg)),
  580                           Fr, GuardSpec) :-
  581    current_prolog_flag(backtrace, true),
  582    \+ is_stack(Ctx0, _Frames),
  583    (   atom(GuardSpec)
  584    ->  debug(backtrace, 'Got uncaught (guard = ~q) exception ~p (Ctx0=~p)',
  585              [GuardSpec, E, Ctx0]),
  586        stack_guard(GuardSpec),
  587        Guard = GuardSpec
  588    ;   prolog_frame_attribute(GuardSpec, predicate_indicator, Guard),
  589        debug(backtrace, 'Got exception ~p (Ctx0=~p, Catcher=~p)',
  590              [E, Ctx0, Guard]),
  591        stack_guard(Guard)
  592    ),
  593    (   current_prolog_flag(backtrace_depth, Depth)
  594    ->  Depth > 0
  595    ;   Depth = 20                  % Thread created before lib was loaded
  596    ),
  597    get_prolog_backtrace(Depth, Stack0,
  598                         [ frame(Fr),
  599                           guard(Guard)
  600                         ]),
  601    debug(backtrace, 'Stack = ~p', [Stack0]),
  602    clean_stack(Stack0, Stack1),
  603    join_stacks(Ctx0, Stack1, Stack).
  604
  605clean_stack(List, List) :-
  606    stack_guard(X), var(X),
  607    !.      % Do not stop if we catch all
  608clean_stack(List, Clean) :-
  609    clean_stack2(List, Clean).
  610
  611clean_stack2([], []).
  612clean_stack2([H|_], [H]) :-
  613    guard_frame(H),
  614    !.
  615clean_stack2([H|T0], [H|T]) :-
  616    clean_stack2(T0, T).
  617
  618guard_frame(frame(_,clause(ClauseRef, _, _))) :-
  619    nth_clause(M:Head, _, ClauseRef),
  620    functor(Head, Name, Arity),
  621    stack_guard(M:Name/Arity).
  622
  623join_stacks(Ctx0, Stack1, Stack) :-
  624    nonvar(Ctx0),
  625    Ctx0 = prolog_stack(Stack0),
  626    is_list(Stack0), !,
  627    append(Stack0, Stack1, Stack).
  628join_stacks(_, Stack, Stack).
  629
  630
  631%!  stack_guard(+Reason) is semidet.
  632%
  633%   Dynamic multifile predicate. It is called  with `none`, `'C'` or
  634%   the predicate indicator of the   _guard_,  the predicate calling
  635%   catch/3. The exception must be of   _compatible_  with the shape
  636%   error(Formal, context(Stack, Msg)). The  default   is  to  catch
  637%   `none`, uncaught exceptions. `'C'`  implies   that  the callback
  638%   from C will handle the exception.
  639
  640stack_guard(none).
  641stack_guard(system:catch_with_backtrace/3).
  642
  643
  644                 /*******************************
  645                 *           MESSAGES           *
  646                 *******************************/
  647
  648:- multifile
  649    prolog:message//1.  650
  651prolog:message(error(Error, context(Stack, Message))) -->
  652    { Message \== 'DWIM could not correct goal',
  653      is_stack(Stack, Frames)
  654    },
  655    !,
  656    '$messages':translate_message(error(Error, context(_, Message))),
  657    [ nl, 'In:', nl ],
  658    (   {is_list(Frames)}
  659    ->  message(Frames)
  660    ;   ['~w'-[Frames]]
  661    ).
  662
  663is_stack(Stack, Frames) :-
  664    nonvar(Stack),
  665    Stack = prolog_stack(Frames)