35
36:- module(prolog_breakpoints,
37 [ set_breakpoint/4, 38 set_breakpoint/5, 39 delete_breakpoint/1, 40 breakpoint_property/2 41 ]). 42:- use_module(prolog_clause). 43:- use_module(library(debug)). 44:- use_module(library(error)). 45
46
60
61:- dynamic
62 user:prolog_event_hook/1. 63:- multifile
64 user:prolog_event_hook/1. 65
89
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)).
136
144
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).
152
168
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).
188
189
194
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 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)).
245
253
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 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 )