mirror of
https://github.com/Mercury-Language/mercury.git
synced 2026-04-19 19:33:46 +00:00
Estimated hours taken: 10 Branches: main, release Fix the monitors that were computing coverage rates in Morphine. The problem was the following. In order to check that two successes and one failure occur for a multi predicate, I was looking at two exits and one fail; but this is wrong since, of course, the two exits can be produced by different calls, and since all multi predicates ends up with a fail event. To get it right, I need to associate the call number to exit and fail events. To do that, I maintain at exit ports the list of call numbers; when an exit event occurs, I consider it as covered iff the current call number is in the list. On the contrary, I consider a failure as covered at a fail port iff the current call number is not in the list. This also holds for semidet and nondet procedures. extras/morphine/non-regression-tests/queens.exp: Update the new expected outputs. extras/morphine/non-regression-tests/queens.in: Update the year of the copyrigth message. extras/morphine/source/call_site_cov.in: extras/morphine/source/pred_cov.in: Change the code so that it does what it is supposed to do. Also use a map instead of a list to store what has to be covered for each procedure. extras/morphine/source/generate_pred_cov.m: extras/morphine/source/generate_call_site_cov.m: extras/morphine/source/coverage_util.m: Generate an initialize/1 predicate that uses map instead of lists. extras/morphine/source/coverage.op: Coverage monitors now output assoc lists.
330 lines
9.4 KiB
Plaintext
330 lines
9.4 KiB
Plaintext
%------------------------------------------------------------------------------%
|
|
% Copyright (C) 1999-2002 INRIA/INSA de Rennes/IFSIC.
|
|
% This file may only be copied under the terms of the GNU Library General
|
|
% Public License - see the file License in the Morphine distribution.
|
|
%
|
|
% Author : Erwan Jahier
|
|
% File : control_flow.op
|
|
%
|
|
|
|
%------------------------------------------------------------------------------%
|
|
opium_scenario(
|
|
name : control_flow,
|
|
files : [control_flow],
|
|
scenarios : [collect],
|
|
message :
|
|
"Scenario that provides commands which generates various control flow graphs. \
|
|
This scenario make use of the graph generator `dot' program (so you will need to \
|
|
have dot available from your PATH environment variable to be able to use this \
|
|
scenario)."
|
|
).
|
|
|
|
%------------------------------------------------------------------------------%
|
|
opium_parameter(
|
|
name : ps_viewer,
|
|
arg_list : [String],
|
|
arg_type_list : [string],
|
|
parameter_type : single,
|
|
default : ["gv "],
|
|
commands : [control_flow_graph, dynamic_call_graph],
|
|
message :
|
|
"Parameter which sets the name of the command used to visualize the generated \
|
|
post-script file."
|
|
).
|
|
|
|
|
|
%------------------------------------------------------------------------------%
|
|
opium_command(
|
|
name : control_flow_graph,
|
|
arg_list : [ProgramCall],
|
|
arg_type_list : [is_mercury_program_call],
|
|
abbrev : cfg,
|
|
interface : button,
|
|
command_type : opium,
|
|
implementation : control_flow_graph_Op,
|
|
parameters : [ps_viewer],
|
|
message :
|
|
"This command generates a control flow graph of the Mercury program \
|
|
execution."
|
|
).
|
|
|
|
control_flow_graph_Op(Program) :-
|
|
( getval(state_of_morphine, running) ->
|
|
abort_trace
|
|
;
|
|
true
|
|
),
|
|
run(Program),
|
|
generate_filename(Program, ".cfg.dot", ".cfg.ps", DotFile, PsFile),
|
|
generate_control_flow_graph(Graph),
|
|
display_graph(Graph, DotFile, PsFile).
|
|
|
|
|
|
% :- type graph ---> list(arc(procedure, procedure)).
|
|
%
|
|
%:- pred generate_control_flow_graph(graph).
|
|
%:- mode generate_control_flow_graph(out) is det.
|
|
%
|
|
generate_control_flow_graph(G) :-
|
|
getenv("MERCURY_MORPHINE_DIR", MorphineDir),
|
|
append_strings(MorphineDir, "/source/collect__control_flow_graph",
|
|
CollectFile),
|
|
collect(CollectFile, Result),
|
|
Result = collected_type(G).
|
|
|
|
% This is the pure Morphine version of generate_control_flow_graph that
|
|
% I originally wrote and which is about 20 times as slow as the collect
|
|
% one:
|
|
%
|
|
% generate_control_flow_graph(G0, Proc, G) :-
|
|
% ( fget_np(port = [call, exit,redo,fail]) ->
|
|
% current(proc_name = Name and arity = N),
|
|
% CurrentProc = Name/N,
|
|
% G1 = [arc(Proc, CurrentProc)|G0],
|
|
% generate_control_flow_graph(G1, CurrentProc, G),
|
|
% !
|
|
% ;
|
|
% % end of the execution
|
|
% remove_dupl(G0, G)
|
|
% ).
|
|
|
|
|
|
%------------------------------------------------------------------------------%
|
|
opium_command(
|
|
name : dynamic_call_graph,
|
|
arg_list : [ProgramCall],
|
|
arg_type_list : [is_mercury_program_call],
|
|
abbrev : dcg,
|
|
interface : button,
|
|
command_type : opium,
|
|
implementation : dynamic_call_graph_Op,
|
|
parameters : [ps_viewer],
|
|
message :
|
|
"This command generates a dynamic call graph of the Mercury program \
|
|
execution. We call a dynamic call graph the dynamic slice of the (static) \
|
|
call graph, i.e. the calls that have effectively been done during the execution."
|
|
).
|
|
|
|
dynamic_call_graph_Op(Program) :-
|
|
run(Program),
|
|
generate_filename(Program, ".dcg.dot", ".dcg.ps", DotFile, PsFile),
|
|
generate_dynamic_call_graph(Graph),
|
|
display_graph(Graph, DotFile, PsFile).
|
|
|
|
%
|
|
%:- pred generate_dynamic_call_graph(graph).
|
|
%:- mode generate_dynamic_call_graph(out) is det.
|
|
%
|
|
|
|
generate_dynamic_call_graph(G) :-
|
|
getenv("MERCURY_MORPHINE_DIR", MorphineDir),
|
|
append_strings(MorphineDir, "/source/collect__dynamic_call_graph",
|
|
CollectFile),
|
|
collect(CollectFile, Result),
|
|
Result = collected_type(G0),
|
|
reverse(G0, G).
|
|
|
|
% This is the pure Morphine version:
|
|
%
|
|
% generate_dynamic_call_graph(G0, G) :-
|
|
% ( fget_np(port = call) ->
|
|
% current(proc_name = Name and arity = N),
|
|
% ancestor(Anc),
|
|
% CurrentProc = Name/N,
|
|
% G1 = [arc(Anc, CurrentProc)|G0],
|
|
% generate_dynamic_call_graph(G1, G),
|
|
% !
|
|
% ;
|
|
% % end of the execution
|
|
% remove_dupl(G0, G)
|
|
% ).
|
|
|
|
% ancestor(Anc) :-
|
|
% current(stack = [_, List|_]),
|
|
% member(proc(_,Name,Arity,_), List),
|
|
% Anc = Name/Arity,
|
|
% !.
|
|
% ancestor(none).
|
|
|
|
%------------------------------------------------------------------------------%
|
|
%
|
|
%:- pred generate_filename(string, string, string, string, string).
|
|
%:- mode generate_filename(in ,in, in, out, out) is det.
|
|
%
|
|
generate_filename(Program, DotExt, PsExt, DotFile, PsFile) :-
|
|
( string(Program) ->
|
|
ProgramStr = Program,
|
|
!
|
|
;
|
|
term_string(Program, ProgramStr)
|
|
),
|
|
decompose_path_call_and_args1(ProgramStr, _, ProgramCallStr, _),
|
|
append_strings(ProgramCallStr, DotExt, DotFile),
|
|
append_strings(ProgramCallStr, PsExt, PsFile).
|
|
|
|
%:- pred display_graph(graph, string, string).
|
|
%:- mode display_graph(in, in, in) is det.
|
|
%
|
|
display_graph(Graph, DotFile, PsFile) :-
|
|
extract_proc_list_from_graph(Graph, ProcList),
|
|
open(DotFile, write, dotfile),
|
|
print(dotfile, "digraph G {\n\n"),
|
|
print(dotfile, "ordering=out;\n\n"),
|
|
% so that the oroff tree nodes are displayed in their
|
|
% order of creation
|
|
dump_proc(ProcList),
|
|
dump_graph(Graph),
|
|
print(dotfile, "}\n"),
|
|
close(dotfile),
|
|
compile_dot(DotFile, PsFile),
|
|
display_ps(PsFile).
|
|
dump_proc([]).
|
|
dump_proc([X|Xs]) :-
|
|
printf(dotfile, "\t \t \"%w\"\n", [X]),
|
|
dump_proc2(Xs).
|
|
|
|
|
|
dump_proc2([]) :- nl(dotfile).
|
|
dump_proc2([X|Xs]) :-
|
|
printf(dotfile, "\t ; \t \"%w\"\n", [X]),
|
|
dump_proc2(Xs).
|
|
|
|
dump_graph([]) :- nl(dotfile).
|
|
dump_graph([arc(X,Y)|CG]) :-
|
|
printf(dotfile, "\t\"%w\" -> \"%w\";\n", [X,Y]),
|
|
dump_graph(CG).
|
|
dump_graph([arc(X, Label, Y)|CG]) :-
|
|
printf(dotfile, "\t\"%w\" -> \"%w\"[label=%w];\n", [X,Y,Label]),
|
|
dump_graph(CG).
|
|
|
|
%------------------------------------------------------------------------------%
|
|
opium_primitive(
|
|
name : compile_dot,
|
|
arg_list : [DotFile, PsFile],
|
|
arg_type_list : [string, string],
|
|
abbrev : _,
|
|
implementation : compile_dot_Op,
|
|
message :
|
|
"Primitive which applies `dot' to DotFile and outputs the resulting \
|
|
post-script in PsFile."
|
|
).
|
|
|
|
compile_dot_Op(DotFile, PsFile) :-
|
|
concat_string(["dot -Tps ", DotFile, " -o ", PsFile], Cmd),
|
|
print(Cmd), nl,
|
|
sh(Cmd).
|
|
|
|
|
|
%------------------------------------------------------------------------------%
|
|
opium_primitive(
|
|
name : display_ps,
|
|
arg_list : [PsFile],
|
|
arg_type_list : [string],
|
|
abbrev : _,
|
|
implementation : display_ps_Op,
|
|
message :
|
|
"primitive which displays post-script files."
|
|
).
|
|
|
|
display_ps_Op(PsFile) :-
|
|
ps_viewer(PsViewer),
|
|
concat_string([PsViewer, " ", PsFile, " &"], Cmd),
|
|
print(Cmd), nl,
|
|
sh(Cmd).
|
|
|
|
|
|
%------------------------------------------------------------------------------%
|
|
%
|
|
% :- pred extract_proc_list_from_graph(graph, list(procedure)).
|
|
% :- mode extract_proc_list_from_graph(in, out) is det.
|
|
%
|
|
extract_proc_list_from_graph(Graph, ProcList) :-
|
|
extract_proc_list_from_graph2(Graph, ProcList0),
|
|
remove_dupl(ProcList0, ProcList1),
|
|
reverse(ProcList1, ProcList).
|
|
|
|
extract_proc_list_from_graph2([], []).
|
|
extract_proc_list_from_graph2([Arc|Graph], [Proc|ProcList]) :-
|
|
( Arc = arc(Proc,_), ! ; Arc = arc(Proc,_,_)),
|
|
extract_proc_list_from_graph2(Graph, ProcList).
|
|
|
|
|
|
%------------------------------------------------------------------------------%
|
|
remove_dupl([], []).
|
|
remove_dupl(L0, L) :-
|
|
L0 = [X|L1],
|
|
remove_dupl(L1, L2),
|
|
( member(X, L2) ->
|
|
L = L2
|
|
;
|
|
L = [X|L2]
|
|
).
|
|
|
|
|
|
%------------------------------------------------------------------------------%
|
|
%------------------------------------------------------------------------------%
|
|
%------------------------------------------------------------------------------%
|
|
opium_command(
|
|
name : proof_tree,
|
|
arg_list : [ProgramCall],
|
|
arg_type_list : [is_mercury_program_call],
|
|
abbrev : pt,
|
|
interface : button,
|
|
command_type : opium,
|
|
implementation : proof_tree_Op,
|
|
parameters : [ps_viewer],
|
|
message :
|
|
"Computes the program proof tree."
|
|
).
|
|
|
|
proof_tree_Op(Program) :-
|
|
run(Program),
|
|
generate_filename(Program, ".pt.dot", ".pt.ps", DotFile, PsFile),
|
|
generate_proof_tree_graph(Graph),
|
|
display_graph(Graph, DotFile, PsFile).
|
|
|
|
%
|
|
%:- pred generate_dynamic_call_graph(graph).
|
|
%:- mode generate_dynamic_call_graph(out) is det.
|
|
%
|
|
|
|
generate_proof_tree_graph(G) :-
|
|
getenv("MERCURY_MORPHINE_DIR", MorphineDir),
|
|
append_strings(MorphineDir, "/source/collect__proof_tree",
|
|
CollectFile),
|
|
get_parameter(collect_arg, CollectArg),
|
|
set_parameter(collect_arg, [yes]),
|
|
collect(CollectFile, Result),
|
|
set_parameter(collect_arg, CollectArg),
|
|
( Result = ct(ProofTree), ! ; Result = pb(Msg), print(Msg), abort),
|
|
tree_to_graph(toplevel, ProofTree, G).
|
|
|
|
tree_to_graph(Father, leaf(_, PredUniv), [arc(Father, Pred)]) :-
|
|
remove_type_pred(PredUniv, Pred).
|
|
tree_to_graph(Father, tree(_, PredUniv, TreeList), Graph) :-
|
|
remove_type_pred(PredUniv, Pred),
|
|
maplist(tree_to_graph(Pred), TreeList, GraphList),
|
|
list_of_list_to_list(GraphList, Graph0),
|
|
Graph = [arc(Father, Pred) | Graph0].
|
|
|
|
remove_type_pred(p(NameStr, _Arity, ArgUnivList), Pred) :-
|
|
maplist(remove_type_arg, ArgUnivList, ArgList),
|
|
( string(NameStr) -> atom_string(Name, NameStr) ; Name = NameStr),
|
|
append([Name], ArgList, NameArgList),
|
|
Pred =.. NameArgList.
|
|
|
|
remove_type_arg(univ_cons(state('<<c_pointer>>')), '-'). % We don't display that
|
|
remove_type_arg(univ_cons(Arg), Arg).
|
|
|
|
|
|
list_of_list_to_list([], []).
|
|
list_of_list_to_list([List], List).
|
|
list_of_list_to_list([List1, List2 | Tail], LList) :-
|
|
append(List1, List2, List),
|
|
LList0 = [List | Tail],
|
|
list_of_list_to_list(LList0, LList).
|
|
|
|
|
|
|
|
|