View source with raw comments or as raw
    1/*  Part of SWI-Prolog
    2
    3    Author:        Jan Wielemaker and Anjo Anjewierden
    4    E-mail:        J.Wielemaker@vu.nl
    5    WWW:           http://www.swi-prolog.org/
    6    Copyright (c)  2011-2015, 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_breakpoints,
   37          [ set_breakpoint/4,           % +File, +Line, +CharPos, -Id
   38            set_breakpoint/5,           % +Owner, +File, +Line, +CharPos, -Id
   39            delete_breakpoint/1,        % +Id
   40            breakpoint_property/2       % ?Id, ?Property
   41          ]).   42:- use_module(prolog_clause).   43:- use_module(library(debug)).   44:- use_module(library(error)).

Manage Prolog break-points

This module provides an interface for development tools to set and delete break-points, giving a location in the source. Development tools that want to track changes to breakpoints must use message_hook/3 to intercept these message terms:

Note that the hook must fail after creating its side-effects to give other hooks the opportunity to react. */

   61:- dynamic
   62    user:prolog_event_hook/1.   63:- multifile
   64    user:prolog_event_hook/1.
 set_breakpoint(+File, +Line, +Char, -Id) is det
 set_breakpoint(+Owner, +File, +Line, +Char, -Id) is det
Put a breakpoint at the indicated source-location. File is a current sourcefile (as reported by source_file/1). Line is the 1-based line in which Char is. Char is the position of the break.

First, '$clause_from_source'/4 uses the SWI-Prolog clause-source information to find the last clause starting before Line. '$break_pc' generated (on backtracking), a list of possible break-points.

Note that in addition to setting the break-point, the system must be in debug mode. With threading enabled, there are various different ways this may be done. See debug/0, tdebug/0 and tdebug/1. Therefore, this predicate does not enable debug mode.

Arguments:
Owner- denotes the file that owns the clause. set_breakpoint/5 is used to set breakpoints in an included file in the context of the Owner main file. See source_file_property/2.
   90set_breakpoint(File, Line, Char, Id) :-
   91    set_breakpoint(File, File, Line, Char, Id).
   92set_breakpoint(Owner, File, Line, Char, Id) :-
   93    debug(break, 'break_at(~q, ~d, ~d).', [File, Line, Char]),
   94    '$clause_from_source'(Owner, File, Line, ClauseRef),
   95    clause_info(ClauseRef, InfoFile, TermPos, _NameOffset),
   96    (   InfoFile == File
   97    ->  '$break_pc'(ClauseRef, PC, NextPC),
   98        debug(break, 'Clause ~p, PC=~p NextPC=~p', [ClauseRef, PC, NextPC]),
   99        '$clause_term_position'(ClauseRef, NextPC, List),
  100        debug(break, 'Location = ~w', [List]),
  101        range(List, TermPos, _0A, Z),
  102        debug(break, 'Term from ~w-~w', [_0A, Z]),
  103        Z >= Char, !
  104    ;   format('Failed to unify clause ~p, using first break',
  105               [ClauseRef]),
  106        '$break_pc'(ClauseRef, PC, _), !
  107    ),
  108    debug(break, 'Break at clause ~w, PC=~w', [ClauseRef, PC]),
  109    with_mutex('$break', next_break_id(Id)),
  110    Location = file_position(File, Line, Char),
  111    asserta(known_breakpoint(ClauseRef, PC, Location, Id), Ref),
  112    catch('$break_at'(ClauseRef, PC, true), E,
  113          (erase(Ref), throw(E))).
  114
  115
  116range(_,  Pos, _, _) :-
  117    var(Pos), !, fail.
  118range([], Pos, A, Z) :-
  119    arg(1, Pos, A),
  120    arg(2, Pos, Z).
  121range([H|T], term_position(_, _, _, _, PosL), A, Z) :-
  122    nth1(H, PosL, Pos),
  123    range(T, Pos, A, Z).
  124
  125:- dynamic
  126    known_breakpoint/4,             %
  127    break_id/1.  128
  129next_break_id(Id) :-
  130    retract(break_id(Id0)),
  131    !,
  132    Id is Id0+1,
  133    asserta(break_id(Id)).
  134next_break_id(1) :-
  135    asserta(break_id(1)).
 delete_breakpoint(+Id) is det
Delete breakpoint with given Id. If successful, print_message(breakpoint(delete, Id)) is called. Message hooks working on this message may still call breakpoint_property/2.
Errors
- existence_error(breakpoint, Id).
  145delete_breakpoint(Id) :-
  146    integer(Id),
  147    known_breakpoint(ClauseRef, PC, _Location, Id),
  148    !,
  149    '$break_at'(ClauseRef, PC, false).
  150delete_breakpoint(Id) :-
  151    existence_error(breakpoint, Id).
 breakpoint_property(?Id, ?Property) is nondet
