34
35:- module(gcover,
36 [ gcover/2,
37 covered_db/6,
38 reset_cover/0,
39 reset_cover/1,
40 loc_file_line/4
41 ]). 42
43:- use_module(library(filepos_line)). 44:- use_module(library(module_files)). 45:- use_module(library(ontrace)). 46:- use_module(library(option)). 47:- use_module(library(prolog_source)). 48:- use_module(library(ntabling)). 49
50:- table loc_file_line/4. 51
52:- public not_dynamic/1. 53
54:- meta_predicate not_dynamic(0). 55
56not_dynamic(Call) :-
57 \+ predicate_property(Call, dynamic).
58
59:- meta_predicate gcover(0,+). 60
61gcover(Goal, OptL1) :-
62 select_option(tag(Tag), OptL1, OptL, user),
63 ontrace(Goal, gcover_port(Tag), [goal(not_dynamic)|OptL]).
64
65:- dynamic covered_db/6. 66
67gcover_port(Tag, Port, _Frame, _PC, _ParentL, Loc, continue) :-
68 record_cover(Loc, Port, Tag).
69
70file_line_end(Module, File, L1, L2) :-
71 setup_call_cleanup(
72 '$push_input_context'(file_line_end),
73 file_line_end_2(Module, File, L1, L2),
74 '$pop_input_context').
75
76file_line_end_2(Module, File, L1, L2) :-
77 catch(open(File, read, In), _, fail),
78 set_stream(In, newline(detect)),
79 call_cleanup(
80 ( read_source_term_at_location(
81 In, _,
82 [ line(L1),
83 module(Module)
84 ]),
85 stream_property(In, position(Pos)),
86 stream_position_data(line_count, Pos, L2)
87 ),
88 close(In)).
89
90loc_file_line(clause_term_position(ClauseRef, TermPos), File, L1, L2) :-
91 clause_property(ClauseRef, file(File)),
92 file_termpos_line2(File, TermPos, L1, L2).
93loc_file_line(clause(ClauseRef), File, L1, L2) :-
94 clause_property(ClauseRef, file(File)),
95 clause_property(ClauseRef, line_count(L1)),
96 clause_property(ClauseRef, module(Module)),
97 file_line_end(Module, File, L1, L2).
98loc_file_line(file_term_position(File, TermPos), File, L1, L2) :-
99 file_termpos_line2(File, TermPos, L1, L2).
100loc_file_line(file(File, L1, _, _), File, L1, L2) :-
101 once(module_file(Module, File)),
102 file_line_end(Module, File, L1, L2).
103loc_file_line(clause_pc(Clause, PC), File, L1, L2) :-
104 clause_pc_location(Clause, PC, Loc),
105 loc_file_line(Loc, File, L1, L2).
106
107file_termpos_line2(File, TermPos, Line1, Line2) :-
108 ( compound(TermPos),
109 arg(1, TermPos, C1),
110 integer(C1),
111 arg(2, TermPos, C2),
112 integer(C2)
113 ->filepos_line(File, C1, Line1, _),
114 filepos_line(File, C2, Line2, _)
115 ; true
116 ).
117
118record_cover(Loc, Port, Tag) :-
119 loc_file_line(Loc, File, Line1, Line2),
120 port_record_cover(Port, File, Line1, Line2, Tag).
121
122port_record_cover(exitcl, File, Line1, Line2, Tag) :- !,
123 decr_record_cover(failure, OutPort, File, Line1, Line2, Tag),
124 incr_record_cover(OutPort, File, Line1, Line2, Tag).
125port_record_cover(unify, File, Line1, Line2, Tag) :- !,
126 incr_record_cover(failure, File, Line1, Line2, Tag).
128port_record_cover(redo(0), File, Line1, Line2, Tag) :- !,
129 incr_record_cover(redo, File, Line1, Line2, Tag).
130port_record_cover(redo(_), File, Line1, Line2, Tag) :- !,
131 incr_record_cover(redoi, File, Line1, Line2, Tag).
132
133port_record_cover(Port, File, Line1, Line2, Tag) :-
134 incr_record_cover(Port, File, Line1, Line2, Tag).
135
136incr_record_cover(Port, File, Line1, Line2, Tag) :-
137 ( retract(covered_db(File, Line1, Line2, Port, Tag, Count1))
138 ->succ(Count1, Count)
139 ; Count=1
140 ),
141 assertz(covered_db(File, Line1, Line2, Port, Tag, Count)).
142
143decr_record_cover(Port, OutPort, File, Line1, Line2, Tag) :-
144 ( retract(covered_db(File, Line1, Line2, Port, Tag, Count1))
145 ->succ(Count, Count1),
146 ( Count =:= 0
147 ->true
148 ; assertz(covered_db(File, Line1, Line2, Port, Tag, Count))
149 ),
150 OutPort = (success)
151 ; OutPort = multi
152 ).
153
154reset_cover :- reset_cover(_).
155
156reset_cover(Tag) :-
157 retractall(covered_db(_, _, _, _, Tag, _))