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-2018, University of Amsterdam 7 VU University Amsterdam 8 CWI, Amsterdam 9 All rights reserved. 10 11 Redistribution and use in source and binary forms, with or without 12 modification, are permitted provided that the following conditions 13 are met: 14 15 1. Redistributions of source code must retain the above copyright 16 notice, this list of conditions and the following disclaimer. 17 18 2. Redistributions in binary form must reproduce the above copyright 19 notice, this list of conditions and the following disclaimer in 20 the documentation and/or other materials provided with the 21 distribution. 22 23 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 24 "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 25 LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 26 FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 27 COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 28 INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 29 BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 30 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 31 CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 32 LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 33 ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 34 POSSIBILITY OF SUCH DAMAGE. 35*/ 36 37:- module(prolog_breakpoints, 38 [ set_breakpoint/4, % +File, +Line, +CharPos, -Id 39 set_breakpoint/5, % +Owner, +File, +Line, +CharPos, -Id 40 delete_breakpoint/1, % +Id 41 breakpoint_property/2 % ?Id, ?Property 42 ]). 43:- autoload(library(debug), [debug/3]). 44:- autoload(library(error), [existence_error/2]). 45:- autoload(library(lists), [nth1/3, member/2]). 46:- autoload(library(prolog_clause), [clause_info/4, clause_name/2]).
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 for the breakpoint to take effect. 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.
85set_breakpoint(File, Line, Char, Id) :- 86 set_breakpoint(File, File, Line, Char, Id). 87set_breakpoint(Owner, File, Line, Char, Id) :- 88 debug(break, 'break_at(~q, ~d, ~d).', [File, Line, Char]), 89 '$clause_from_source'(Owner, File, Line, ClauseRefs), 90 member(ClauseRef, ClauseRefs), 91 clause_info(ClauseRef, InfoFile, TermPos, _NameOffset), 92 ( InfoFile == File 93 -> '$break_pc'(ClauseRef, PC, NextPC), 94 debug(break, 'Clause ~p, PC=~p NextPC=~p', [ClauseRef, PC, NextPC]), 95 '$clause_term_position'(ClauseRef, NextPC, List), 96 debug(break, 'Location = ~w', [List]), 97 range(List, TermPos, A, Z), 98 debug(break, 'Term from ~w-~w', [A, Z]), 99 Z >= Char, !, 100 Len is Z - A, 101 b_setval('$breakpoint', file_location(File, Line, A, Len)) 102 ; print_message(warning, breakpoint(no_source(ClauseRef, File, Line))), 103 '$break_pc'(ClauseRef, PC, _), !, 104 nb_delete('$breakpoint') 105 ), 106 debug(break, 'Break at clause ~w, PC=~w', [ClauseRef, PC]), 107 '$break_at'(ClauseRef, PC, true), 108 nb_delete('$breakpoint'), 109 known_breakpoint(ClauseRef, PC, _Location, Id). 110 111range(_, Pos, _, _) :- 112 var(Pos), !, fail. 113range([], Pos, A, Z) :- 114 arg(1, Pos, A), 115 arg(2, Pos, Z). 116range([H|T], term_position(_, _, _, _, PosL), A, Z) :- 117 nth1(H, PosL, Pos), 118 range(T, Pos, A, Z). 119 120:- dynamic 121 known_breakpoint/4, % ClauseRef, PC, Location, Id 122 break_id/1. 123 124next_break_id(Id) :- 125 retract(break_id(Id0)), 126 !, 127 Id is Id0+1, 128 asserta(break_id(Id)). 129next_break_id(1) :- 130 asserta(break_id(1)).
print_message(breakpoint(delete, Id))
is called. Message hooks
working on this message may still call breakpoint_property/2.
140delete_breakpoint(Id) :- 141 integer(Id), 142 known_breakpoint(ClauseRef, PC, _Location, Id), 143 !, 144 '$break_at'(ClauseRef, PC, false). 145delete_breakpoint(Id) :- 146 existence_error(breakpoint, Id).
164breakpoint_property(Id, file(File)) :- 165 known_breakpoint(ClauseRef,_,_,Id), 166 clause_property(ClauseRef, file(File)). 167breakpoint_property(Id, line_count(Line)) :- 168 known_breakpoint(_,_,Location,Id), 169 location_line(Location, Line). 170breakpoint_property(Id, character_range(Start, Len)) :- 171 known_breakpoint(ClauseRef,PC,Location,Id), 172 ( Location = file_location(_File, _Line, Start, Len) 173 -> true 174 ; break_location(ClauseRef, PC, _File, Start-End), 175 Len is End+1-Start 176 ). 177breakpoint_property(Id, clause(Reference)) :- 178 known_breakpoint(Reference,_,_,Id). 179 180location_line(file_location(_File, Line, _Start, _Len), Line). 181location_line(file_character_range(File, Start, _Len), Line) :- 182 file_line(File, Start, Line). 183location_line(file_line(_File, Line), Line).
191file_line(File, Start, Line) :- 192 setup_call_cleanup( 193 prolog_clause:try_open_source(File, In), 194 stream_line(In, Start, 1, Line), 195 close(In)). 196 197stream_line(In, _, Line0, Line) :- 198 at_end_of_stream(In), 199 !, 200 Line = Line0. 201stream_line(In, Index, Line0, Line) :- 202 skip(In, 0'\n), 203 character_count(In, At), 204 ( At > Index 205 -> Line = Line0 206 ; Line1 is Line0+1, 207 stream_line(In, Index, Line1, Line) 208 ). 209 210 211 /******************************* 212 * FEEDBACK * 213 *******************************/ 214 215:- initialization 216 prolog_unlisten(break, onbreak), 217 prolog_listen(break, onbreak). 218 219onbreak(exist, ClauseRef, PC) :- 220 known_breakpoint(ClauseRef, PC, _Location, Id), 221 !, 222 break_message(breakpoint(exist, Id)). 223onbreak(true, ClauseRef, PC) :- 224 !, 225 debug(break, 'Trap in Clause ~p, PC ~d', [ClauseRef, PC]), 226 with_mutex('$break', next_break_id(Id)), 227 ( nb_current('$breakpoint', Location) 228 -> true 229 ; break_location(ClauseRef, PC, File, A-Z) 230 -> Len is Z+1-A, 231 Location = file_character_range(File, A, Len) 232 ; clause_property(ClauseRef, file(File)), 233 clause_property(ClauseRef, line_count(Line)) 234 -> Location = file_line(File, Line) 235 ; Location = unknown 236 ), 237 asserta(known_breakpoint(ClauseRef, PC, Location, Id)), 238 break_message(breakpoint(set, Id)). 239onbreak(false, ClauseRef, PC) :- 240 debug(break, 'Remove breakpoint from ~p, PC ~d', [ClauseRef, PC]), 241 clause(known_breakpoint(ClauseRef, PC, _Location, Id), true, Ref), 242 call_cleanup(break_message(breakpoint(delete, Id)), erase(Ref)). 243onbreak(gc, ClauseRef, PC) :- 244 debug(break, 'Remove breakpoint from ~p, PC ~d (due to CGC)', 245 [ClauseRef, PC]), 246 retractall(known_breakpoint(ClauseRef, PC, _Location, _Id)). 247 248break_message(Message) :- 249 print_message(informational, Message).
259break_location(ClauseRef, PC, File, A-Z) :- 260 clause_info(ClauseRef, File, TermPos, _NameOffset), 261 '$fetch_vm'(ClauseRef, PC, NPC, _VMI), 262 '$clause_term_position'(ClauseRef, NPC, List), 263 debug(break, 'ClausePos = ~w', [List]), 264 range(List, TermPos, A, Z), 265 debug(break, 'Range: ~d .. ~d', [A, Z]). 266 267 268 /******************************* 269 * MESSAGES * 270 *******************************/ 271 272:- multifile 273 prolog:message/3. 274 275prologmessage(breakpoint(no_source(ClauseRef, _File, Line))) --> 276 [ 'Failed to find line ~d in body of clause ~p. Breaking at start of body.'- 277 [Line, ClauseRef] 278 ]. 279prologmessage(breakpoint(SetClear, Id)) --> 280 setclear(SetClear), 281 breakpoint(Id). 282 283setclear(set) --> 284 ['Breakpoint ']. 285setclear(exist) --> 286 ['Existing breakpoint ']. 287setclear(delete) --> 288 ['Deleted breakpoint ']. 289 290breakpoint(Id) --> 291 breakpoint_name(Id), 292 ( { breakpoint_property(Id, file(File)), 293 file_base_name(File, Base), 294 breakpoint_property(Id, line_count(Line)) 295 } 296 -> [ ' at ~w:~d'-[Base, Line] ] 297 ; [] 298 ). 299 300breakpoint_name(Id) --> 301 { breakpoint_property(Id, clause(ClauseRef)) }, 302 ( { clause_property(ClauseRef, erased) } 303 -> ['~w'-[Id]] 304 ; { clause_name(ClauseRef, Name) }, 305 ['~w in ~w'-[Id, Name]] 306 )
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 user:message_hook/3 to intercept these message terms:
breakpoint(set, Id)
breakpoint(delete, Id)
Note that the hook must fail after creating its side-effects to give other hooks the opportunity to react. */