1 | % (c) 2009-2019 Lehrstuhl fuer Softwaretechnik und Programmiersprachen, | |
2 | % Heinrich Heine Universitaet Duesseldorf | |
3 | % This software is licenced under EPL 1.0 (http://www.eclipse.org/org/documents/epl-v10.html) | |
4 | ||
5 | :- module(coverage_statistics,[tcltk_compute_coverage/1, tcltk_compute_coverage_and_enabling/1, | |
6 | tcltk_compute_coverage_and_degree/1, | |
7 | tcltk_compute_min_max/1,pretty_print_min_max_coverage_to_file/1, | |
8 | compute_the_coverage/5,pretty_print_coverage_information_to_file/1, | |
9 | operation_hit/2,query_node_hit/2, uncovered_operation/1]). | |
10 | ||
11 | :- use_module(state_space). | |
12 | :- use_module(tools). | |
13 | ||
14 | :- use_module(module_information). | |
15 | :- module_info(group,animator). | |
16 | :- module_info(description,'This module is responsible for computing state space coverage info.'). | |
17 | ||
18 | /* --------------------------------------------------- */ | |
19 | :- dynamic var_min_max/3. | |
20 | ||
21 | tcltk_compute_min_max(_) :- | |
22 | retractall(var_min_max(_,_,_)), | |
23 | visited_expression(_ID,State), %print(treat(_ID)),nl, | |
24 | treat_state(State),fail. | |
25 | tcltk_compute_min_max(list(Res)) :- | |
26 | findall(Info, get_info(Info,constant,true), CRes1), | |
27 | findall(Info, get_info(Info,constant,false), CRes2), | |
28 | findall(Info, get_info(Info,variable,true), VRes1), | |
29 | findall(Info, get_info(Info,variable,false), VRes2), | |
30 | combine_results(CRes1,CRes2,CRes), | |
31 | combine_results(VRes1,VRes2,VRes), | |
32 | (CRes=[] -> Res = ['VARIABLES'|VRes] ; | |
33 | append(['CONSTANTS'|CRes],['VARIABLES'|VRes],Res)). | |
34 | ||
35 | combine_results([],[],R) :- !, R=[]. | |
36 | combine_results(Eq,[],R) :- R = ['/* all only one value: */'|Eq]. | |
37 | combine_results([],NotEq,R) :- R = ['/* all more than one value: */'|NotEq]. | |
38 | combine_results(Eq,NotEq,R) :- | |
39 | append(['/* with one value: */'|Eq],['/* with more than one value: */'|NotEq],R). | |
40 | ||
41 | :- use_module(tools_files,[write_lines_to_file/2]). | |
42 | pretty_print_min_max_coverage_to_file(Filename) :- | |
43 | tcltk_compute_min_max(list(Res)), | |
44 | write_lines_to_file(Filename,Res). | |
45 | ||
46 | :- use_module(b_machine_hierarchy,[abstract_constant/2,concrete_constant/2]). | |
47 | ||
48 | get_info(Info,Type,Equal) :- var_min_max(Var,Min,Max), | |
49 | (Min==Max -> Equal=true ; Equal=false), | |
50 | ((abstract_constant(Var,_) ; concrete_constant(Var,_)) | |
51 | -> Type = constant ; Type = variable), | |
52 | translate_min_max(Var,Min,Max,Info). | |
53 | ||
54 | translate_min_max(Var,M,M,Info) :- !, translate:translate_bvalue(M,TMin), | |
55 | ajoin([Var,'=',TMin],Info). | |
56 | translate_min_max(Var,pred_false,pred_true,Info) :- !, | |
57 | ajoin([Var,':BOOL'],Info). | |
58 | translate_min_max(Var,fd(X,T),fd(Y,T),Info) :- !, | |
59 | gen_fd_set(X,Y,T,Set), translate:translate_bvalue(Set,TS), | |
60 | ajoin([Var,':',TS],Info). | |
61 | translate_min_max(Var,Min,Max,Info) :- | |
62 | translate:translate_bvalue(Min,TMin), | |
63 | translate:translate_bvalue(Max,TMax), | |
64 | ajoin([Var,':',TMin,'..',TMax],Info). | |
65 | ||
66 | gen_fd_set(X,Y,_T,R) :- X>Y,!, R=[]. | |
67 | gen_fd_set(X,Y,T,[fd(X,T)|R]) :- X1 is X+1, | |
68 | gen_fd_set(X1,Y,T,R). | |
69 | ||
70 | treat_state(const_and_vars(_,S)) :- !, treat_state(S). | |
71 | treat_state(concrete_constants(S)) :- !, treat_state(S). | |
72 | treat_state(csp_and_b(_,S)) :- !, treat_state(S). | |
73 | treat_state(List) :- member(bind(VAR,VAL),List), treat_var(VAR,VAL),fail. | |
74 | ||
75 | treat_var(Variable,Value) :- retract(var_min_max(Variable,Min,Max)),!, | |
76 | (is_smaller(Value,Min) -> NewMin = Value ; NewMin=Min), | |
77 | (is_smaller(Max,Value) -> NewMax = Value ; NewMax=Max), | |
78 | assert(var_min_max(Variable,NewMin,NewMax)). | |
79 | treat_var(Variable,Value) :- | |
80 | assert(var_min_max(Variable,Value,Value)). | |
81 | ||
82 | :- use_module(library(avl),[avl_size/2, avl_max/2]). | |
83 | is_smaller(int(X),int(Y)) :- !, X < Y. | |
84 | is_smaller(string(X),string(Y)) :- !, X @< Y. | |
85 | is_smaller(pred_true,_) :- !,fail. | |
86 | is_smaller(pred_false,_) :- !. | |
87 | is_smaller(fd(X,T),fd(Y,T)) :- !, X < Y. | |
88 | is_smaller((X1,X2),(Y1,Y2)) :- !, (is_smaller(X1,Y1) -> true ; X1=Y1, is_smaller(X2,Y2)). | |
89 | is_smaller([],Y) :- !, Y \== []. | |
90 | is_smaller(_,[]) :- !, fail. | |
91 | is_smaller(avl_set(X),avl_set(Y)) :- avl_size(X,SX), avl_size(Y,SY),!, | |
92 | (SX<SY -> true | |
93 | ; SX=SY, avl_max(X,MaxX), avl_max(Y,MaxY), | |
94 | (is_smaller(MaxX,MaxY) % used to be X@<Y | |
95 | -> true | |
96 | ; MaxX=MaxY -> X @< Y) % if same maximum; look at all values; TO DO: maybe look at avl_min first | |
97 | ). | |
98 | is_smaller(X,Y) :- X @< Y. | |
99 | ||
100 | /* --------------------------------------------------- */ | |
101 | ||
102 | pretty_print_coverage_information_to_file(FileName) :- | |
103 | tcltk_compute_coverage(list(Res)), | |
104 | write_lines_to_file(FileName,Res). | |
105 | ||
106 | tcltk_compute_coverage(list(Res)) :- | |
107 | compute_the_coverage(Res,_,_,false,false). | |
108 | tcltk_compute_coverage_and_enabling(list(Res)) :- | |
109 | compute_the_coverage(Res,_,_,true,false). | |
110 | tcltk_compute_coverage_and_degree(list(Res)) :- | |
111 | compute_the_coverage(Res,_,_,false,true). | |
112 | ||
113 | compute_the_coverage(Res,TotalNodeNr,TotalTransSum,ShowEnabledInfo,ShowDegreeInfo) :- | |
114 | retractall(operation_hit(_,_,_)), | |
115 | reset_node_hit(ShowEnabledInfo,ShowDegreeInfo), | |
116 | compute_coverage(ShowEnabledInfo,ShowDegreeInfo), | |
117 | findall(S2, | |
118 | (operation_hit(OpS,Nr), | |
119 | %translate:translate_bexpression(Op,OpS), | |
120 | string_concatenate(':',Nr,S1), | |
121 | string_concatenate(OpS,S1,S2)),Res1_), | |
122 | sort(Res1_,Res1), | |
123 | findall(S2, | |
124 | (query_node_hit(Prop,Nr), | |
125 | string_concatenate(':',Nr,S1), | |
126 | string_concatenate(Prop,S1,S2)),Res0), | |
127 | findall(OpName, uncovered_operation(OpName),Res2_), | |
128 | sort(Res2_,Res2), | |
129 | total_number_of_transitions(TotalTransSum), | |
130 | (ShowEnabledInfo=false | |
131 | -> ResDE = [] | |
132 | ; | |
133 | findall(DE, def_enables(DE),EnRes1), | |
134 | findall(DE, pos_enables(DE),EnRes2), | |
135 | append(['DEFINITELY_ENABLED_AFTER'|EnRes1],['POSSIBLY_ENABLES'|EnRes2],ResDE) | |
136 | ), | |
137 | (ShowDegreeInfo=false | |
138 | -> Res00 = ResDE | |
139 | ; | |
140 | findall(DegR, (max_degree(OpDeg,MaxDeg), tools:ajoin([OpDeg,'->',MaxDeg],DegR)), DegRes), | |
141 | append(['MAXIMUM_DEGREE'|DegRes],ResDE,Res00) | |
142 | ), | |
143 | get_op_string('UNCOVERED_',UNCOVEREDSTR), | |
144 | get_op_string('TOTAL_',TOTALSTR), get_op_string('COVERED_',COVEREDSTR), | |
145 | append([UNCOVEREDSTR|Res2],Res00,Res01), | |
146 | append([TOTALSTR,TotalTransSum,COVEREDSTR|Res1], | |
147 | Res01,OpRes), | |
148 | append(['STATES'|Res0],OpRes,Res), | |
149 | query_node_hit(total,TotalNodeNr). | |
150 | %% ,pge_algo: disabled_operations_stats(ResPGE), % in case no disabling stats are given an empty list is returned | |
151 | %% append(Res_,ResPGE,Res). | |
152 | ||
153 | :- use_module(specfile,[get_specification_description/2]). | |
154 | get_op_string(PREFIX,Res) :- | |
155 | get_specification_description(operations,OP), | |
156 | string_concatenate(PREFIX,OP,Res). | |
157 | ||
158 | ||
159 | def_enables(X) :- operation_hit(OpName1,_,_), | |
160 | definitely_enabled_after(OpName1,OpName2), | |
161 | string_concatenate('====>',OpName2,R1), | |
162 | string_concatenate(OpName1,R1,X). | |
163 | pos_enables(X) :- operation_hit(OpName1,_,_), | |
164 | possibly_enables(OpName1,OpName2), | |
165 | % \+ definitely_enabled_after(OpName1,OpName2), | |
166 | string_concatenate('=?+=>',OpName2,R1), | |
167 | string_concatenate(OpName1,R1,X). | |
168 | ||
169 | /* note: assumes that same operation name cannot appear with multiple arity */ | |
170 | ||
171 | total_number_of_transitions(TotalTransSum) :- | |
172 | findall(Nr,operation_hit(_,_,Nr),List), | |
173 | sum_list(List,0,TotalTransSum). | |
174 | ||
175 | sum_list([],N,N). | |
176 | sum_list([H|T],SoFar,Res) :- S1 is SoFar+H, sum_list(T,S1,Res). | |
177 | ||
178 | operation_hit(OpTerm,Nr) :- | |
179 | operation_hit(OpTerm,_ID,Nr). | |
180 | ||
181 | :- dynamic operation_hit/3. | |
182 | operation_hit(operation(_),node__2,55). | |
183 | ||
184 | :- use_module(specfile,[get_possible_event/1]). | |
185 | ||
186 | uncovered_operation(OpName) :- specfile:get_possible_event(OpName), | |
187 | \+(operation_hit(OpName,_,_)). | |
188 | ||
189 | add_hit(OpName,ID) :- | |
190 | ((OpName = partition(N,_Op,P)) -> LookupName = partition(N,_,P) ; LookupName = OpName), | |
191 | (retract(operation_hit(LookupName,NID,Nr)) | |
192 | -> (N1 is Nr+1, assert(operation_hit(LookupName,NID,N1))) | |
193 | ; (assert(operation_hit(OpName,ID,1))) | |
194 | ). | |
195 | ||
196 | query_node_hit(Prop,Nr) :- node_hit(Prop,Nr). | |
197 | query_node_hit(total,Nr) :- node_hit(open,N1), node_hit(live,N2), node_hit(deadlocked,N3), | |
198 | Nr is N1+N2+N3. | |
199 | :- dynamic node_hit/2. | |
200 | node_hit(property,22). | |
201 | add_node_hit(OpName) :- | |
202 | (retract(node_hit(OpName,Nr)) | |
203 | -> N1 is Nr+1, assert(node_hit(OpName,N1)) | |
204 | ; assert(node_hit(OpName,1)) | |
205 | ). | |
206 | ||
207 | reset_node_hit(ShowEnabledInfo,ShowDegreeInfo) :- | |
208 | retractall(node_hit(_,_)), | |
209 | assert(node_hit(deadlocked,0)), | |
210 | (specfile:b_or_z_mode -> assert(node_hit(invariant_violated,0)), | |
211 | assert(node_hit(invariant_not_checked,0)) | |
212 | ; true), | |
213 | assert(node_hit(open,0)), assert(node_hit(live,0)), | |
214 | init_enabling_degree_info(ShowEnabledInfo,ShowDegreeInfo). | |
215 | ||
216 | :- use_module(specfile,[get_operation_name/2]). | |
217 | ||
218 | compute_coverage(_,_) :- | |
219 | visited_expression_id(ID), | |
220 | (not_all_transitions_added(ID) | |
221 | -> add_node_hit(open) | |
222 | ; (transition(ID,_,_) -> add_node_hit(live) | |
223 | ; user:is_not_interesting(ID) -> add_node_hit(ignored) | |
224 | ; max_reached_or_timeout_for_node(ID) -> true % e.g., if MAX_OPERATIONS set to 0 | |
225 | ; otherwise -> add_node_hit(deadlocked)) | |
226 | ), | |
227 | (max_reached_or_timeout_for_node(ID) | |
228 | -> add_node_hit(explored_but_not_all_transitions_computed) | |
229 | ; true | |
230 | ), | |
231 | (invariant_violated(ID) -> add_node_hit(invariant_violated) ; true), | |
232 | (time_out_for_invariant(ID) -> add_node_hit(invariant_time_out) ; true), | |
233 | (time_out_for_assertions(ID) -> add_node_hit(assertions_time_out) ; true), | |
234 | (invariant_not_yet_checked(ID) -> add_node_hit(invariant_not_checked) ; true), | |
235 | (state_error(ID,_,Err),Err\=invariant_violated -> | |
236 | (Err=abort_error(TYPE,_,_,_) | |
237 | -> add_node_hit(TYPE) | |
238 | ; add_node_hit(state_error)) | |
239 | ; true), | |
240 | fail. | |
241 | compute_coverage(ShowEnabledInfo,ShowDegreeInfo) :- | |
242 | transition(ID,Operation,NID), | |
243 | nonvar(Operation), | |
244 | get_operation_name(Operation,OpName), | |
245 | add_hit(OpName,ID), | |
246 | (ShowEnabledInfo=true -> update_enabling_info(ID,OpName,NID) ; true), | |
247 | (ShowDegreeInfo=true -> update_degree_info(ID,OpName) ; true), | |
248 | fail. | |
249 | %compute_coverage :- print_message('Covered Properties: '), | |
250 | % node_hit(OpName,Nr), | |
251 | % print(OpName), print(' nodes:'), print(Nr),nl, | |
252 | % fail. | |
253 | %compute_coverage :- print_message('Covered Operations: '), | |
254 | % operation_hit(OpName,_ID,Nr), | |
255 | % print(OpName), print(' hits:'), print(Nr),nl, | |
256 | % fail. | |
257 | %compute_coverage :- nl, print_message('Uncovered Operations: '), | |
258 | % uncovered_operation(OpName), | |
259 | % print(OpName), print(' '), fail. | |
260 | compute_coverage(_ShowEnabledInfo,ShowDegreeInfo) :- | |
261 | (ShowDegreeInfo=true -> finalise_degree_info ; true), | |
262 | nl. | |
263 | ||
264 | ||
265 | ||
266 | ||
267 | :- dynamic definitely_enabled_after/2, possibly_enables/2. /* true if after an operation another is always enabled */ | |
268 | :- dynamic degree_in_id/3, max_degree/2. | |
269 | ||
270 | :- use_module(specfile,[b_mode/0]). | |
271 | :- use_module(bmachine,[b_is_operation_name/1,b_get_machine_operation/4]). | |
272 | init_enabling_degree_info(ShowEnabledInfo,_ShowDegreeInfo) :- | |
273 | retractall(definitely_enabled_after(_,_)), | |
274 | retractall(possibly_enables(_,_)), | |
275 | retractall(degree_in_id(_,_,_)), | |
276 | retractall(max_degree(_,_)), | |
277 | b_mode, | |
278 | ShowEnabledInfo=true, | |
279 | (b_is_operation_name(OpName1) ; OpName1 = '$initialise_machine'), | |
280 | b_is_operation_name(OpName2), | |
281 | assert(definitely_enabled_after(OpName1,OpName2)), | |
282 | fail. | |
283 | init_enabling_degree_info(_,_). | |
284 | ||
285 | finalise_degree_info :- update_last_degree. | |
286 | ||
287 | update_enabling_info(_ID,OpName,NewID) :- %print(upd(_ID,OpName)),nl, | |
288 | definitely_enabled_after(OpName,OpName2), | |
289 | (opname_possible(NewID,OpName2) | |
290 | -> true | |
291 | ; retract(definitely_enabled_after(OpName,OpName2)) | |
292 | %(retract(definitely_enabled_after(OpName,OpName2)) -> print(retract(OpName,OpName2))) | |
293 | ),fail. | |
294 | update_enabling_info(ID,OpName,NewID) :- %print(upd(_ID,OpName)),nl, | |
295 | opname_possible(NewID,OpName2), % Note: can succeed multiple times with same OpName2 ! | |
296 | \+ possibly_enables(OpName,OpName2), | |
297 | \+ opname_possible(ID,OpName2), | |
298 | assert(possibly_enables(OpName,OpName2)), | |
299 | fail. | |
300 | update_enabling_info(_,_,_). | |
301 | ||
302 | % NOTE: we suppose that the transitions are grouped by operation name for each state id; may not be true for CSP ?? | |
303 | update_degree_info(ID,OpName) :- | |
304 | get_cur_degree(ID,OpName,Degree), D1 is Degree+1, | |
305 | assert(degree_in_id(ID,OpName,D1)), | |
306 | fail. | |
307 | update_degree_info(_,_). | |
308 | ||
309 | update_last_degree :- (retract(degree_in_id(_,OpName,D)) -> update_degree(OpName,D) ; true). | |
310 | ||
311 | get_cur_degree(ID,OpName,Degree) :- | |
312 | (retract(degree_in_id(ID,OpName,D)) -> Degree=D | |
313 | ; update_last_degree, | |
314 | Degree=0). | |
315 | update_degree(OpName,Degree) :- | |
316 | ((max_degree(OpName,Max), Max>=Degree) -> true | |
317 | ; retractall(max_degree(OpName,_)), format('Update degree ~w -> ~w~n',[OpName,Degree]), | |
318 | assert(max_degree(OpName,Degree)) | |
319 | ). | |
320 | opname_possible(ID,OpName) :- transition(ID,Op,_),get_operation_name(Op,OpName). | |
321 | ||
322 | % ------------------------------------- | |
323 | ||
324 |