36
37:- module(prolog_breakpoints,
38 [ set_breakpoint/4, 39 set_breakpoint/5, 40 delete_breakpoint/1, 41 breakpoint_property/2 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]). 47
48
62
84
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, SubPos),
98 arg(1, SubPos, A),
99 arg(2, SubPos, Z),
100 debug(break, 'Term from ~w-~w', [A, Z]),
101 Z >= Char, !,
102 Len is Z - A,
103 b_setval('$breakpoint', file_location(File, Line, A, Len))
104 ; print_message(warning, breakpoint(no_source(ClauseRef, File, Line))),
105 '$break_pc'(ClauseRef, PC, _), !,
106 nb_delete('$breakpoint')
107 ),
108 debug(break, 'Break at clause ~w, PC=~w', [ClauseRef, PC]),
109 '$break_at'(ClauseRef, PC, true),
110 nb_delete('$breakpoint'),
111 known_breakpoint(ClauseRef, PC, _Location, Id).
112
113range(_, Pos, _), var(Pos) =>
114 fail.
115range(List, parentheses_term_position(_,_,Pos), SubPos) =>
116 range(List, Pos, SubPos).
117range([], Pos, SubPos) =>
118 SubPos = Pos.
119range([H|T], term_position(_, _, _, _, PosL), SubPos) =>
120 nth1(H, PosL, Pos),
121 range(T, Pos, SubPos).
122range(exit, Pos, SubPos) =>
123 arg(2, Pos, End),
124 Dot is End,
125 EndDot is Dot+1,
126 SubPos = Dot-EndDot.
127
128:- dynamic
129 known_breakpoint/4, 130 break_id/1. 131
132next_break_id(Id) :-
133 retract(break_id(Id0)),
134 !,
135 Id is Id0+1,
136 asserta(break_id(Id)).
137next_break_id(1) :-
138 asserta(break_id(1)).
139
147
148delete_breakpoint(Id) :-
149 integer(Id),
150 known_breakpoint(ClauseRef, PC, _Location, Id),
151 !,
152 '$break_at'(ClauseRef, PC, false).
153delete_breakpoint(Id) :-
154 existence_error(breakpoint, Id).
155
171
172breakpoint_property(Id, file(File)) :-
173 known_breakpoint(ClauseRef,_,_,Id),
174 clause_property(ClauseRef, file(File)).
175breakpoint_property(Id, line_count(Line)) :-
176 known_breakpoint(_,_,Location,Id),
177 location_line(Location, Line).
178breakpoint_property(Id, character_range(Start, Len)) :-
179 known_breakpoint(ClauseRef,PC,Location,Id),
180 ( Location = file_location(_File, _Line, Start, Len)
181 -> true
182 ; break_location(ClauseRef, PC, _File, Start-End),
183 Len is End+1-Start
184 ).
185breakpoint_property(Id, clause(Reference)) :-
186 known_breakpoint(Reference,_,_,Id).
187
188location_line(file_location(_File, Line, _Start, _Len), Line).
189location_line(file_character_range(File, Start, _Len), Line) :-
190 file_line(File, Start, Line).
191location_line(file_line(_File, Line), Line).
192
193
198
199file_line(File, Start, Line) :-
200 setup_call_cleanup(
201 prolog_clause:try_open_source(File, In),
202 stream_line(In, Start, 1, Line),
203 close(In)).
204
205stream_line(In, _, Line0, Line) :-
206 at_end_of_stream(In),
207 !,
208 Line = Line0.
209stream_line(In, Index, Line0, Line) :-
210 skip(In, 0'\n),
211 character_count(In, At),
212 ( At > Index
213 -> Line = Line0
214 ; Line1 is Line0+1,
215 stream_line(In, Index, Line1, Line)
216 ).
217
218
219 222
223:- initialization
224 prolog_unlisten(break, onbreak),
225 prolog_listen(break, onbreak). 226
227onbreak(exist, ClauseRef, PC) :-
228 known_breakpoint(ClauseRef, PC, _Location, Id),
229 !,
230 break_message(breakpoint(exist, Id)).
231onbreak(true, ClauseRef, PC) :-
232 !,
233 debug(break, 'Trap in Clause ~p, PC ~d', [ClauseRef, PC]),
234 with_mutex('$break', next_break_id(Id)),
235 ( nb_current('$breakpoint', Location)
236 -> true
237 ; break_location(ClauseRef, PC, File, A-Z)
238 -> Len is Z+1-A,
239 Location = file_character_range(File, A, Len)
240 ; clause_property(ClauseRef, file(File)),
241 clause_property(ClauseRef, line_count(Line))
242 -> Location = file_line(File, Line)
243 ; Location = unknown
244 ),
245 asserta(known_breakpoint(ClauseRef, PC, Location, Id)),
246 break_message(breakpoint(set, Id)).
247onbreak(false, ClauseRef, PC) :-
248 debug(break, 'Remove breakpoint from ~p, PC ~d', [ClauseRef, PC]),
249 clause(known_breakpoint(ClauseRef, PC, _Location, Id), true, Ref),
250 call_cleanup(break_message(breakpoint(delete, Id)), erase(Ref)).
251onbreak(gc, ClauseRef, PC) :-
252 debug(break, 'Remove breakpoint from ~p, PC ~d (due to CGC)',
253 [ClauseRef, PC]),
254 retractall(known_breakpoint(ClauseRef, PC, _Location, _Id)).
255
256break_message(Message) :-
257 print_message(informational, Message).
258
266
267break_location(ClauseRef, PC, File, A-Z) :-
268 clause_info(ClauseRef, File, TermPos, _NameOffset),
269 '$fetch_vm'(ClauseRef, PC, NPC, _VMI),
270 '$clause_term_position'(ClauseRef, NPC, List),
271 debug(break, 'ClausePos = ~w', [List]),
272 range(List, TermPos, A, Z),
273 debug(break, 'Range: ~d .. ~d', [A, Z]).
274
275
276 279
280:- multifile
281 prolog:message/3. 282
283prolog:message(breakpoint(no_source(ClauseRef, _File, Line))) -->
284 [ 'Failed to find line ~d in body of clause ~p. Breaking at start of body.'-
285 [Line, ClauseRef]
286 ].
287prolog:message(breakpoint(SetClear, Id)) -->
288 setclear(SetClear),
289 breakpoint(Id).
290
291setclear(set) -->
292 ['Breakpoint '].
293setclear(exist) -->
294 ['Existing breakpoint '].
295setclear(delete) -->
296 ['Deleted breakpoint '].
297
298breakpoint(Id) -->
299 breakpoint_name(Id),
300 ( { breakpoint_property(Id, file(File)),
301 breakpoint_property(Id, line_count(Line))
302 }
303 -> [ ' at ', url(File:Line) ]
304 ; []
305 ).
306
307breakpoint_name(Id) -->
308 { breakpoint_property(Id, clause(ClauseRef)) },
309 ( { clause_property(ClauseRef, erased) }
310 -> ['~w'-[Id]]
311 ; { clause_name(ClauseRef, Name) },
312 ['~w in ~w'-[Id, Name]]
313 )