True when Property is a property of the breakpoint Id. Defined properties are:
file(File)
Provided if the breakpoint is in a clause associated to a file. May not be known.
line_count(Line)
Line of the breakpoint. May not be known.
character_range(Start, Len)
One-based character offset of the break-point. May not be known.
clause(Reference)
Reference of the clause in which the breakpoint resides.
  169breakpoint_property(Id, file(File)) :-
  170    known_breakpoint(ClauseRef,_,_,Id),
  171    clause_property(ClauseRef, file(File)).
  172breakpoint_property(Id, line_count(Line)) :-
  173    known_breakpoint(_,_,Location,Id),
  174    location_line(Location, Line).
  175breakpoint_property(Id, character_range(Start, Len)) :-
  176    known_breakpoint(ClauseRef,PC,_,Id),
  177    (   known_breakpoint(_,_,file_character_range(Start,Len),Id)
  178    ;   break_location(ClauseRef, PC, _File, Start-End),
  179        Len is End+1-Start
  180    ).
  181breakpoint_property(Id, clause(Reference)) :-
  182    known_breakpoint(Reference,_,_,Id).
  183
  184location_line(file_position(_File, Line, _Char), Line).
  185location_line(file_character_range(File, Start, _Len), Line) :-
  186    file_line(File, Start, Line).
  187location_line(file_line(_File, Line), Line).
 file_line(+File, +StartIndex, -Line) is det
True when Line is the 1-based line offset in which we find character StartIndex.
  195file_line(File, Start, Line) :-
  196    setup_call_cleanup(
  197        open(File, read, In),
  198        stream_line(In, Start, 1, Line),
  199        close(In)).
  200
  201stream_line(In, _, Line0, Line) :-
  202    at_end_of_stream(In),
  203    !,
  204    Line = Line0.
  205stream_line(In, Index, Line0, Line) :-
  206    skip(In, 0'\n),
  207    character_count(In, At),
  208    (   At > Index
  209    ->  Line = Line0
  210    ;   Line1 is Line0+1,
  211        stream_line(In, Index, Line1, Line)
  212    ).
  213
  214
  215                 /*******************************
  216                 *            FEEDBACK          *
  217                 *******************************/
  218
  219user:prolog_event_hook(break(ClauseRef, PC, Set)) :-
  220    break(Set, ClauseRef, PC).
  221
  222break(true, ClauseRef, PC) :-
  223    known_breakpoint(ClauseRef, PC, _Location, Id),
  224    !,
  225    print_message(informational, breakpoint(set, Id)).
  226break(true, ClauseRef, PC) :-
  227    !,
  228    debug(break, 'Trap in Clause ~p, PC ~d', [ClauseRef, PC]),
  229    with_mutex('$break', next_break_id(Id)),
  230    (   break_location(ClauseRef, PC, File, A-Z)
  231    ->  Len is Z+1-A,
  232        Location = file_character_range(File, A, Len)
  233    ;   clause_property(ClauseRef, file(File)),
  234        clause_property(ClauseRef, line_count(Line))
  235    ->  Location = file_line(File, Line)
  236    ;   Location = unknown
  237    ),
  238    asserta(known_breakpoint(ClauseRef, PC, Location, Id)),
  239    print_message(informational, breakpoint(set, Id)).
  240break(false, ClauseRef, PC) :-
  241    debug(break, 'Remove breakpoint from ~p, PC ~d', [ClauseRef, PC]),
  242    clause(known_breakpoint(ClauseRef, PC, _Location, Id), true, Ref),
  243    call_cleanup(print_message(informational, breakpoint(delete, Id)),
  244                 erase(Ref)).
 break_location(+ClauseRef, +PC, -File, -AZ) is det
True when File and AZ represent the location of the goal called at PC in ClauseRef.
Arguments:
AZ- is a term A-Z, where A and Z are character positions in File.
  254break_location(ClauseRef, PC, File, A-Z) :-
  255    clause_info(ClauseRef, File, TermPos, _NameOffset),
  256    '$fetch_vm'(ClauseRef, PC, NPC, _VMI),
  257    '$clause_term_position'(ClauseRef, NPC, List),
  258    debug(break, 'ClausePos = ~w', [List]),
  259    range(List, TermPos, A, Z),
  260    debug(break, 'Range: ~d .. ~d', [A, Z]).
  261
  262
  263                 /*******************************
  264                 *            MESSAGES          *
  265                 *******************************/
  266
  267:- multifile
  268    prolog:message/3.  269
  270prolog:message(breakpoint(SetClear, Id)) -->
  271    setclear(SetClear),
  272    breakpoint(Id).
  273
  274setclear(set) -->
  275    ['Breakpoint '].
  276setclear(delete) -->
  277    ['Deleted breakpoint '].
  278
  279breakpoint(Id) -->
  280    breakpoint_name(Id),
  281    (   { breakpoint_property(Id, file(File)),
  282          file_base_name(File, Base),
  283          breakpoint_property(Id, line_count(Line))
  284        }
  285    ->  [ ' at ~w:~d'-[Base, Line] ]
  286    ;   []
  287    ).
  288
  289breakpoint_name(Id) -->
  290    { breakpoint_property(Id, clause(ClauseRef)) },
  291    (   { clause_property(ClauseRef, erased) }
  292    ->  ['~w'-[Id]]
  293    ;   { clause_name(ClauseRef, Name) },
  294        ['~w in ~w'-[Id, Name]]
  295    )