35
36:- module(prolog_cover,
37 [ show_coverage/1, 38 show_coverage/2
39 ]). 40:- use_module(library(ordsets)). 41:- use_module(library(apply)). 42
43:- set_prolog_flag(generate_debug_info, false).
74:- dynamic
75 entered/1, 76 exited/1. 77
78:- meta_predicate
79 show_coverage(0),
80 show_coverage(0,+).
89show_coverage(Goal) :-
90 show_coverage(Goal, []).
91show_coverage(Goal, Modules):-
92 setup_call_cleanup(
93 setup_trace(State),
94 once(Goal),
95 cleanup_trace(State, Modules)).
96
97setup_trace(state(Visible, Leash, Ref)) :-
98 set_prolog_flag(coverage_analysis, true),
99 asserta((user:prolog_trace_interception(Port, Frame, _, continue) :-
100 prolog_cover:assert_cover(Port, Frame)), Ref),
101 port_mask([unify,exit], Mask),
102 '$visible'(Visible, Mask),
103 '$leash'(Leash, Mask),
104 trace.
105
106port_mask([], 0).
107port_mask([H|T], Mask) :-
108 port_mask(T, M0),
109 '$syspreds':port_name(H, Bit),
110 Mask is M0 \/ Bit.
111
112cleanup_trace(state(Visible, Leash, Ref), Modules) :-
113 nodebug,
114 '$visible'(_, Visible),
115 '$leash'(_, Leash),
116 erase(Ref),
117 set_prolog_flag(coverage_analysis, false),
118 covered(Succeeded, Failed),
119 file_coverage(Succeeded, Failed, Modules).
128assert_cover(unify, Frame) :-
129 running_static_pred(Frame),
130 prolog_frame_attribute(Frame, clause, Cl),
131 !,
132 assert_entered(Cl).
133assert_cover(exit, Frame) :-
134 running_static_pred(Frame),
135 prolog_frame_attribute(Frame, clause, Cl),
136 !,
137 assert_exited(Cl).
138assert_cover(_, _).
144running_static_pred(Frame) :-
145 prolog_frame_attribute(Frame, goal, Goal),
146 \+ predicate_property(Goal, dynamic).
153assert_entered(Cl) :-
154 entered(Cl),
155 !.
156assert_entered(Cl) :-
157 assert(entered(Cl)).
158
159assert_exited(Cl) :-
160 exited(Cl),
161 !.
162assert_exited(Cl) :-
163 assert(exited(Cl)).
169covered(Succeeded, Failed) :-
170 findall(Cl, (entered(Cl), \+exited(Cl)), Failed0),
171 findall(Cl, retract(exited(Cl)), Succeeded0),
172 retractall(entered(Cl)),
173 sort(Failed0, Failed),
174 sort(Succeeded0, Succeeded).
175
176
177
187file_coverage(Succeeded, Failed, Modules) :-
188 format('~N~n~`=t~78|~n'),
189 format('~tCoverage by File~t~78|~n'),
190 format('~`=t~78|~n'),
191 format('~w~t~w~64|~t~w~72|~t~w~78|~n',
192 ['File', 'Clauses', '%Cov', '%Fail']),
193 format('~`=t~78|~n'),
194 forall(source_file(File),
195 file_coverage(File, Succeeded, Failed, Modules)),
196 format('~`=t~78|~n').
197
198file_coverage(File, Succeeded, Failed, Modules) :-
199 findall(Cl, clause_source(Cl, File, _), Clauses),
200 sort(Clauses, All),
201 ( ord_intersect(All, Succeeded)
202 -> true
203 ; ord_intersect(All, Failed)
204 ),
205 !,
206 ord_intersection(All, Failed, FailedInFile),
207 ord_intersection(All, Succeeded, SucceededInFile),
208 ord_subtract(All, SucceededInFile, UnCov1),
209 ord_subtract(UnCov1, FailedInFile, Uncovered),
210
211 212 213 exclude(is_pldoc, All, All_wo_pldoc),
214 exclude(is_pldoc, Uncovered, Uncovered_wo_pldoc),
215 exclude(is_pldoc, FailedInFile, Failed_wo_pldoc),
216
217 218 exclude(is_system_clause, All_wo_pldoc, All_wo_system),
219 exclude(is_system_clause, Uncovered_wo_pldoc, Uncovered_wo_system),
220 exclude(is_system_clause, Failed_wo_pldoc, Failed_wo_system),
221
222 length(All_wo_system, AC),
223 length(Uncovered_wo_system, UC),
224 length(Failed_wo_system, FC),
225
226 CP is 100-100*UC/AC,
227 FCP is 100*FC/AC,
228 summary(File, 56, SFile),
229 format('~w~t ~D~64| ~t~1f~72| ~t~1f~78|~n', [SFile, AC, CP, FCP]),
230 detailed_report(Uncovered_wo_system, Modules).
231file_coverage(_,_,_,_).
232
233
234is_system_clause(Clause) :-
235 clause_name(Clause, Name),
236 Name = system:_.
237
238is_pldoc(Clause) :-
239 clause_name(Clause, _Module:Name2/_Arity),
240 pldoc_predicate(Name2).
241
242pldoc_predicate('$pldoc').
243pldoc_predicate('$mode').
244pldoc_predicate('$pred_option').
245
246summary(Atom, MaxLen, Summary) :-
247 atom_length(Atom, Len),
248 ( Len < MaxLen
249 -> Summary = Atom
250 ; SLen is MaxLen - 5,
251 sub_atom(Atom, _, SLen, 0, End),
252 atom_concat('...', End, Summary)
253 ).
259clause_source(Clause, File, Line) :-
260 nonvar(Clause),
261 !,
262 clause_property(Clause, file(File)),
263 clause_property(Clause, line_count(Line)).
264clause_source(Clause, File, Line) :-
265 Pred = _:_,
266 source_file(Pred, File),
267 \+ predicate_property(Pred, multifile),
268 nth_clause(Pred, _Index, Clause),
269 clause_property(Clause, line_count(Line)).
270clause_source(Clause, File, Line) :-
271 Pred = _:_,
272 predicate_property(Pred, multifile),
273 nth_clause(Pred, _Index, Clause),
274 clause_property(Clause, file(File)),
275 clause_property(Clause, line_count(Line)).
279detailed_report(Uncovered, Modules):-
280 maplist(clause_line_pair, Uncovered, Pairs),
281 include(pair_in_modules(Modules), Pairs, Pairs_in_modules),
282 ( Pairs_in_modules \== []
283 -> sort(Pairs_in_modules, Pairs_sorted),
284 group_pairs_by_key(Pairs_sorted, Compact_pairs),
285 nl,
286 format('~2|Clauses not covered from modules ~p~n', [Modules]),
287 format('~4|Predicate ~59|Clauses at lines ~n', []),
288 maplist(print_clause_line, Compact_pairs),
289 nl
290 ; true
291 ).
292
293pair_in_modules(Modules,(Module:_Name)-_Line):-
294 memberchk(Module, Modules).
295
296clause_line_pair(Clause, Name-Line):-
297 clause_property(Clause, line_count(Line)),
298 clause_name(Clause, Name).
299
300clause_name(Clause,Name):-
301 clause(Module:Head, _, Clause),
302 functor(Head,F,A),
303 Name=Module:F/A.
304
305print_clause_line((Module:Name/Arity)-Lines):-
306 term_to_atom(Module:Name, Complete_name),
307 summary(Complete_name, 54, SName),
308 format('~4|~w~t~59|~p~n', [SName/Arity, Lines])
Clause cover analysis
The purpose of this module is to find which part of the program has been used by a certain goal. Usage is defined in terms of clauses that have fired, seperated in clauses that succeeded at least once and clauses that failed on each occasion.
This module relies on the SWI-Prolog tracer hooks. It modifies these hooks and collects the results, after which it restores the debugging environment. This has some limitations:
The result is represented as a list of clause-references. As the references to clauses of dynamic predicates cannot be guaranteed, these are omitted from the result.