34
35:- module(ontrace, [ontrace/3,
36 clause_pc_location/3,
37 cleanup_trace/1]). 38
39:- use_module(library(apply)). 40:- use_module(library(edinburgh)). 41:- use_module(library(lists)). 42:- use_module(library(option)). 43:- use_module(library(ntabling)). 44:- use_module(library(prolog_clause), []). 45:- use_module(library(prolog_codewalk), []). 46:- use_module(library(prolog_source)). 47:- use_module(library(clambda)). 48:- use_module(library(call_inoutex)). 49:- init_expansors. 50
51:- meta_predicate ontrace(0,6,:). 52
53ontrace(Goal, OnTrace, Options) :-
54 State=state(_, _, _), 55 call_inoutex(Goal,
56 setup_trace(State, OnTrace, Options),
57 cleanup_trace(State)).
58
59:- public true_1/1. 60true_1(_).
61
62is_meta(goal).
63is_meta(file).
64
65:- multifile
66 user:prolog_trace_interception/4. 67:- dynamic
68 user:prolog_trace_interception/4. 69
70:- thread_local
71 ontrace_enabled/4. 72
73user:prolog_trace_interception(Port, Frame, PC, Action) :-
74 ontrace_enabled(M, OnTrace, ValidGoal, ValidFile),
75 !,
76 trace_port(Port, Frame, PC, M:OnTrace, M:ValidGoal, M:ValidFile, Action).
77
80user:message_hook(trace_mode(on), _, _) :-
81 ontrace_enabled(_, _, _, _),
82 !,
83 fail.
87setup_trace(State, M:OnTrace, MOptL) :-
88 meta_options(is_meta, MOptL, OptL),
89 select_option(goal(ValidGoal), OptL, OptL1, ontrace:true_1),
90 select_option(file(ValidFile), OptL1, OptL2, ontrace:true_1),
91 92 select_option(ports(Ports), OptL2, _,
93 [+call, +exit, +fail, +unify, +exception]),
94 95 asserta(ontrace_enabled(M, OnTrace, ValidGoal, ValidFile), Ref),
96 once('$syspreds':map_bits(port_name, Ports, 0, Mask)),
97 '$visible'(Visible, Mask),
98 '$leash'(Leash, Mask),
99 nb_setarg(1, State, Visible),
100 nb_setarg(2, State, Leash),
101 nb_setarg(3, State, Ref),
102 trace.
106cleanup_trace(state(Visible, Leash, Ref)) :-
107 nodebug,
108 '$visible'(_, Visible),
109 '$leash'(_, Leash),
110 erase(Ref),
111 !.
112cleanup_trace(State) :-
113 print_message(error, format('Failed when saving tracer data', [State])),
114 fail.
115
116user_defined_module(M) :-
117 module_property(M, class(user)),
118 M \= ontrace.
119
120:- public trace_port/7. 121:- meta_predicate trace_port(+,+,+,5,1,1,-). 122
123trace_port(Port, Frame, PC, OnTrace, ValidGoal, ValidFile, Action) :-
124 prolog_frame_attribute(Frame, goal, M:H), 125 \+ \+ call(ValidGoal, M:H),
126 ignore(( Port = (exit),
127 prolog_frame_attribute(Frame, clause, ExCl),
128 129 check_and_call(exitcl, Frame, PC, OnTrace, ValidGoal, ValidFile,
130 _, [], Frame, ExCl, clause(ExCl))
131 )),
132 find_parents(Port, Frame, ParentL, RFrame, Cl, SubLoc),
133 check_and_call(Port, Frame, PC, OnTrace, ValidGoal, ValidFile, Action,
134 ParentL, RFrame, Cl, SubLoc),
135 !.
136trace_port(_, _, _, _, _, _, continue).
137
138check_and_call(Port, Frame, PC, OnTrace, ValidGoal, ValidFile, Action,
139 ParentL, RFrame, Cl, SubLoc) :-
140 prolog_frame_attribute(RFrame, goal, CM:CH),
141 ( ( clause_property(Cl, file(File))
142 ; module_property(CM, file(File))
143 )
144 -> \+ \+ call(ValidFile, File)
145 ; true
146 ),
147 \+ \+ call(ValidGoal, CM:CH),
148 \+ \+ ( member(F, [Frame|ParentL]),
149 prolog_frame_attribute(F, goal, PM:_),
150 user_defined_module(PM)
151 ),
152 call(OnTrace, Port, Frame, PC, ParentL, SubLoc, Action).
153
154find_parents(Port, Frame, ParentL, RFrame, Cl, Loc) :-
155 ( member(Port, [unify, redo(_)])
156 ->ParentL = [],
157 prolog_frame_attribute(Frame, clause, Cl),
158 RFrame = Frame,
159 Loc = clause(Cl)
160 ; find_parent_with_pc(Frame, PC, [], ParentL),
161 [Parent|_] = ParentL,
162 prolog_frame_attribute(Parent, clause, Cl),
163 RFrame = Parent,
164 Loc = clause_pc(Cl, PC)
165 ).
166
167find_parent_with_pc(Frame, PC, List1, List) :-
168 prolog_frame_attribute(Frame, parent, Parent),
169 ( prolog_frame_attribute(Frame, pc, PC)
170 ->List = [Parent|List1]
171 ; find_parent_with_pc(Parent, PC, [Parent|List1], List)
172 ).
173
174:- multifile
175 prolog:message_location//1. 176
177:- table
178 clause_pc_location/3. 179
180clause_pc_location(Clause, PC, Loc) :-
181 ( '$clause_term_position'(Clause, PC, List)
182 ->clause_subloc(Clause, List, Loc)
183 ; Loc = clause(Clause)
184 ).
185
186prolog:message_location(clause_pc(Clause, PC)) -->
187 {clause_pc_location(Clause, PC, Loc)},
188 '$messages':swi_location(Loc).
192clause_subloc(Cl, List, SubLoc) :-
193 ( clause_property(Cl, file(File)),
194 clause_property(Cl, line_count(Line)),
195 clause_property(Cl, module(Module))
196 ->file_line_module_subloc(Cl, List, File, Line, Module, SubLoc)
197 ; SubLoc = clause(Cl)
198 ).
199
200read_term_at_line(File, Line, Module, Clause, TermPos) :-
201 setup_call_cleanup(
202 ( '$push_input_context'(ontrace_info),
203 catch(open(File, read, In), _, fail),
204 set_stream(In, newline(detect))
205 ),
206 read_source_term_at_location(
207 In, Clause,
208 [ line(Line),
209 module(Module),
210 subterm_positions(TermPos)
211 ]),
212 ( close(In),
213 '$pop_input_context'
214 )).
215
216file_line_module_subloc(Cl, List, File, Line, Module, SubLoc) :-
217 ( read_term_at_line(File, Line, Module, Term, TermPos)
218 219 ->( prolog_clause:ci_expand(Term, ClauseL, Module, TermPos, CPosL),
220 match_clause(Cl, ClauseL, Module, CPosL, ClausePos, List2, List),
221 nonvar(ClausePos)
222 ->foldl(find_subgoal, List2, ClausePos, SubPos) 223 ; SubPos = TermPos
224 ),
225 SubLoc = file_term_position(File, SubPos)
226 ; SubLoc = file(File, Line, -1, _)
227 ).
228
229list_pos(term_position(_, _, _, _, PosL), PosL).
230list_pos(list_position(_, _, PosL, _), PosL).
231list_pos(parentheses_term_position(_, _, Pos1), Pos) :-
232 nonvar(Pos1),
233 list_pos(Pos1, Pos).
234list_pos(F-T, [F-T]).
235
236find_subgoal(A, TermPos, Pos) :-
237 list_pos(TermPos, PosL),
238 is_list(PosL),
239 nth1(A, PosL, Pos),
240 nonvar(Pos), !.
241find_subgoal(_, Pos, Pos).
242
243match_clause(Ref, ClauseL, Module, CPosL, CPos, List, Tail) :-
244 245 ( is_list(ClauseL)
246 ->clause(Head, Body, Ref),
247 nth1(Pos, ClauseL, Clause),
248 ( ( is_list(CPosL),
249 TermPosL = CPosL
250 ; CPosL = list_position(_,_, TermPosL, _),
251 is_list(TermPosL)
252 )
253 ->nth1(Pos, TermPosL, CPos)
254 ),
255 256 normalize_cl(Clause, Module, Module, NClause),
257 NClause =@= (Head :- Body)
258 ->List = [Pos|Tail]
259 ; List = Tail,
260 CPos = CPosL
261 ).
262
263normalize_cl(M:Clause, _, CM, NClause) :- !,
264 normalize_cl(Clause, M, CM, NClause).
265normalize_cl((Head :- Body), M, CM, (MHead :- NBody)) :- !,
266 strip_mod(Head, M, MHead),
267 strip_mod(Body, CM, MBody),
268 ( MBody = M:Body
269 ->NBody = Body
270 ; NBody = MBody
271 ).
272normalize_cl(Head, M, CM, NClause) :-
273 normalize_cl((Head :- true), M, CM, NClause).
274
275strip_mod(M:Term, _, MTerm) :-
276 strip_mod(Term, M, MTerm).
277strip_mod(Term, M, M:Term)