1 | % (c) 2009-2024 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, | |
8 | pretty_print_min_max_coverage_to_file/1, | |
9 | compute_the_coverage/5,pretty_print_coverage_information_to_file/1, | |
10 | operation_hit/2,query_node_hit/2, uncovered_operation/1, | |
11 | tcltk_analyse_constants/2, tcltk_analyse_constants/1]). | |
12 | ||
13 | ||
14 | :- use_module(probsrc(module_information)). | |
15 | :- module_info(group,misc). | |
16 | :- module_info(description,'This module is responsible for computing state space coverage info.'). | |
17 | ||
18 | :- use_module(probsrc(state_space)). | |
19 | :- use_module(probsrc(tools)). | |
20 | :- use_module(probsrc(debug),[debug_println/2, debug_format/3]). | |
21 | :- use_module(probsrc(error_manager),[add_message/4, add_message/3, add_debug_message/4, add_debug_message/3]). | |
22 | :- use_module(probsrc(tools_lists), [length_less/2]). | |
23 | /* --------------------------------------------------- */ | |
24 | ||
25 | :- use_module(probsrc(state_space),[current_state_id/1, | |
26 | get_constants_state_for_id/2, try_get_unique_constants_state/1]). | |
27 | ||
28 | % provide information about the constants | |
29 | % can be run in procli with -csv constants_analysis | |
30 | tcltk_analyse_constants(Table) :- | |
31 | current_state_id(CurID), | |
32 | tcltk_analyse_constants(CurID,Table). | |
33 | ||
34 | :- dynamic constant_can_be_expanded/1. | |
35 | ||
36 | :- use_module(library(samsort),[samsort/3]). | |
37 | % called constants_analysis in meta_interface and probcli | |
38 | tcltk_analyse_constants(ID,Table) :- | |
39 | (get_constants_state_for_id(ID,Constants) -> true | |
40 | ; ID=root, try_get_unique_constants_state(Constants)), | |
41 | !, | |
42 | retractall(constant_can_be_expanded(_)), | |
43 | findall(H,get_value_info(H,'$DUMMY$',int(0),_),Header), | |
44 | reorder_cols(Header,RHeader), | |
45 | findall(list([CstID|RInfos]), (member(bind(CstID,CstVal),Constants), | |
46 | findall(Info,get_value_info(_,CstID,CstVal,Info),Infos), | |
47 | reorder_cols(Infos,RInfos), | |
48 | debug_format(19,' ~w : ~w~n',[CstID,RInfos]) | |
49 | ), Entries), | |
50 | samsort(samorder,Entries,SEntries), | |
51 | findall(Cst,constant_can_be_expanded(Cst),ExpList), | |
52 | (ExpList=[] -> true | |
53 | ; format(user_output,'Constants that are currently symbolic but could be expanded:~n ~w~n',[ExpList])), | |
54 | Table = list([list(['CONSTANT'|RHeader]) | SEntries]). | |
55 | tcltk_analyse_constants(_,Table) :- | |
56 | Table = list([list(['SETUP CONSTANTS first'])]). | |
57 | ||
58 | % move message column to end | |
59 | reorder_cols([Class,MessageCol|OtherCols],ResCols) :- append([Class|OtherCols],[MessageCol],ResCols). | |
60 | ||
61 | samorder(list([ID,_,_,TS|_]),list([ID2,_,_,TS2|_])) :- !,(TS,ID) @>= (TS2,ID2). | |
62 | samorder(A,B) :- print(unknown_samorder(A,B)),nl. | |
63 | ||
64 | :- use_module(library(terms), [term_size/2]). | |
65 | :- use_module(probsrc(translate), [translate_bvalue_with_limit/3]). | |
66 | get_value_info(class,ID,_,Class) :- get_id_class(ID,Class). | |
67 | get_value_info(INFO,ID,_,Kind) :- | |
68 | is_registered_function_name(ID,_), | |
69 | get_registered_function(_MemoID,ID,RegValue),!, % use stored Memo value instead | |
70 | get_value_info2(INFO,ID,RegValue,Kind). | |
71 | get_value_info(INFO,ID,Value,Kind) :- | |
72 | get_value_info2(INFO,ID,Value,Kind). | |
73 | ||
74 | :- use_module(probsrc(debug),[debug_mode/1]). | |
75 | get_value_info2(Info, ID, Value, Res) :- | |
76 | (var(Info) -> true ; Info=message -> true ; Info=kind), | |
77 | get_value_kind(Value,ID,Kind,Msg), | |
78 | (Info=message,Res=Msg ; Info=kind, Res=Kind). | |
79 | get_value_info2(termsize, _, Value, TS) :- term_size(Value,TS). | |
80 | get_value_info2(value, _, Value, TS) :- | |
81 | (debug_mode(on) -> Limit=1000 ; Limit=100), | |
82 | translate_bvalue_with_limit(Value,Limit,TS). | |
83 | ||
84 | % TO DO: use is_concrete_constants_state_id to check if other value exists | |
85 | % TO DO: check if abstract_constants can be converted back | |
86 | ||
87 | :- use_module(probsrc(bmachine),[b_is_variable/1,b_is_constant/1]). | |
88 | :- use_module(probsrc(b_machine_hierarchy),[abstract_constant/2]). | |
89 | :- use_module(probsrc(memoization),[is_registered_function_name/2, get_registered_function/3]). | |
90 | :- use_module(probsrc(kernel_objects),[cardinality_as_int/2]). | |
91 | ||
92 | get_id_class(ID,Res) :- is_registered_function_name(ID,Expanded),!, | |
93 | (Expanded=true -> Res = 'MEMOIZED (expanded)' ; Res='MEMOIZED'). % probably abstract | |
94 | get_id_class(ID,Res) :- abstract_constant(ID,_),!, Res='ABSTRACT'. | |
95 | get_id_class(ID,Res) :- b_is_constant(ID),!, Res='CONCRETE'. | |
96 | get_id_class(ID,Res) :- b_is_variable(ID),!, Res='VARIABLE'. | |
97 | get_id_class(_,'?'). | |
98 | ||
99 | ||
100 | :- use_module(probsrc(custom_explicit_sets),[is_interval_closure/3, explicit_set_cardinality/2, | |
101 | is_infinite_or_very_large_explicit_set/2, | |
102 | try_expand_custom_set_with_catch/3, is_infinite_explicit_set/1]). | |
103 | :- use_module(library(avl),[avl_size/2]). | |
104 | :- use_module(probsrc(tools_timeout), [time_out_with_factor_call/4]). | |
105 | :- use_module(probsrc(translate), [translate_bvalue_kind/2]). | |
106 | ||
107 | ||
108 | get_value_kind(CL,_,Res,Msg) :- is_infinite_explicit_set(CL),!, | |
109 | Res = 'INFINITE-Set', | |
110 | Msg = 'The constant is definitely infinite'. | |
111 | get_value_kind(CL,_,Res,Msg) :- is_interval_closure(CL,_,_),!, | |
112 | Res = 'INTERVAL', | |
113 | Msg = 'The constant is a symbolic interval.'. | |
114 | get_value_kind(SimpleValue,_ID,Res,Msg) :- SimpleValue \= closure(_,_,_), | |
115 | translate_bvalue_kind(SimpleValue,R), !, | |
116 | Res=R, Msg=''. | |
117 | get_value_kind(closure(P,T,B),ID,Res,Msg) :- | |
118 | add_debug_message(constants_analysis,'Checking if symbolic closure can be expanded: ',ID,B), | |
119 | % TODO: provide flag as this can take long | |
120 | time_out_with_factor_call( | |
121 | try_expand_custom_set_with_catch(closure(P,T,B),Expansion,get_value_kind), | |
122 | min_max_time_out(10,100,15000), % Factor 10, minimum 100 ms, maximum 15 seconds | |
123 | [silent], | |
124 | (add_debug_message(constants_analysis,'TIME-OUT during expansion for: ',ID),fail)), | |
125 | %functor(Expansion,FF,NN),print(expansion(FF,NN)),nl, | |
126 | %translate:translate_bvalue_with_limit(Expansion,100000,S), write(S),nl, | |
127 | Expansion \= closure(_,_,_), | |
128 | %(length(Expansion,Len) -> print(len(Len)),nl ; true), | |
129 | time_out_with_factor_call( | |
130 | compute_cardinality(closure(P,T,B),Expansion,Size), | |
131 | min_max_time_out(10,100,15000), % Factor=10, but min. 100 ms, max 15000 ms | |
132 | [], | |
133 | (add_debug_message(constants_analysis,'TIME-OUT during computation of Size: ',ID), | |
134 | Size='?')), | |
135 | !, % Symbolic set can be expanded; user could mark it as concrete constant | |
136 | (abstract_constant(ID,_) | |
137 | -> CC = 'Abstract constant ', | |
138 | Suggestion = ' (by moving to CONCRETE_CONSTANTS or annotating with /*@desc expand */ pragma): ' | |
139 | ; CC = 'Concrete constant ', | |
140 | Suggestion = ' (by annotating with /*@desc expand */ pragma) ' | |
141 | ), | |
142 | ajoin([CC,'is stored symbolically but can be expanded to a finite set of size ',Size, | |
143 | Suggestion ],Msg), | |
144 | get_constant_span(ID,Span), | |
145 | (constant_can_be_expanded(ID) -> true ; assert(constant_can_be_expanded(ID))), | |
146 | add_debug_message(constants_analysis,Msg,ID,Span), | |
147 | ajoin(['FINITE-SYMBOLIC-Set:',Size],Res). | |
148 | get_value_kind(CL,_,Res,Msg) :- | |
149 | is_infinite_or_very_large_explicit_set(CL,20000),!, | |
150 | Res= 'LARGE-SYMBOLIC-Set', | |
151 | Msg = 'The constant has at least 20000 elements'. | |
152 | get_value_kind(closure(_P,_T,_B),_,Res,Msg) :- !, | |
153 | Res = 'SYMBOLIC-Set', Msg=''. | |
154 | get_value_kind(global_set(G),_,Res,Msg) :- !, | |
155 | Res = G, Msg='The constant is a synonym for an enumerated or deferred set'. | |
156 | %TODO: Z freetypes, ... | |
157 | get_value_kind(_,_,'?',''). | |
158 | ||
159 | compute_cardinality(_,Expansion,Size) :- length_less(Expansion,100),!, cardinality_as_int(Expansion,int(Size)). | |
160 | compute_cardinality(CS,_,Res) :- explicit_set_cardinality(CS,Size),!,Res=Size. | |
161 | compute_cardinality(_,Expansion,Size) :- cardinality_as_int(Expansion,int(Size)). % can be slow for long lists | |
162 | ||
163 | ||
164 | /* --------------------------------------------------- */ | |
165 | :- dynamic var_min_max/3. | |
166 | ||
167 | :- use_module(probsrc(bmachine),[get_nr_of_machine_variables/1, get_nr_of_machine_constants/1, get_constant_span/2]). | |
168 | ||
169 | tcltk_compute_min_max(Res) :- tcltk_compute_min_max(interesting,Res). | |
170 | ||
171 | tcltk_compute_min_max(_,Res) :- | |
172 | \+ (visited_expression_id(X), X\=root),!, | |
173 | Res = list([list(['No states visited yet!'])]). | |
174 | tcltk_compute_min_max(Which,_) :- | |
175 | retractall(var_min_max(_,_,_)), | |
176 | visited_expression(ID,State), %print(treat(_ID)),nl, | |
177 | (Which = all -> true | |
178 | ; \+ not_interesting(ID)), % only those satisfying scope predicate | |
179 | %functor(State,F,N),print(processing(ID,F,N)),nl, | |
180 | treat_state_for_min_max(State),fail. | |
181 | tcltk_compute_min_max(_,list([Header|Res])) :- | |
182 | Header = list(['Identifier','Type','Min-Value','Max-Value']), | |
183 | findall(Info, get_info(Info,constant), CRes), | |
184 | findall(Info, get_info(Info,variable), VRes), | |
185 | append(CRes,VRes,Res). | |
186 | ||
187 | :- use_module(probsrc(tools_files),[write_lines_to_file/2]). | |
188 | pretty_print_min_max_coverage_to_file(Filename) :- | |
189 | tcltk_compute_min_max(list(Res)), | |
190 | write_lines_to_file(Filename,Res). | |
191 | ||
192 | :- use_module(probsrc(b_machine_hierarchy),[abstract_constant/2,concrete_constant/2]). | |
193 | ||
194 | get_info(Info,Type) :- | |
195 | var_min_max(Var,Min,Max), | |
196 | ((abstract_constant(Var,_) ; concrete_constant(Var,_)) | |
197 | -> Type = constant ; Type = variable), | |
198 | translate_min_max(Var,Min,Max,Type,Info). | |
199 | ||
200 | translate_min_max(Var,M,M,Type,Info) :- !, | |
201 | translate:translate_bvalue(M,TMin), | |
202 | Info = list([Var,Type,TMin,'(same value)']). | |
203 | translate_min_max(Var,pred_false,pred_true,Type,Info) :- !, | |
204 | Info = list([Var,Type,'BOOL','(full type)']). | |
205 | translate_min_max(Var,fd(X,T),fd(Y,T),Type,Info) :- !, | |
206 | gen_fd_set(X,Y,T,Set), translate:translate_bvalue(Set,TS), | |
207 | Info = list([Var,Type,TS,'(possible values)']). | |
208 | translate_min_max(Var,Min,Max,Type,Info) :- | |
209 | translate:translate_bvalue(Min,TMin), | |
210 | translate:translate_bvalue(Max,TMax), | |
211 | Info = list([Var,Type,TMin,TMax]). | |
212 | ||
213 | gen_fd_set(X,Y,_T,R) :- X>Y,!, R=[]. | |
214 | gen_fd_set(X,Y,T,[fd(X,T)|R]) :- X1 is X+1, | |
215 | gen_fd_set(X1,Y,T,R). | |
216 | ||
217 | :- use_module(probsrc(specfile),[property/2]). | |
218 | % treat a visited state for min_max | |
219 | treat_state_for_min_max(const_and_vars(_,S)) :- !, treat_state_for_min_max(S). | |
220 | treat_state_for_min_max(concrete_constants(S)) :- !, treat_state_for_min_max(S). | |
221 | treat_state_for_min_max(csp_and_b(_,S)) :- !, treat_state_for_min_max(S). | |
222 | treat_state_for_min_max(root) :- !. | |
223 | treat_state_for_min_max([]) :- !. | |
224 | treat_state_for_min_max(List) :- List=[_|_],!,member(bind(VAR,VAL),List), treat_var(VAR,VAL),fail. | |
225 | treat_state_for_min_max(OtherState) :- \+ b_or_z_mode, | |
226 | property(OtherState,'='(Variable,Value)), | |
227 | treat_var(Variable,Value),fail. | |
228 | ||
229 | treat_var(Variable,Value) :- retract(var_min_max(Variable,Min,Max)),!, | |
230 | (is_smaller(Value,Min) -> NewMin = Value ; NewMin=Min), | |
231 | (is_smaller(Max,Value) -> NewMax = Value ; NewMax=Max), | |
232 | assertz(var_min_max(Variable,NewMin,NewMax)). | |
233 | treat_var(Variable,Value) :- | |
234 | assertz(var_min_max(Variable,Value,Value)). | |
235 | ||
236 | :- use_module(library(avl),[avl_size/2, avl_max/2]). | |
237 | is_smaller(int(X),int(Y)) :- !, X < Y. | |
238 | is_smaller(string(X),string(Y)) :- !, X @< Y. | |
239 | is_smaller(pred_true,_) :- !,fail. | |
240 | is_smaller(pred_false,_) :- !. | |
241 | is_smaller(fd(X,T),fd(Y,T)) :- !, X < Y. | |
242 | is_smaller((X1,X2),(Y1,Y2)) :- !, (is_smaller(X1,Y1) -> true ; X1=Y1, is_smaller(X2,Y2)). | |
243 | is_smaller([],Y) :- !, Y \== []. | |
244 | is_smaller(_,[]) :- !, fail. | |
245 | is_smaller(avl_set(X),avl_set(Y)) :- avl_size(X,SX), avl_size(Y,SY),!, | |
246 | (SX<SY -> true | |
247 | ; SX=SY, avl_max(X,MaxX), avl_max(Y,MaxY), | |
248 | (is_smaller(MaxX,MaxY) % used to be X@<Y | |
249 | -> true | |
250 | ; MaxX=MaxY -> X @< Y) % if same maximum; look at all values; TO DO: maybe look at avl_min first | |
251 | ). | |
252 | is_smaller(X,Y) :- X @< Y. | |
253 | ||
254 | /* --------------------------------------------------- */ | |
255 | ||
256 | pretty_print_coverage_information_to_file(FileName) :- | |
257 | tcltk_compute_coverage(list(Res)), | |
258 | write_lines_to_file(FileName,Res). | |
259 | ||
260 | tcltk_compute_coverage(list(Res)) :- | |
261 | compute_the_coverage(Res,_,_,false,false). | |
262 | tcltk_compute_coverage_and_enabling(list(Res)) :- | |
263 | compute_the_coverage(Res,_,_,true,false). | |
264 | tcltk_compute_coverage_and_degree(list(Res)) :- | |
265 | compute_the_coverage(Res,_,_,false,true). | |
266 | ||
267 | compute_the_coverage(Res,TotalNodeNr,TotalTransSum,ShowEnabledInfo,ShowDegreeInfo) :- | |
268 | retractall(operation_hit(_,_,_)), | |
269 | reset_node_hit(ShowEnabledInfo,ShowDegreeInfo), | |
270 | compute_coverage(ShowEnabledInfo,ShowDegreeInfo), | |
271 | findall(S2, | |
272 | (operation_hit(OpS,Nr), | |
273 | %translate:translate_bexpression(Op,OpS), | |
274 | string_concatenate(':',Nr,S1), | |
275 | string_concatenate(OpS,S1,S2)),Res1_), | |
276 | sort(Res1_,Res1), | |
277 | length(Res1,NrCovered), | |
278 | findall(S2, | |
279 | (query_node_hit(Prop,Nr), | |
280 | string_concatenate(':',Nr,S1), | |
281 | string_concatenate(Prop,S1,S2)),Res0), | |
282 | findall(OpName, uncovered_operation(OpName),Res2_), | |
283 | sort(Res2_,Res2), | |
284 | length(Res2,NrUncovered), | |
285 | total_number_of_transitions(TotalTransSum), | |
286 | (ShowEnabledInfo=false | |
287 | -> ResDE = [] | |
288 | ; | |
289 | findall(DE, def_enables(DE),EnRes1), | |
290 | findall(DE, pos_enables(DE),EnRes2), | |
291 | append(['DEFINITELY_ENABLED_AFTER'|EnRes1],['POSSIBLY_ENABLES'|EnRes2],ResDE) | |
292 | ), | |
293 | (ShowDegreeInfo=false | |
294 | -> Res00 = ResDE | |
295 | ; | |
296 | findall(DegR, (max_degree(OpDeg,MaxDeg), tools:ajoin([OpDeg,'->',MaxDeg],DegR)), DegRes), | |
297 | append(['MAXIMUM_DEGREE'|DegRes],ResDE,Res00) | |
298 | ), | |
299 | get_op_string('UNCOVERED_',NrUncovered,UNCOVEREDSTR), | |
300 | TOTALSTR = 'TOTAL_TRANSITIONS', %get_op_string('TOTAL_',TOTALSTR), | |
301 | get_op_string('COVERED_',NrCovered,COVEREDSTR), | |
302 | append([UNCOVEREDSTR|Res2],Res00,Res01), | |
303 | append([TOTALSTR,TotalTransSum,COVEREDSTR|Res1], | |
304 | Res01,OpRes), | |
305 | query_node_hit(total,TotalNodeNr), | |
306 | ajoin(['STATES (',TotalNodeNr,')'],StatesStr), | |
307 | append([StatesStr|Res0],OpRes,Res). | |
308 | %% ,pge_algo: disabled_operations_stats(ResPGE), % in case no disabling stats are given an empty list is returned | |
309 | %% append(Res_,ResPGE,Res). | |
310 | ||
311 | :- use_module(probsrc(specfile),[get_specification_description/2]). | |
312 | get_op_string(PREFIX,Res) :- | |
313 | get_specification_description(operations,OP), | |
314 | string_concatenate(PREFIX,OP,Res). | |
315 | get_op_string(PREFIX,Nr,Res) :- | |
316 | get_specification_description(operations,OP), | |
317 | ajoin([PREFIX,OP,' (',Nr,')'],Res). | |
318 | ||
319 | ||
320 | def_enables(X) :- operation_hit(OpName1,_,_), | |
321 | definitely_enabled_after(OpName1,OpName2), | |
322 | string_concatenate('====>',OpName2,R1), | |
323 | string_concatenate(OpName1,R1,X). | |
324 | pos_enables(X) :- operation_hit(OpName1,_,_), | |
325 | possibly_enables(OpName1,OpName2), | |
326 | % \+ definitely_enabled_after(OpName1,OpName2), | |
327 | string_concatenate('=?+=>',OpName2,R1), | |
328 | string_concatenate(OpName1,R1,X). | |
329 | ||
330 | /* note: assumes that same operation name cannot appear with multiple arity */ | |
331 | ||
332 | total_number_of_transitions(TotalTransSum) :- | |
333 | findall(Nr,operation_hit(_,_,Nr),List), | |
334 | sum_list(List,0,TotalTransSum). | |
335 | ||
336 | sum_list([],N,N). | |
337 | sum_list([H|T],SoFar,Res) :- S1 is SoFar+H, sum_list(T,S1,Res). | |
338 | ||
339 | operation_hit(OpTerm,Nr) :- | |
340 | operation_hit(OpTerm,_ID,Nr). | |
341 | ||
342 | :- dynamic operation_hit/3. | |
343 | operation_hit(operation(_),node__2,55). | |
344 | ||
345 | :- use_module(probsrc(specfile),[get_possible_event/1]). | |
346 | ||
347 | uncovered_operation(OpName) :- get_possible_event(OpName), | |
348 | \+(operation_hit(OpName,_,_)). | |
349 | ||
350 | add_cov_hit(OpName,ID) :- | |
351 | ((OpName = partition(N,_Op,P)) -> LookupName = partition(N,_,P) ; LookupName = OpName), | |
352 | (retract(operation_hit(LookupName,NID,Nr)) | |
353 | -> (N1 is Nr+1, assertz(operation_hit(LookupName,NID,N1))) | |
354 | ; (assertz(operation_hit(OpName,ID,1))) | |
355 | ). | |
356 | ||
357 | query_node_hit(Prop,Nr) :- node_hit(Prop,Nr). | |
358 | query_node_hit(total,Nr) :- node_hit(open,N1), node_hit(live,N2), node_hit(deadlocked,N3), | |
359 | Nr is N1+N2+N3. | |
360 | :- dynamic node_hit/2. | |
361 | node_hit(property,22). | |
362 | add_node_hit(OpName) :- | |
363 | (retract(node_hit(OpName,Nr)) | |
364 | -> N1 is Nr+1, assertz(node_hit(OpName,N1)) | |
365 | ; assertz(node_hit(OpName,1)) | |
366 | ). | |
367 | ||
368 | reset_node_hit(ShowEnabledInfo,ShowDegreeInfo) :- | |
369 | retractall(node_hit(_,_)), | |
370 | assertz(node_hit(deadlocked,0)), | |
371 | (b_or_z_mode -> assertz(node_hit(invariant_violated,0)), | |
372 | assertz(node_hit(invariant_not_checked,0)) | |
373 | ; true), | |
374 | assertz(node_hit(open,0)), assertz(node_hit(live,0)), | |
375 | init_enabling_degree_info(ShowEnabledInfo,ShowDegreeInfo). | |
376 | ||
377 | :- use_module(probsrc(specfile),[get_operation_name/2]). | |
378 | ||
379 | :- use_module(probsrc(state_space_exploration_modes),[is_not_interesting/1]). | |
380 | compute_coverage(_,_) :- | |
381 | visited_expression_id(ID), | |
382 | (not_all_transitions_added(ID) | |
383 | -> add_node_hit(open) | |
384 | ; (transition(ID,_,_) -> add_node_hit(live) | |
385 | ; is_not_interesting(ID) -> add_node_hit(ignored) | |
386 | ; max_reached_or_timeout_for_node(ID) -> true % e.g., if MAX_OPERATIONS set to 0 | |
387 | ; add_node_hit(deadlocked)) | |
388 | ), | |
389 | (max_reached_or_timeout_for_node(ID) | |
390 | -> add_node_hit(explored_but_not_all_transitions_computed) | |
391 | ; true | |
392 | ), | |
393 | (invariant_violated(ID) -> add_node_hit(invariant_violated) ; true), | |
394 | (time_out_for_invariant(ID) -> add_node_hit(invariant_time_out) ; true), | |
395 | (time_out_for_assertions(ID) -> add_node_hit(assertions_time_out) ; true), | |
396 | (invariant_not_yet_checked(ID) -> add_node_hit(invariant_not_checked) ; true), | |
397 | (state_error(ID,_,Err),Err\=invariant_violated -> | |
398 | (Err=abort_error(TYPE,_,_,_) | |
399 | -> add_node_hit(TYPE) | |
400 | ; add_node_hit(state_error)) | |
401 | ; true), | |
402 | fail. | |
403 | compute_coverage(ShowEnabledInfo,ShowDegreeInfo) :- | |
404 | transition(ID,Operation,NID), | |
405 | nonvar(Operation), | |
406 | get_operation_name(Operation,OpName), | |
407 | add_cov_hit(OpName,ID), | |
408 | (ShowEnabledInfo=true -> update_enabling_info(ID,OpName,NID) ; true), | |
409 | (ShowDegreeInfo=true -> update_degree_info(ID,OpName) ; true), | |
410 | fail. | |
411 | %compute_coverage :- print_message('Covered Properties: '), | |
412 | % node_hit(OpName,Nr), | |
413 | % print(OpName), print(' nodes:'), print(Nr),nl, | |
414 | % fail. | |
415 | %compute_coverage :- print_message('Covered Operations: '), | |
416 | % operation_hit(OpName,_ID,Nr), | |
417 | % print(OpName), print(' hits:'), print(Nr),nl, | |
418 | % fail. | |
419 | %compute_coverage :- nl, print_message('Uncovered Operations: '), | |
420 | % uncovered_operation(OpName), | |
421 | % print(OpName), print(' '), fail. | |
422 | compute_coverage(_ShowEnabledInfo,ShowDegreeInfo) :- | |
423 | (ShowDegreeInfo=true -> finalise_degree_info ; true). | |
424 | ||
425 | ||
426 | ||
427 | ||
428 | :- dynamic definitely_enabled_after/2, possibly_enables/2. /* true if after an operation another is always enabled */ | |
429 | :- dynamic degree_in_id/3, max_degree/2. | |
430 | ||
431 | :- use_module(probsrc(specfile),[b_or_z_mode/0]). | |
432 | :- use_module(probsrc(bmachine),[b_is_operation_name/1]). | |
433 | init_enabling_degree_info(ShowEnabledInfo,_ShowDegreeInfo) :- | |
434 | retractall(definitely_enabled_after(_,_)), | |
435 | retractall(possibly_enables(_,_)), | |
436 | retractall(degree_in_id(_,_,_)), | |
437 | retractall(max_degree(_,_)), | |
438 | b_or_z_mode, | |
439 | ShowEnabledInfo=true, | |
440 | (b_is_operation_name(OpName1) ; OpName1 = '$initialise_machine'), | |
441 | b_is_operation_name(OpName2), | |
442 | assertz(definitely_enabled_after(OpName1,OpName2)), | |
443 | fail. | |
444 | init_enabling_degree_info(_,_). | |
445 | ||
446 | finalise_degree_info :- update_last_degree. | |
447 | ||
448 | update_enabling_info(_ID,OpName,NewID) :- %print(upd(_ID,OpName)),nl, | |
449 | definitely_enabled_after(OpName,OpName2), | |
450 | (opname_possible(NewID,OpName2) | |
451 | -> true | |
452 | ; retract(definitely_enabled_after(OpName,OpName2)) | |
453 | %(retract(definitely_enabled_after(OpName,OpName2)) -> print(retract(OpName,OpName2))) | |
454 | ),fail. | |
455 | update_enabling_info(ID,OpName,NewID) :- %print(upd(_ID,OpName)),nl, | |
456 | opname_possible(NewID,OpName2), % Note: can succeed multiple times with same OpName2 ! | |
457 | \+ possibly_enables(OpName,OpName2), | |
458 | \+ opname_possible(ID,OpName2), | |
459 | assertz(possibly_enables(OpName,OpName2)), | |
460 | fail. | |
461 | update_enabling_info(_,_,_). | |
462 | ||
463 | % NOTE: we suppose that the transitions are grouped by operation name for each state id; may not be true for CSP ?? | |
464 | update_degree_info(ID,OpName) :- | |
465 | get_cur_degree(ID,OpName,Degree), D1 is Degree+1, | |
466 | assertz(degree_in_id(ID,OpName,D1)), | |
467 | fail. | |
468 | update_degree_info(_,_). | |
469 | ||
470 | update_last_degree :- (retract(degree_in_id(_,OpName,D)) -> update_degree(OpName,D) ; true). | |
471 | ||
472 | get_cur_degree(ID,OpName,Degree) :- | |
473 | (retract(degree_in_id(ID,OpName,D)) -> Degree=D | |
474 | ; update_last_degree, | |
475 | Degree=0). | |
476 | update_degree(OpName,Degree) :- | |
477 | ((max_degree(OpName,Max), Max>=Degree) -> true | |
478 | ; retractall(max_degree(OpName,_)), format('Update degree ~w -> ~w~n',[OpName,Degree]), | |
479 | assertz(max_degree(OpName,Degree)) | |
480 | ). | |
481 | opname_possible(ID,OpName) :- transition(ID,Op,_),get_operation_name(Op,OpName). | |
482 | ||
483 | % ------------------------------------- | |
484 | ||
485 |