From bceee0662a9f6aac6b0987517498bff2ade144df Mon Sep 17 00:00:00 2001 From: Stavros Aronis Date: Thu, 9 Feb 2012 14:55:38 +0100 Subject: Refactoring in plt --- lib/dialyzer/src/dialyzer_plt.erl | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) (limited to 'lib/dialyzer') diff --git a/lib/dialyzer/src/dialyzer_plt.erl b/lib/dialyzer/src/dialyzer_plt.erl index 06eaadad9c..c973762df1 100644 --- a/lib/dialyzer/src/dialyzer_plt.erl +++ b/lib/dialyzer/src/dialyzer_plt.erl @@ -156,12 +156,7 @@ lookup_contract(#plt{contracts = Contracts}, [{mfa(), {{Filename::string(), Line::pos_integer()}, #contract{}}}]. lookup_callbacks(#plt{callbacks = Callbacks}, Mod) when is_atom(Mod) -> - FunModFilter = - fun({M, _F, _A}, _Val) -> M =:= Mod; - ( _Key, _Val) -> false - end, - ModCallbacks = dict:filter(FunModFilter, Callbacks), - dict:to_list(ModCallbacks). + table_filter_module(Callbacks, Mod). -type ret_args_types() :: {erl_types:erl_type(), [erl_types:erl_type()]}. @@ -608,6 +603,14 @@ table_lookup_module(Plt, Mod) -> false -> {value, List} end. +table_filter_module(Plt, Mod) -> + FunModFilter = + fun({M, _F, _A}, _Val) -> M =:= Mod; + ( _Key, _Val) -> false + end, + ModCallbacks = dict:filter(FunModFilter, Plt), + dict:to_list(ModCallbacks). + table_all_modules(Plt) -> Fold = fun({M, _F, _A}, _Val, Acc) -> sets:add_element(M, Acc); -- cgit v1.2.3 From 130465c94d6c56e97dab1c1880435e68e57759c7 Mon Sep 17 00:00:00 2001 From: Stavros Aronis Date: Wed, 15 Feb 2012 15:47:46 +0100 Subject: Flatten order of dataflow analyses Dataflow analysis was structured to find SCCs of modules, without making any use of the information that these were indeed SCCs. --- lib/dialyzer/src/dialyzer_callgraph.erl | 49 +++++++++--------------------- lib/dialyzer/src/dialyzer_succ_typings.erl | 31 +++---------------- 2 files changed, 19 insertions(+), 61 deletions(-) (limited to 'lib/dialyzer') diff --git a/lib/dialyzer/src/dialyzer_callgraph.erl b/lib/dialyzer/src/dialyzer_callgraph.erl index effd619bde..13eb9418dc 100644 --- a/lib/dialyzer/src/dialyzer_callgraph.erl +++ b/lib/dialyzer/src/dialyzer_callgraph.erl @@ -238,33 +238,30 @@ renew_race_info(CG, RaceCode, PublicTables, NamedTables) -> modules(#callgraph{digraph = DG}) -> ordsets:from_list([M || {M,_F,_A} <- digraph_vertices(DG)]). --spec module_postorder(callgraph()) -> [[module()]]. +-spec module_postorder(callgraph()) -> [module()]. module_postorder(#callgraph{digraph = DG}) -> - {MDG, _Nodes} = get_module_digraph_and_nodes(DG), - MDG1 = digraph_utils:condensation(MDG), - PostOrder = digraph_utils:postorder(MDG1), - PostOrder1 = sort_sccs_internally(PostOrder, MDG), - digraph:delete(MDG1), - digraph_delete(MDG), - PostOrder1. - -get_module_digraph_and_nodes(DG) -> Edges = digraph_edges(DG), Nodes = ordsets:from_list([M || {M,_F,_A} <- digraph_vertices(DG)]), - MDG = digraph:new(), - MDG = digraph_confirm_vertices(Nodes, MDG), - MDG = create_module_digraph(Edges, MDG), - {MDG, Nodes}. + MDG = digraph:new([acyclic]), + MDG1 = digraph_confirm_vertices(Nodes, MDG), + MDG2 = create_module_digraph(Edges, MDG1), + PostOrder = digraph_utils:postorder(MDG2), + digraph:delete(MDG2), + PostOrder. %% The module deps of a module are modules that depend on the module -spec module_deps(callgraph()) -> dict(). module_deps(#callgraph{digraph = DG}) -> - {MDG, Nodes} = get_module_digraph_and_nodes(DG), - Deps = [{N, ordsets:from_list(digraph:in_neighbours(MDG, N))} + Edges = digraph_edges(DG), + Nodes = ordsets:from_list([M || {M,_F,_A} <- digraph_vertices(DG)]), + MDG = digraph:new(), + MDG1 = digraph_confirm_vertices(Nodes, MDG), + MDG2 = create_module_digraph(Edges, MDG1), + Deps = [{N, ordsets:from_list(digraph:in_neighbours(MDG2, N))} || N <- Nodes], - digraph_delete(MDG), + digraph_delete(MDG2), dict:from_list(Deps). -spec strip_module_deps(dict(), set()) -> dict(). @@ -276,22 +273,6 @@ strip_module_deps(ModDeps, StripSet) -> FilterFun2 = fun(_Key, ValSet) -> ValSet =/= [] end, dict:filter(FilterFun2, ModDeps1). -sort_sccs_internally(PO, MDG) -> - sort_sccs_internally(PO, MDG, []). - -sort_sccs_internally([SCC|SCCs], MDG, Acc) -> - case SCC of - [_, _, _ | _] -> % length(SCC) >= 3 - TmpDG = digraph_utils:subgraph(MDG, SCC), - NewSCC = digraph_utils:postorder(TmpDG), - digraph_delete(TmpDG), - sort_sccs_internally(SCCs, MDG, [NewSCC|Acc]); - _ -> - sort_sccs_internally(SCCs, MDG, [SCC|Acc]) - end; -sort_sccs_internally([], _MDG, Acc) -> - lists:reverse(Acc). - create_module_digraph([{{M, _, _}, {M, _, _}}|Left], MDG) -> create_module_digraph(Left, MDG); create_module_digraph([{{M1, _, _}, {M2, _, _}}|Left], MDG) -> @@ -314,7 +295,7 @@ reset_from_funs(Funs, #callgraph{digraph = DG} = CG) -> digraph_delete(SubGraph), CG#callgraph{postorder = Postorder}. --spec module_postorder_from_funs([mfa_or_funlbl()], callgraph()) -> [[module()]]. +-spec module_postorder_from_funs([mfa_or_funlbl()], callgraph()) -> [module()]. module_postorder_from_funs(Funs, #callgraph{digraph = DG} = CG) -> SubGraph = digraph_reaching_subgraph(Funs, DG), diff --git a/lib/dialyzer/src/dialyzer_succ_typings.erl b/lib/dialyzer/src/dialyzer_succ_typings.erl index 5982603b7b..2c2c39951c 100644 --- a/lib/dialyzer/src/dialyzer_succ_typings.erl +++ b/lib/dialyzer/src/dialyzer_succ_typings.erl @@ -181,17 +181,13 @@ refine_succ_typings(ModulePostorder, State) -> ?debug("Module postorder: ~p\n", [ModulePostorder]), refine_succ_typings(ModulePostorder, State, []). -refine_succ_typings([SCC|SCCs], State, Fixpoint) -> - Msg = io_lib:format("Dataflow of one SCC: ~w\n", [SCC]), +refine_succ_typings([M|Rest], State, Fixpoint) -> + Msg = io_lib:format("Dataflow of module: ~w\n", [M]), send_log(State#st.parent, Msg), ?debug("~s\n", [Msg]), - {NewState, FixpointFromScc} = - case SCC of - [M] -> refine_one_module(M, State); - [_|_] -> refine_one_scc(SCC, State) - end, + {NewState, FixpointFromScc} = refine_one_module(M, State), NewFixpoint = ordsets:union(Fixpoint, FixpointFromScc), - refine_succ_typings(SCCs, NewState, NewFixpoint); + refine_succ_typings(Rest, NewState, NewFixpoint); refine_succ_typings([], State, Fixpoint) -> case Fixpoint =:= [] of true -> {fixpoint, State}; @@ -225,25 +221,6 @@ refine_one_module(M, State) -> st__renew_state_calls(Callgraph, State) -> State#st{callgraph = Callgraph}. -refine_one_scc(SCC, State) -> - refine_one_scc(SCC, State, []). - -refine_one_scc(SCC, State, AccFixpoint) -> - {NewState, FixpointFromScc} = refine_mods_in_scc(SCC, State, []), - case FixpointFromScc =:= [] of - true -> {NewState, AccFixpoint}; - false -> - NewAccFixpoint = ordsets:union(AccFixpoint, FixpointFromScc), - refine_one_scc(SCC, NewState, NewAccFixpoint) - end. - -refine_mods_in_scc([Mod|Mods], State, Fixpoint) -> - {NewState, FixpointFromModule} = refine_one_module(Mod, State), - NewFixpoint = ordsets:union(FixpointFromModule, Fixpoint), - refine_mods_in_scc(Mods, NewState, NewFixpoint); -refine_mods_in_scc([], State, Fixpoint) -> - {State, Fixpoint}. - reached_fixpoint(OldTypes, NewTypes) -> reached_fixpoint(OldTypes, NewTypes, false). -- cgit v1.2.3 From b45b7e76502cb2df55bec392c5d6badfecd997ca Mon Sep 17 00:00:00 2001 From: Stavros Aronis Date: Sun, 12 Feb 2012 19:41:27 +0100 Subject: Simplify typesig postorder calculation As the storing in the codeserver is organized per function there is no need for fancy code to make use of the old caching capabilities. --- lib/dialyzer/src/dialyzer_callgraph.erl | 62 +-------------------------------- 1 file changed, 1 insertion(+), 61 deletions(-) (limited to 'lib/dialyzer') diff --git a/lib/dialyzer/src/dialyzer_callgraph.erl b/lib/dialyzer/src/dialyzer_callgraph.erl index 13eb9418dc..2a01b0f753 100644 --- a/lib/dialyzer/src/dialyzer_callgraph.erl +++ b/lib/dialyzer/src/dialyzer_callgraph.erl @@ -515,68 +515,8 @@ digraph_in_neighbours(V, DG) -> List -> List end. -%% Pick all the independent nodes (leaves) from one module. Then try -%% to stay within the module until no more independent nodes can be -%% chosen. Then pick a new module and so on. -%% -%% Note that an SCC that ranges over more than one module is -%% considered to belong to all modules to make sure that we do not -%% lose any nodes. - digraph_postorder(Digraph) -> - %% Remove all self-edges for SCCs. - Edges = [digraph:edge(Digraph, E) || E <- digraph:edges(Digraph)], - SelfEdges = [E || {E, V, V, _} <- Edges], - true = digraph:del_edges(Digraph, SelfEdges), - %% Determine the first module outside of the loop. - Leaves = digraph_leaves(Digraph), - case Leaves =:= [] of - true -> []; - false -> - {Module, Taken} = take_sccs_from_fresh_module(Leaves), - true = digraph:del_vertices(Digraph, Taken), - digraph_postorder(Digraph, Module, [Taken]) - end. - -digraph_postorder(Digraph, LastModule, Acc) -> - Leaves = digraph_leaves(Digraph), - case Leaves =:= [] of - true -> lists:append(lists:reverse(Acc)); - false -> - case [SCC || SCC <- Leaves, scc_belongs_to_module(SCC, LastModule)] of - [] -> - {NewModule, NewTaken} = take_sccs_from_fresh_module(Leaves), - true = digraph:del_vertices(Digraph, NewTaken), - digraph_postorder(Digraph, NewModule, [NewTaken|Acc]); - NewTaken -> - true = digraph:del_vertices(Digraph, NewTaken), - digraph_postorder(Digraph, LastModule, [NewTaken|Acc]) - end - end. - -digraph_leaves(Digraph) -> - [V || V <- digraph:vertices(Digraph), digraph:out_degree(Digraph, V) =:= 0]. - -take_sccs_from_fresh_module(Leaves) -> - NewModule = find_module(hd(Leaves)), - {NewModule, - [SCC || SCC <- Leaves, scc_belongs_to_module(SCC, NewModule)]}. - --spec scc_belongs_to_module(scc(), module()) -> boolean(). - -scc_belongs_to_module([Label|Left], Module) when is_integer(Label) -> - scc_belongs_to_module(Left, Module); -scc_belongs_to_module([{M, _, _}|Left], Module) -> - if M =:= Module -> true; - true -> scc_belongs_to_module(Left, Module) - end; -scc_belongs_to_module([], _Module) -> - false. - --spec find_module(scc()) -> module(). - -find_module([{M, _, _}|_]) -> M; -find_module([Label|Left]) when is_integer(Label) -> find_module(Left). + digraph_utils:postorder(Digraph). digraph_finalize(DG) -> DG1 = digraph_utils:condensation(DG), -- cgit v1.2.3 From 70a98e31f56ef96383a25c71507c39875979f86c Mon Sep 17 00:00:00 2001 From: Stavros Aronis Date: Thu, 1 Dec 2011 17:15:22 +0100 Subject: Replace Dialyzer's hand-made codeserver with an ets table --- lib/dialyzer/src/dialyzer_codeserver.erl | 92 ++++++++------------------------ 1 file changed, 21 insertions(+), 71 deletions(-) (limited to 'lib/dialyzer') diff --git a/lib/dialyzer/src/dialyzer_codeserver.erl b/lib/dialyzer/src/dialyzer_codeserver.erl index f1e87affbd..1506f4edac 100644 --- a/lib/dialyzer/src/dialyzer_codeserver.erl +++ b/lib/dialyzer/src/dialyzer_codeserver.erl @@ -63,7 +63,7 @@ %%-------------------------------------------------------------------- --record(codeserver, {table_pid :: pid(), +-record(codeserver, {table_pid :: ets:tid(), exported_types = sets:new() :: set(), % set(mfa()) temp_exported_types = sets:new() :: set(), % set(mfa()) exports = sets:new() :: set(), % set(mfa()) @@ -83,18 +83,28 @@ -spec new() -> codeserver(). new() -> - #codeserver{table_pid = table__new()}. + #codeserver{table_pid = ets:new(dialyzer_codeserver, [private, compressed])}. -spec delete(codeserver()) -> 'ok'. delete(#codeserver{table_pid = TablePid}) -> - table__delete(TablePid). + ets:delete(TablePid). -spec insert(atom(), cerl:c_module(), codeserver()) -> codeserver(). insert(Mod, ModCode, CS) -> - NewTablePid = table__insert(CS#codeserver.table_pid, Mod, ModCode), - CS#codeserver{table_pid = NewTablePid}. + Name = cerl:module_name(ModCode), + Exports = cerl:module_exports(ModCode), + Attrs = cerl:module_attrs(ModCode), + Defs = cerl:module_defs(ModCode), + As = cerl:get_ann(ModCode), + Funs = + [{{Mod, cerl:fname_id(Var), cerl:fname_arity(Var)}, + Val} || Val = {Var, _Fun} <- Defs], + Keys = [Key || {Key, _Value} <- Funs], + ets:insert(CS#codeserver.table_pid, + [{Mod, {Name, Exports, Attrs, Keys, As}} | Funs]), + CS. -spec insert_temp_exported_types(set(), codeserver()) -> codeserver(). @@ -268,69 +278,9 @@ finalize_contracts(CnDict, CbDict, CS) -> temp_callbacks = dict:new() }. -table__new() -> - spawn_link(fun() -> table__loop(none, dict:new()) end). - -table__delete(TablePid) -> - TablePid ! stop, - ok. - -table__lookup(TablePid, Key) -> - TablePid ! {self(), lookup, Key}, - receive - {TablePid, Key, Ans} -> Ans - end. - -table__insert(TablePid, Key, Val) -> - TablePid ! {insert, [{Key, term_to_binary(Val, [compressed])}]}, - TablePid. - -table__loop(Cached, Map) -> - receive - stop -> ok; - {Pid, lookup, {M, F, A} = MFA} -> - {NewCached, Ans} = - case Cached of - {M, Tree} -> - Val = find_fun(F, A, Tree), - {Cached, Val}; - _ -> - Tree = fetch_and_expand(M, Map), - Val = find_fun(F, A, Tree), - {{M, Tree}, Val} - end, - Pid ! {self(), MFA, Ans}, - table__loop(NewCached, Map); - {Pid, lookup, Mod} when is_atom(Mod) -> - Ans = case Cached of - {Mod, Tree} -> Tree; - _ -> fetch_and_expand(Mod, Map) - end, - Pid ! {self(), Mod, Ans}, - table__loop({Mod, Ans}, Map); - {insert, List} -> - NewMap = lists:foldl(fun({Key, Val}, AccMap) -> - dict:store(Key, Val, AccMap) - end, Map, List), - table__loop(Cached, NewMap) - end. - -fetch_and_expand(Mod, Map) -> - try - Bin = dict:fetch(Mod, Map), - binary_to_term(Bin) - catch - _:_ -> - S = atom_to_list(Mod), - Msg = "found no module named '" ++ S ++ "' in the analyzed files", - exit({error, Msg}) - end. - -find_fun(F, A, Tree) -> - Pred = - fun({Var, _Fun}) -> - (cerl:fname_id(Var) =/= F) orelse - (cerl:fname_arity(Var) =/= A) - end, - [Val|_] = lists:dropwhile(Pred, cerl:module_defs(Tree)), - Val. +table__lookup(TablePid, M) when is_atom(M) -> + {Name, Exports, Attrs, Keys, As} = ets:lookup_element(TablePid, M, 2), + Defs = [table__lookup(TablePid, Key) || Key <- Keys], + cerl:ann_c_module(As, Name, Exports, Attrs, Defs); +table__lookup(TablePid, MFA) -> + ets:lookup_element(TablePid, MFA, 2). -- cgit v1.2.3 From 8a3ea1fa45cac31cdc1241a6ea1e1a0d857c1429 Mon Sep 17 00:00:00 2001 From: Stavros Aronis Date: Thu, 22 Dec 2011 19:50:13 +0200 Subject: Convert remaining codeserver fields to ETS tables --- lib/dialyzer/src/dialyzer_analysis_callgraph.erl | 7 +- lib/dialyzer/src/dialyzer_codeserver.erl | 218 +++++++++++++++-------- lib/dialyzer/src/dialyzer_plt.erl | 7 +- 3 files changed, 144 insertions(+), 88 deletions(-) (limited to 'lib/dialyzer') diff --git a/lib/dialyzer/src/dialyzer_analysis_callgraph.erl b/lib/dialyzer/src/dialyzer_analysis_callgraph.erl index 2c4622155a..7221d81a72 100644 --- a/lib/dialyzer/src/dialyzer_analysis_callgraph.erl +++ b/lib/dialyzer/src/dialyzer_analysis_callgraph.erl @@ -348,12 +348,9 @@ compile_common(File, AbstrCode, CompOpts, Callgraph, CServer, UseContracts) -> store_core(Mod, Core, NoWarn, Callgraph, CServer) -> Exp = get_exports_from_core(Core), - OldExpTypes = dialyzer_codeserver:get_temp_exported_types(CServer), - NewExpTypes = get_exported_types_from_core(Core), - MergedExpTypes = sets:union(NewExpTypes, OldExpTypes), + ExpTypes = get_exported_types_from_core(Core), CServer1 = dialyzer_codeserver:insert_exports(Exp, CServer), - CServer2 = dialyzer_codeserver:insert_temp_exported_types(MergedExpTypes, - CServer1), + CServer2 = dialyzer_codeserver:insert_temp_exported_types(ExpTypes, CServer1), {LabeledCore, CServer3} = label_core(Core, CServer2), store_code_and_build_callgraph(Mod, LabeledCore, Callgraph, CServer3, NoWarn). diff --git a/lib/dialyzer/src/dialyzer_codeserver.erl b/lib/dialyzer/src/dialyzer_codeserver.erl index 1506f4edac..9989118671 100644 --- a/lib/dialyzer/src/dialyzer_codeserver.erl +++ b/lib/dialyzer/src/dialyzer_codeserver.erl @@ -52,9 +52,7 @@ new/0, set_next_core_label/2, set_temp_records/2, - store_records/3, store_temp_records/3, - store_contracts/3, store_temp_contracts/4]). -export_type([codeserver/0]). @@ -63,32 +61,91 @@ %%-------------------------------------------------------------------- --record(codeserver, {table_pid :: ets:tid(), - exported_types = sets:new() :: set(), % set(mfa()) - temp_exported_types = sets:new() :: set(), % set(mfa()) - exports = sets:new() :: set(), % set(mfa()) - next_core_label = 0 :: label(), - records = dict:new() :: dict(), - temp_records = dict:new() :: dict(), - contracts = dict:new() :: dict(), - callbacks = dict:new() :: dict(), - temp_contracts = dict:new() :: dict(), - temp_callbacks = dict:new() :: dict() +-type dict_ets() :: ets:tid(). +-type set_ets() :: ets:tid(). + +-record(codeserver, {next_core_label = 0 :: label(), + code :: dict_ets(), + exported_types :: set_ets(), % set(mfa()) + records :: dict_ets(), + contracts :: dict_ets(), + callbacks :: dict_ets(), + exports :: 'clean' | set_ets(), % set(mfa()) + temp_exported_types :: 'clean' | set_ets(), % set(mfa()) + temp_records :: 'clean' | dict_ets(), + temp_contracts :: 'clean' | dict_ets(), + temp_callbacks :: 'clean' | dict_ets() }). -opaque codeserver() :: #codeserver{}. %%-------------------------------------------------------------------- +ets_dict_find(Key, Table) -> + try ets:lookup_element(Table, Key, 2) of + Val -> {ok, Val} + catch + _:_ -> error + end. + +ets_dict_store(Key, Element, Table) -> + true = ets:insert(Table, {Key, Element}), + Table. + +ets_dict_store_dict(Dict, Table) -> + true = ets:insert(Table, dict:to_list(Dict)). + +ets_dict_to_dict(Table) -> + Fold = fun({Key,Value}, Dict) -> dict:store(Key, Value, Dict) end, + ets:foldl(Fold, dict:new(), Table). + +ets_set_is_element(Key, Table) -> + case ets:lookup(Table, Key) of + [] -> false; + _ -> true + end. + +ets_set_insert_set(Set, Table) -> + ets_set_insert_list(sets:to_list(Set), Table). + +ets_set_insert_list(List, Table) -> + true = ets:insert(Table, [{E} || E <- List]). + +ets_set_to_set(Table) -> + Fold = fun({E}, Set) -> sets:add_element(E, Set) end, + ets:foldl(Fold, sets:new(), Table). + +ets_read_concurrent_table(Name) -> + ets:new(Name,[{read_concurrency, true}]). + +%%-------------------------------------------------------------------- + -spec new() -> codeserver(). new() -> - #codeserver{table_pid = ets:new(dialyzer_codeserver, [private, compressed])}. + CodeOptions = [compressed, public, {read_concurrency, true}], + Code = ets:new(dialyzer_codeserver_code, CodeOptions), + TempOptions = [public, {write_concurrency, true}], + [Exports, TempExportedTypes, TempRecords, TempContracts, TempCallbacks] = + [ets:new(Name, TempOptions) || + Name <- + [dialyzer_codeserver_exports, dialyzer_codeserver_temp_exported_types, + dialyzer_codeserver_temp_records, dialyzer_codeserver_temp_contracts, + dialyzer_codeserver_temp_callbacks]], + #codeserver{code = Code, + exports = Exports, + temp_exported_types = TempExportedTypes, + temp_records = TempRecords, + temp_contracts = TempContracts, + temp_callbacks = TempCallbacks}. -spec delete(codeserver()) -> 'ok'. -delete(#codeserver{table_pid = TablePid}) -> - ets:delete(TablePid). +delete(#codeserver{code = Code, exported_types = ExportedTypes, + records = Records, contracts = Contracts, + callbacks = Callbacks}) -> + lists:foreach(fun ets:delete/1, + [Code, ExportedTypes, Records, Contracts, Callbacks]). -spec insert(atom(), cerl:c_module(), codeserver()) -> codeserver(). @@ -102,56 +159,61 @@ insert(Mod, ModCode, CS) -> [{{Mod, cerl:fname_id(Var), cerl:fname_arity(Var)}, Val} || Val = {Var, _Fun} <- Defs], Keys = [Key || {Key, _Value} <- Funs], - ets:insert(CS#codeserver.table_pid, - [{Mod, {Name, Exports, Attrs, Keys, As}} | Funs]), + ModEntry = {Mod, {Name, Exports, Attrs, Keys, As}}, + true = ets:insert(CS#codeserver.code, [ModEntry|Funs]), CS. +-spec get_temp_exported_types(codeserver()) -> set(). + +get_temp_exported_types(#codeserver{temp_exported_types = TempExpTypes}) -> + ets_set_to_set(TempExpTypes). + -spec insert_temp_exported_types(set(), codeserver()) -> codeserver(). insert_temp_exported_types(Set, CS) -> - CS#codeserver{temp_exported_types = Set}. + TempExportedTypes = CS#codeserver.temp_exported_types, + true = ets_set_insert_set(Set, TempExportedTypes), + CS. -spec insert_exports([mfa()], codeserver()) -> codeserver(). insert_exports(List, #codeserver{exports = Exports} = CS) -> - Set = sets:from_list(List), - NewExports = sets:union(Exports, Set), - CS#codeserver{exports = NewExports}. + true = ets_set_insert_list(List, Exports), + CS. -spec is_exported(mfa(), codeserver()) -> boolean(). is_exported(MFA, #codeserver{exports = Exports}) -> - sets:is_element(MFA, Exports). + ets_set_is_element(MFA, Exports). -spec get_exported_types(codeserver()) -> set(). % set(mfa()) get_exported_types(#codeserver{exported_types = ExpTypes}) -> - ExpTypes. - --spec get_temp_exported_types(codeserver()) -> set(). - -get_temp_exported_types(#codeserver{temp_exported_types = TempExpTypes}) -> - TempExpTypes. + ets_set_to_set(ExpTypes). -spec get_exports(codeserver()) -> set(). % set(mfa()) get_exports(#codeserver{exports = Exports}) -> - Exports. + ets_set_to_set(Exports). -spec finalize_exported_types(set(), codeserver()) -> codeserver(). finalize_exported_types(Set, CS) -> - CS#codeserver{exported_types = Set, temp_exported_types = sets:new()}. + ExportedTypes = ets_read_concurrent_table(dialyzer_codeserver_exported_types), + true = ets_set_insert_set(Set, ExportedTypes), + TempExpTypes = CS#codeserver.temp_exported_types, + true = ets:delete(TempExpTypes), + CS#codeserver{exported_types = ExportedTypes, temp_exported_types = clean}. -spec lookup_mod_code(atom(), codeserver()) -> cerl:c_module(). lookup_mod_code(Mod, CS) when is_atom(Mod) -> - table__lookup(CS#codeserver.table_pid, Mod). + table__lookup(CS#codeserver.code, Mod). -spec lookup_mfa_code(mfa(), codeserver()) -> {cerl:c_var(), cerl:c_fun()}. lookup_mfa_code({_M, _F, _A} = MFA, CS) -> - table__lookup(CS#codeserver.table_pid, MFA). + table__lookup(CS#codeserver.code, MFA). -spec get_next_core_label(codeserver()) -> label(). @@ -163,20 +225,10 @@ get_next_core_label(#codeserver{next_core_label = NCL}) -> set_next_core_label(NCL, CS) -> CS#codeserver{next_core_label = NCL}. --spec store_records(atom(), dict(), codeserver()) -> codeserver(). - -store_records(Mod, Dict, #codeserver{records = RecDict} = CS) - when is_atom(Mod) -> - case dict:size(Dict) =:= 0 of - true -> CS; - false -> CS#codeserver{records = dict:store(Mod, Dict, RecDict)} - end. - -spec lookup_mod_records(atom(), codeserver()) -> dict(). -lookup_mod_records(Mod, #codeserver{records = RecDict}) - when is_atom(Mod) -> - case dict:find(Mod, RecDict) of +lookup_mod_records(Mod, #codeserver{records = RecDict}) when is_atom(Mod) -> + case ets_dict_find(Mod, RecDict) of error -> dict:new(); {ok, Dict} -> Dict end. @@ -184,7 +236,7 @@ lookup_mod_records(Mod, #codeserver{records = RecDict}) -spec get_records(codeserver()) -> dict(). get_records(#codeserver{records = RecDict}) -> - RecDict. + ets_dict_to_dict(RecDict). -spec store_temp_records(atom(), dict(), codeserver()) -> codeserver(). @@ -192,59 +244,58 @@ store_temp_records(Mod, Dict, #codeserver{temp_records = TempRecDict} = CS) when is_atom(Mod) -> case dict:size(Dict) =:= 0 of true -> CS; - false -> CS#codeserver{temp_records = dict:store(Mod, Dict, TempRecDict)} + false -> CS#codeserver{temp_records = ets_dict_store(Mod, Dict, TempRecDict)} end. -spec get_temp_records(codeserver()) -> dict(). get_temp_records(#codeserver{temp_records = TempRecDict}) -> - TempRecDict. + ets_dict_to_dict(TempRecDict). -spec set_temp_records(dict(), codeserver()) -> codeserver(). set_temp_records(Dict, CS) -> - CS#codeserver{temp_records = Dict}. + true = ets:delete(CS#codeserver.temp_records), + TempRecords = ets:new(dialyzer_codeserver_temp_records,[]), + true = ets_dict_store_dict(Dict, TempRecords), + CS#codeserver{temp_records = TempRecords}. -spec finalize_records(dict(), codeserver()) -> codeserver(). finalize_records(Dict, CS) -> - CS#codeserver{records = Dict, temp_records = dict:new()}. - --spec store_contracts(atom(), dict(), codeserver()) -> codeserver(). - -store_contracts(Mod, Dict, #codeserver{contracts = C} = CS) when is_atom(Mod) -> - case dict:size(Dict) =:= 0 of - true -> CS; - false -> CS#codeserver{contracts = dict:store(Mod, Dict, C)} - end. + true = ets:delete(CS#codeserver.temp_records), + Records = ets_read_concurrent_table(dialyzer_codeserver_records), + true = ets_dict_store_dict(Dict, Records), + CS#codeserver{records = Records, temp_records = clean}. -spec lookup_mod_contracts(atom(), codeserver()) -> dict(). lookup_mod_contracts(Mod, #codeserver{contracts = ContDict}) when is_atom(Mod) -> - case dict:find(Mod, ContDict) of + case ets_dict_find(Mod, ContDict) of error -> dict:new(); - {ok, Dict} -> Dict + {ok, Keys} -> + dict:from_list([get_contract_pair(Key, ContDict)|| Key <- Keys]) end. +get_contract_pair(Key, ContDict) -> + {Key, ets:lookup_element(ContDict, Key, 2)}. + -spec lookup_mfa_contract(mfa(), codeserver()) -> 'error' | {'ok', dialyzer_contracts:file_contract()}. -lookup_mfa_contract({M,_F,_A} = MFA, #codeserver{contracts = ContDict}) -> - case dict:find(M, ContDict) of - error -> error; - {ok, Dict} -> dict:find(MFA, Dict) - end. +lookup_mfa_contract(MFA, #codeserver{contracts = ContDict}) -> + ets_dict_find(MFA, ContDict). -spec get_contracts(codeserver()) -> dict(). get_contracts(#codeserver{contracts = ContDict}) -> - ContDict. + ets_dict_to_dict(ContDict). --spec get_callbacks(codeserver()) -> dict(). +-spec get_callbacks(codeserver()) -> list(). get_callbacks(#codeserver{callbacks = CallbDict}) -> - CallbDict. + ets:tab2list(CallbDict). -spec store_temp_contracts(atom(), dict(), dict(), codeserver()) -> codeserver(). @@ -256,27 +307,40 @@ store_temp_contracts(Mod, SpecDict, CallbackDict, CS1 = case dict:size(SpecDict) =:= 0 of true -> CS; - false -> CS#codeserver{temp_contracts = dict:store(Mod, SpecDict, Cn)} + false -> + CS#codeserver{temp_contracts = ets_dict_store(Mod, SpecDict, Cn)} end, case dict:size(CallbackDict) =:= 0 of true -> CS1; - false -> CS1#codeserver{temp_callbacks = dict:store(Mod, CallbackDict, Cb)} + false -> + CS1#codeserver{temp_callbacks = ets_dict_store(Mod, CallbackDict, Cb)} end. -spec get_temp_contracts(codeserver()) -> {dict(), dict()}. get_temp_contracts(#codeserver{temp_contracts = TempContDict, temp_callbacks = TempCallDict}) -> - {TempContDict, TempCallDict}. + {ets_dict_to_dict(TempContDict), ets_dict_to_dict(TempCallDict)}. -spec finalize_contracts(dict(), dict(), codeserver()) -> codeserver(). -finalize_contracts(CnDict, CbDict, CS) -> - CS#codeserver{contracts = CnDict, - callbacks = CbDict, - temp_contracts = dict:new(), - temp_callbacks = dict:new() - }. +finalize_contracts(SpecDict, CallbackDict, CS) -> + Contracts = ets_read_concurrent_table(dialyzer_codeserver_contracts), + Callbacks = ets_read_concurrent_table(dialyzer_codeserver_callbacks), + Contracts = dict:fold(fun decompose_spec_dict/3, Contracts, SpecDict), + Callbacks = dict:fold(fun decompose_cb_dict/3, Callbacks, CallbackDict), + CS#codeserver{contracts = Contracts, callbacks = Callbacks, + temp_contracts = clean, temp_callbacks = clean}. + +decompose_spec_dict(Mod, Dict, Table) -> + Keys = dict:fetch_keys(Dict), + true = ets:insert(Table, dict:to_list(Dict)), + true = ets:insert(Table, {Mod, Keys}), + Table. + +decompose_cb_dict(_Mod, Dict, Table) -> + true = ets:insert(Table, dict:to_list(Dict)), + Table. table__lookup(TablePid, M) when is_atom(M) -> {Name, Exports, Attrs, Keys, As} = ets:lookup_element(TablePid, M, 2), diff --git a/lib/dialyzer/src/dialyzer_plt.erl b/lib/dialyzer/src/dialyzer_plt.erl index c973762df1..01d777d138 100644 --- a/lib/dialyzer/src/dialyzer_plt.erl +++ b/lib/dialyzer/src/dialyzer_plt.erl @@ -138,12 +138,7 @@ insert_contract_list(#plt{contracts = Contracts} = PLT, List) -> -spec insert_callbacks(plt(), dialyzer_codeserver:codeserver()) -> plt(). insert_callbacks(#plt{callbacks = Callbacks} = Plt, Codeserver) -> - FunPreferNew = fun(_Key, _Val1, Val2) -> Val2 end, - FunDictMerger = - fun(_Key, Value, AccIn) -> dict:merge(FunPreferNew, Value, AccIn) end, - MergedCallbacks = dict:fold(FunDictMerger, dict:new(), - dialyzer_codeserver:get_callbacks(Codeserver)), - List = dict:to_list(MergedCallbacks), + List = dialyzer_codeserver:get_callbacks(Codeserver), Plt#plt{callbacks = table_insert_list(Callbacks, List)}. -spec lookup_contract(plt(), mfa_patt()) -> 'none' | {'value', #contract{}}. -- cgit v1.2.3 From f912241953ecece658f429ffd09f77e2e49ffa4c Mon Sep 17 00:00:00 2001 From: Stavros Aronis Date: Thu, 9 Feb 2012 14:57:06 +0100 Subject: Success typing analysis uses ETS tables for necessary plt info Only the info and contracts dicts were neccessary for success typing inference and these have been converted to ETS tables. --- lib/dialyzer/src/dialyzer_plt.erl | 65 +++++++++++++++++++++++++----- lib/dialyzer/src/dialyzer_succ_typings.erl | 4 +- 2 files changed, 58 insertions(+), 11 deletions(-) (limited to 'lib/dialyzer') diff --git a/lib/dialyzer/src/dialyzer_plt.erl b/lib/dialyzer/src/dialyzer_plt.erl index 01d777d138..0c0773e977 100644 --- a/lib/dialyzer/src/dialyzer_plt.erl +++ b/lib/dialyzer/src/dialyzer_plt.erl @@ -55,7 +55,10 @@ plt_and_info_from_file/1, get_specs/1, get_specs/4, - to_file/4]). + to_file/4, + get_mini_plt/1, + restore_full_plt/2 + ]). %% Debug utilities -export([pp_non_returning/0, pp_mod/1]). @@ -82,7 +85,12 @@ contracts = table_new() :: dict(), callbacks = table_new() :: dict(), exported_types = sets:new() :: set()}). --opaque plt() :: #plt{}. + +-record(mini_plt, {info :: ets:tid(), + contracts :: ets:tid() + }). + +-opaque plt() :: #plt{} | #mini_plt{}. -include("dialyzer.hrl"). @@ -133,7 +141,10 @@ delete_list(#plt{info = Info, types = Types, -spec insert_contract_list(plt(), dialyzer_contracts:plt_contracts()) -> plt(). insert_contract_list(#plt{contracts = Contracts} = PLT, List) -> - PLT#plt{contracts = table_insert_list(Contracts, List)}. + PLT#plt{contracts = table_insert_list(Contracts, List)}; +insert_contract_list(#mini_plt{contracts = Contracts} = PLT, List) -> + true = ets:insert(Contracts, List), + PLT. -spec insert_callbacks(plt(), dialyzer_codeserver:codeserver()) -> plt(). @@ -145,7 +156,10 @@ insert_callbacks(#plt{callbacks = Callbacks} = Plt, Codeserver) -> lookup_contract(#plt{contracts = Contracts}, {M, F, _} = MFA) when is_atom(M), is_atom(F) -> - table_lookup(Contracts, MFA). + table_lookup(Contracts, MFA); +lookup_contract(#mini_plt{contracts = ETSContracts}, + {M, F, _} = MFA) when is_atom(M), is_atom(F) -> + ets_table_lookup(ETSContracts, MFA). -spec lookup_callbacks(plt(), module()) -> [{mfa(), {{Filename::string(), Line::pos_integer()}, #contract{}}}]. @@ -158,15 +172,23 @@ lookup_callbacks(#plt{callbacks = Callbacks}, Mod) when is_atom(Mod) -> -spec insert_list(plt(), [{mfa() | integer(), ret_args_types()}]) -> plt(). insert_list(#plt{info = Info} = PLT, List) -> - PLT#plt{info = table_insert_list(Info, List)}. + PLT#plt{info = table_insert_list(Info, List)}; +insert_list(#mini_plt{info = Info} = PLT, List) -> + true = ets:insert(Info, List), + PLT. -spec lookup(plt(), integer() | mfa_patt()) -> 'none' | {'value', ret_args_types()}. -lookup(#plt{info = Info}, {M, F, _} = MFA) when is_atom(M), is_atom(F) -> - table_lookup(Info, MFA); -lookup(#plt{info = Info}, Label) when is_integer(Label) -> - table_lookup(Info, Label). +lookup(Plt, {M, F, _} = MFA) when is_atom(M), is_atom(F) -> + lookup_1(Plt, MFA); +lookup(Plt, Label) when is_integer(Label) -> + lookup_1(Plt, Label). + +lookup_1(#plt{info = Info}, MFAorLabel) -> + table_lookup(Info, MFAorLabel); +lookup_1(#mini_plt{info = Info}, MFAorLabel) -> + ets_table_lookup(Info, MFAorLabel). -spec insert_types(plt(), dict()) -> plt(). @@ -493,6 +515,24 @@ init_md5_list_1([], DiffList, Acc) -> init_md5_list_1(Md5List, [], Acc) -> {ok, lists:reverse(Acc, Md5List)}. +-spec get_mini_plt(plt()) -> plt(). + +get_mini_plt(#plt{info = Info, contracts = Contracts}) -> + ETSInfo = ets:new(plt_info, [public]), + ETSContracts = ets:new(plt_contracts, [public]), + true = ets:insert(ETSInfo, dict:to_list(Info)), + true = ets:insert(ETSContracts, dict:to_list(Contracts)), + #mini_plt{info = ETSInfo, contracts = ETSContracts}. + +-spec restore_full_plt(plt(), plt()) -> plt(). + +restore_full_plt(#mini_plt{info = ETSInfo, contracts = ETSContracts}, Plt) -> + Info = dict:from_list(ets:tab2list(ETSInfo)), + Contracts = dict:from_list(ets:tab2list(ETSContracts)), + ets:delete(ETSContracts), + ets:delete(ETSInfo), + Plt#plt{info = Info, contracts = Contracts}. + %%--------------------------------------------------------------------------- %% Edoc @@ -585,6 +625,13 @@ table_lookup(Plt, Obj) -> {ok, Val} -> {value, Val} end. +ets_table_lookup(Plt, Obj) -> + try ets:lookup_element(Plt, Obj, 2) of + Val -> {value, Val} + catch + _:_ -> none + end. + table_lookup_module(Plt, Mod) -> List = dict:fold(fun(Key, Val, Acc) -> case Key of diff --git a/lib/dialyzer/src/dialyzer_succ_typings.erl b/lib/dialyzer/src/dialyzer_succ_typings.erl index 2c2c39951c..9e6702b484 100644 --- a/lib/dialyzer/src/dialyzer_succ_typings.erl +++ b/lib/dialyzer/src/dialyzer_succ_typings.erl @@ -75,10 +75,10 @@ analyze_callgraph(Callgraph, Plt, Codeserver) -> dialyzer_plt:plt(). analyze_callgraph(Callgraph, Plt, Codeserver, Parent) -> - State = #st{callgraph = Callgraph, plt = Plt, + State = #st{callgraph = Callgraph, plt = dialyzer_plt:get_mini_plt(Plt), codeserver = Codeserver, parent = Parent}, NewState = get_refined_success_typings(State), - NewState#st.plt. + dialyzer_plt:restore_full_plt(NewState#st.plt, Plt). %%-------------------------------------------------------------------- -- cgit v1.2.3 From e6fa01359a41d3b054260d01d2880820c867ca2b Mon Sep 17 00:00:00 2001 From: Stavros Aronis Date: Fri, 10 Feb 2012 14:42:30 +0100 Subject: Parallel typesig analysis --- lib/dialyzer/src/Makefile | 2 + lib/dialyzer/src/dialyzer.hrl | 1 + lib/dialyzer/src/dialyzer_analysis_callgraph.erl | 7 +- lib/dialyzer/src/dialyzer_callgraph.erl | 140 +++++++++----- lib/dialyzer/src/dialyzer_succ_typings.erl | 127 ++++++++----- lib/dialyzer/src/dialyzer_typesig_coordinator.erl | 215 ++++++++++++++++++++++ lib/dialyzer/src/dialyzer_typesig_worker.erl | 142 ++++++++++++++ 7 files changed, 538 insertions(+), 96 deletions(-) create mode 100644 lib/dialyzer/src/dialyzer_typesig_coordinator.erl create mode 100644 lib/dialyzer/src/dialyzer_typesig_worker.erl (limited to 'lib/dialyzer') diff --git a/lib/dialyzer/src/Makefile b/lib/dialyzer/src/Makefile index 04f3b844c4..2f1eaf5754 100644 --- a/lib/dialyzer/src/Makefile +++ b/lib/dialyzer/src/Makefile @@ -64,6 +64,8 @@ MODULES = \ dialyzer_races \ dialyzer_succ_typings \ dialyzer_typesig \ + dialyzer_typesig_coordinator \ + dialyzer_typesig_worker \ dialyzer_utils HRL_FILES= dialyzer.hrl dialyzer_gui_wx.hrl diff --git a/lib/dialyzer/src/dialyzer.hrl b/lib/dialyzer/src/dialyzer.hrl index 5e089d1773..44b1ebeabd 100644 --- a/lib/dialyzer/src/dialyzer.hrl +++ b/lib/dialyzer/src/dialyzer.hrl @@ -110,6 +110,7 @@ -type label() :: non_neg_integer(). -type rep_mode() :: 'quiet' | 'normal' | 'verbose'. -type start_from() :: 'byte_code' | 'src_code'. +-type mfa_or_funlbl() :: label() | mfa(). %%-------------------------------------------------------------------- %% Record declarations used by various files diff --git a/lib/dialyzer/src/dialyzer_analysis_callgraph.erl b/lib/dialyzer/src/dialyzer_analysis_callgraph.erl index 7221d81a72..7060028d17 100644 --- a/lib/dialyzer/src/dialyzer_analysis_callgraph.erl +++ b/lib/dialyzer/src/dialyzer_analysis_callgraph.erl @@ -179,22 +179,21 @@ analyze_callgraph(Callgraph, State) -> Parent = State#analysis_state.parent, DocPlt = State#analysis_state.doc_plt, Plt = dialyzer_plt:insert_callbacks(State#analysis_state.plt, Codeserver), - Callgraph1 = dialyzer_callgraph:finalize(Callgraph), {NewPlt, NewDocPlt} = case State#analysis_state.analysis_type of plt_build -> - {dialyzer_succ_typings:analyze_callgraph(Callgraph1, Plt, + {dialyzer_succ_typings:analyze_callgraph(Callgraph, Plt, Codeserver, Parent), DocPlt}; succ_typings -> NoWarn = State#analysis_state.no_warn_unused, {Warnings, NewPlt0, NewDocPlt0} = - dialyzer_succ_typings:get_warnings(Callgraph1, Plt, DocPlt, + dialyzer_succ_typings:get_warnings(Callgraph, Plt, DocPlt, Codeserver, NoWarn, Parent), send_warnings(State#analysis_state.parent, Warnings), {NewPlt0, NewDocPlt0} end, - dialyzer_callgraph:delete(Callgraph1), + dialyzer_callgraph:delete(Callgraph), State#analysis_state{plt = NewPlt, doc_plt = NewDocPlt}. %%-------------------------------------------------------------------- diff --git a/lib/dialyzer/src/dialyzer_callgraph.erl b/lib/dialyzer/src/dialyzer_callgraph.erl index 2a01b0f753..1cbb83a44a 100644 --- a/lib/dialyzer/src/dialyzer_callgraph.erl +++ b/lib/dialyzer/src/dialyzer_callgraph.erl @@ -43,12 +43,14 @@ %% module_postorder/1, module_postorder_from_funs/2, new/0, + mini_callgraph/1, + get_depends_on/2, + get_required_by/2, in_neighbours/2, renew_race_info/4, reset_from_funs/2, scan_core_tree/2, strip_module_deps/2, - take_scc/1, remove_external/1, to_dot/2, to_ps/3]). @@ -65,7 +67,6 @@ %%---------------------------------------------------------------------- --type mfa_or_funlbl() :: label() | mfa(). -type scc() :: [mfa_or_funlbl()]. -type mfa_calls() :: [{mfa_or_funlbl(), mfa_or_funlbl()}]. @@ -78,9 +79,6 @@ %% digraph - A digraph representing the callgraph. %% Nodes are represented as MFAs or labels. %% esc - A set of all escaping functions as reported by dialyzer_dep. -%% postorder - A list of strongly connected components of the callgraph -%% sorted in a topological bottom-up order. -%% This is produced by calling finalize/1. %% name_map - A mapping from label to MFA. %% rev_name_map - A reverse mapping of the name_map. %% rec_var_map - A dict mapping from letrec bound labels to function names. @@ -91,13 +89,13 @@ %%----------------------------------------------------------------------------- -record(callgraph, {digraph = digraph:new() :: digraph(), - esc = sets:new() :: set(), - name_map = dict:new() :: dict(), - rev_name_map = dict:new() :: dict(), - postorder = [] :: [scc()], - rec_var_map = dict:new() :: dict(), - self_rec = sets:new() :: set(), - calls = dict:new() :: dict(), + active_digraph :: digraph(), + esc = sets:new() :: set() | ets:tid(), + name_map = dict:new() :: dict() | ets:tid(), + rev_name_map = dict:new() :: dict() | ets:tid(), + rec_var_map = dict:new() :: dict() | ets:tid(), + self_rec = sets:new() :: set() | ets:tid(), + calls = dict:new() :: dict() | ets:tid(), race_code = dict:new() :: dict(), public_tables = [] :: [label()], named_tables = [] :: [string()], @@ -115,6 +113,25 @@ new() -> #callgraph{}. +-spec mini_callgraph(callgraph()) -> callgraph(). + +mini_callgraph(#callgraph{digraph = Digraph, + active_digraph = ActiveDigraph, + esc = Esc, + name_map = NameMap, + rev_name_map = RevNameMap, + rec_var_map = RecVarMap, + self_rec = SelfRecs, + calls = Calls}) -> + #callgraph{digraph = Digraph, + active_digraph = ActiveDigraph, + esc = Esc, + name_map = NameMap, + rev_name_map = RevNameMap, + rec_var_map = RecVarMap, + self_rec = SelfRecs, + calls = Calls}. + -spec delete(callgraph()) -> 'true'. delete(#callgraph{digraph = Digraph}) -> @@ -129,32 +146,32 @@ all_nodes(#callgraph{digraph = DG}) -> lookup_rec_var(Label, #callgraph{rec_var_map = RecVarMap}) when is_integer(Label) -> - dict:find(Label, RecVarMap). + ets_lookup_dict(Label, RecVarMap). -spec lookup_call_site(label(), callgraph()) -> 'error' | {'ok', [_]}. % XXX: refine lookup_call_site(Label, #callgraph{calls = Calls}) when is_integer(Label) -> - dict:find(Label, Calls). + ets_lookup_dict(Label, Calls). -spec lookup_name(label(), callgraph()) -> 'error' | {'ok', mfa()}. lookup_name(Label, #callgraph{name_map = NameMap}) when is_integer(Label) -> - dict:find(Label, NameMap). + ets_lookup_dict(Label, NameMap). -spec lookup_label(mfa_or_funlbl(), callgraph()) -> 'error' | {'ok', integer()}. lookup_label({_,_,_} = MFA, #callgraph{rev_name_map = RevNameMap}) -> - dict:find(MFA, RevNameMap); + ets_lookup_dict(MFA, RevNameMap); lookup_label(Label, #callgraph{}) when is_integer(Label) -> {ok, Label}. -spec in_neighbours(mfa_or_funlbl(), callgraph()) -> 'none' | [mfa_or_funlbl(),...]. -in_neighbours(Label, #callgraph{digraph = Digraph, name_map = NameMap}) +in_neighbours(Label, #callgraph{digraph = Digraph} = CG) when is_integer(Label) -> - Name = case dict:find(Label, NameMap) of + Name = case lookup_name(Label, CG) of {ok, Val} -> Val; error -> Label end, @@ -165,12 +182,12 @@ in_neighbours({_, _, _} = MFA, #callgraph{digraph = Digraph}) -> -spec is_self_rec(mfa_or_funlbl(), callgraph()) -> boolean(). is_self_rec(MfaOrLabel, #callgraph{self_rec = SelfRecs}) -> - sets:is_element(MfaOrLabel, SelfRecs). + ets_lookup_set(MfaOrLabel, SelfRecs). -spec is_escaping(label(), callgraph()) -> boolean(). is_escaping(Label, #callgraph{esc = Esc}) when is_integer(Label) -> - sets:is_element(Label, Esc). + ets_lookup_set(Label, Esc). -type callgraph_edge() :: {mfa_or_funlbl(),mfa_or_funlbl()}. -spec add_edges([callgraph_edge()], callgraph()) -> callgraph(). @@ -186,13 +203,6 @@ add_edges(Edges, MFAs, #callgraph{digraph = DG} = CG) -> DG = digraph_confirm_vertices(MFAs, DG), add_edges(Edges, CG). --spec take_scc(callgraph()) -> 'none' | {'ok', scc(), callgraph()}. - -take_scc(#callgraph{postorder = [SCC|SCCs]} = CG) -> - {ok, SCC, CG#callgraph{postorder = SCCs}}; -take_scc(#callgraph{postorder = []}) -> - none. - -spec remove_external(callgraph()) -> {callgraph(), [tuple()]}. remove_external(#callgraph{digraph = DG} = CG) -> @@ -229,6 +239,16 @@ renew_race_info(CG, RaceCode, PublicTables, NamedTables) -> public_tables = PublicTables, named_tables = NamedTables}. +-spec get_depends_on(scc(), callgraph()) -> [scc()]. + +get_depends_on(SCC, #callgraph{active_digraph = DG}) -> + digraph:out_neighbours(DG, SCC). + +-spec get_required_by(scc(), callgraph()) -> [scc()]. + +get_required_by(SCC, #callgraph{active_digraph = DG}) -> + digraph:in_neighbours(DG, SCC). + %%---------------------------------------------------------------------- %% Handling of modules & SCCs %%---------------------------------------------------------------------- @@ -282,18 +302,44 @@ create_module_digraph([{_, _}|Left], MDG) -> create_module_digraph([], MDG) -> MDG. --spec finalize(callgraph()) -> callgraph(). - -finalize(#callgraph{digraph = DG} = CG) -> - CG#callgraph{postorder = digraph_finalize(DG)}. - --spec reset_from_funs([mfa_or_funlbl()], callgraph()) -> callgraph(). - -reset_from_funs(Funs, #callgraph{digraph = DG} = CG) -> +-spec finalize(callgraph()) -> {[scc()], callgraph()}. + +finalize(#callgraph{digraph = DG, + esc = Esc, + name_map = NameMap, + rev_name_map = RevNameMap, + rec_var_map = RecVarMap, + self_rec = SelfRec, + calls = Calls + } = CG) -> + [ETSEsc, ETSNameMap, ETSRevNameMap, ETSRecVarMap, ETSSelfRec, ETSCalls] = + [ets:new(N,[public]) || + N <- [callgraph_esc, callgraph_name_map, callgraph_rev_name_map, + callgraph_rec_var_map, callgraph_self_rec, callgraph_calls]], + [true,true] = [ets:insert(ETS, [{E} || E <- sets:to_list(Data)]) || + {ETS, Data} <- [{ETSEsc, Esc}, {ETSSelfRec, SelfRec}]], + [true, true, true, true] = + [ets:insert(ETS, dict:to_list(Data)) || + {ETS, Data} <- [{ETSNameMap, NameMap}, {ETSRevNameMap, RevNameMap}, + {ETSRecVarMap, RecVarMap}, {ETSCalls, Calls}]], + {ActiveDG, Postorder} = digraph_finalize(DG), + {Postorder, CG#callgraph{active_digraph = ActiveDG, + esc = ETSEsc, + name_map = ETSNameMap, + rev_name_map = ETSRevNameMap, + rec_var_map = ETSRecVarMap, + self_rec = ETSSelfRec, + calls = ETSCalls}}. + +-spec reset_from_funs([mfa_or_funlbl()], callgraph()) -> {[scc()], callgraph()}. + +reset_from_funs(Funs, #callgraph{digraph = DG, + active_digraph = OldActiveDG} = CG) -> + digraph_delete(OldActiveDG), SubGraph = digraph_reaching_subgraph(Funs, DG), - Postorder = digraph_finalize(SubGraph), + {NewActiveDG, Postorder} = digraph_finalize(SubGraph), digraph_delete(SubGraph), - CG#callgraph{postorder = Postorder}. + {Postorder, CG#callgraph{active_digraph = NewActiveDG}}. -spec module_postorder_from_funs([mfa_or_funlbl()], callgraph()) -> [module()]. @@ -302,7 +348,20 @@ module_postorder_from_funs(Funs, #callgraph{digraph = DG} = CG) -> PO = module_postorder(CG#callgraph{digraph = SubGraph}), digraph_delete(SubGraph), PO. - + +ets_lookup_dict(Key, Table) -> + try ets:lookup_element(Table, Key, 2) of + Val -> {ok, Val} + catch + _:_ -> error + end. + +ets_lookup_set(Key, Table) -> + case ets:lookup(Table, Key) of + [] -> false; + _ -> true + end. + %%---------------------------------------------------------------------- %% Core code %%---------------------------------------------------------------------- @@ -516,13 +575,12 @@ digraph_in_neighbours(V, DG) -> end. digraph_postorder(Digraph) -> - digraph_utils:postorder(Digraph). + digraph_utils:topsort(Digraph). digraph_finalize(DG) -> DG1 = digraph_utils:condensation(DG), Postorder = digraph_postorder(DG1), - digraph:delete(DG1), - Postorder. + {DG1, Postorder}. digraph_reaching_subgraph(Funs, DG) -> Vertices = digraph_utils:reaching(Funs, DG), diff --git a/lib/dialyzer/src/dialyzer_succ_typings.erl b/lib/dialyzer/src/dialyzer_succ_typings.erl index 9e6702b484..af935f54f7 100644 --- a/lib/dialyzer/src/dialyzer_succ_typings.erl +++ b/lib/dialyzer/src/dialyzer_succ_typings.erl @@ -29,7 +29,12 @@ -export([analyze_callgraph/3, analyze_callgraph/4, - get_warnings/6]). + get_warnings/6, + find_succ_types_for_scc/1, + collect_scc_data/2, + find_required_by/2, + find_depends_on/2 + ]). %% These are only intended as debug functions. -export([doit/1, @@ -75,15 +80,20 @@ analyze_callgraph(Callgraph, Plt, Codeserver) -> dialyzer_plt:plt(). analyze_callgraph(Callgraph, Plt, Codeserver, Parent) -> - State = #st{callgraph = Callgraph, plt = dialyzer_plt:get_mini_plt(Plt), - codeserver = Codeserver, parent = Parent}, - NewState = get_refined_success_typings(State), + NewState = + init_state_and_get_success_typings(Callgraph, Plt, Codeserver, Parent), dialyzer_plt:restore_full_plt(NewState#st.plt, Plt). %%-------------------------------------------------------------------- -get_refined_success_typings(State) -> - case find_succ_typings(State) of +init_state_and_get_success_typings(Callgraph, Plt, Codeserver, Parent) -> + {SCCs, Callgraph1} = dialyzer_callgraph:finalize(Callgraph), + State = #st{callgraph = Callgraph1, plt = dialyzer_plt:get_mini_plt(Plt), + codeserver = Codeserver, parent = Parent}, + get_refined_success_typings(SCCs, State). + +get_refined_success_typings(SCCs, State) -> + case find_succ_typings(SCCs, State) of {fixpoint, State1} -> State1; {not_fixpoint, NotFixpoint1, State1} -> Callgraph = State1#st.callgraph, @@ -97,9 +107,10 @@ get_refined_success_typings(State) -> Callgraph1 = State2#st.callgraph, %% Need to reset the callgraph. NotFixpoint4 = [lookup_name(F, Callgraph1) || F <- NotFixpoint3], - Callgraph2 = dialyzer_callgraph:reset_from_funs(NotFixpoint4, - Callgraph1), - get_refined_success_typings(State2#st{callgraph = Callgraph2}) + {NewSCCs, Callgraph2} = + dialyzer_callgraph:reset_from_funs(NotFixpoint4, Callgraph1), + NewState = State2#st{callgraph = Callgraph2}, + get_refined_success_typings(NewSCCs, NewState) end end. @@ -110,9 +121,11 @@ get_refined_success_typings(State) -> {[dial_warning()], dialyzer_plt:plt(), doc_plt()}. get_warnings(Callgraph, Plt, DocPlt, Codeserver, NoWarnUnused, Parent) -> - InitState = #st{callgraph = Callgraph, codeserver = Codeserver, - no_warn_unused = NoWarnUnused, parent = Parent, plt = Plt}, - NewState = get_refined_success_typings(InitState), + InitState = + init_state_and_get_success_typings(Callgraph, Plt, Codeserver, Parent), + NewState = + InitState#st{no_warn_unused = NoWarnUnused, + plt = dialyzer_plt:restore_full_plt(InitState#st.plt, Plt)}, Mods = dialyzer_callgraph:modules(NewState#st.callgraph), CWarns = dialyzer_contracts:get_invalid_contract_warnings(Mods, Codeserver, NewState#st.plt), @@ -213,8 +226,8 @@ refine_one_module(M, State) -> {State1, ordsets:new()}; {false, NotFixpoint} -> ?debug("Not fixpoint\n", []), - NewState = insert_into_plt(dict:from_list(NotFixpoint), State), - NewState1 = st__renew_state_calls(NewCallgraph, NewState), + NewPlt = insert_into_plt(dict:from_list(NotFixpoint), Callgraph, PLT), + NewState1 = st__renew_state_calls(NewCallgraph, State#st{plt = NewPlt}), {NewState1, ordsets:from_list([FunLbl || {FunLbl,_Type} <- NotFixpoint])} end. @@ -276,31 +289,44 @@ compare_types_1([], [], _Strict, NotFixpoint) -> false -> {false, NotFixpoint} end. -find_succ_typings(State) -> - find_succ_typings(State, []). - -find_succ_typings(#st{callgraph = Callgraph, parent = Parent} = State, - NotFixpoint) -> - case dialyzer_callgraph:take_scc(Callgraph) of - {ok, SCC, NewCallgraph} -> - Msg = io_lib:format("Typesig analysis for SCC: ~w\n", [format_scc(SCC)]), - ?debug("~s", [Msg]), - send_log(Parent, Msg), - {NewState, NewNotFixpoint1} = - analyze_scc(SCC, State#st{callgraph = NewCallgraph}), - NewNotFixpoint2 = ordsets:union(NewNotFixpoint1, NotFixpoint), - find_succ_typings(NewState, NewNotFixpoint2); - none -> - ?debug("==================== Typesig done ====================\n\n", []), - case NotFixpoint =:= [] of - true -> {fixpoint, State}; - false -> {not_fixpoint, NotFixpoint, State} - end +find_succ_typings(SCCs, #st{codeserver = Codeserver, callgraph = Callgraph, + plt = Plt} = State) -> + Servers = {Codeserver, dialyzer_callgraph:mini_callgraph(Callgraph), Plt}, + Coordinator = dialyzer_typesig_coordinator:start(Servers), + find_succ_typings(SCCs, State, Coordinator). + +find_succ_typings([SCC|Rest], #st{parent = Parent} = State, Coordinator) -> + Msg = io_lib:format("Typesig analysis for SCC: ~w\n", [format_scc(SCC)]), + ?debug("~s", [Msg]), + send_log(Parent, Msg), + dialyzer_typesig_coordinator:scc_spawn(SCC, Coordinator), + find_succ_typings(Rest, State, Coordinator); +find_succ_typings([], State, Coordinator) -> + dialyzer_typesig_coordinator:all_spawned(Coordinator), + NotFixpoint = dialyzer_typesig_coordinator:receive_not_fixpoint(), + ?debug("==================== Typesig done ====================\n\n", []), + case NotFixpoint =:= [] of + true -> {fixpoint, State}; + false -> {not_fixpoint, NotFixpoint, State} end. -analyze_scc(SCC, #st{codeserver = Codeserver, - callgraph = Callgraph, - plt = Plt} = State) -> +-type servers() :: term(). +-type scc_data() :: term(). +-type scc() :: [mfa_or_funlbl()]. + +-spec find_depends_on(scc(), servers()) -> [scc()]. + +find_depends_on(SCC, {_Codeserver, Callgraph, _Plt}) -> + dialyzer_callgraph:get_depends_on(SCC, Callgraph). + +-spec find_required_by(scc(), servers()) -> [scc()]. + +find_required_by(SCC, {_Codeserver, Callgraph, _Plt}) -> + dialyzer_callgraph:get_required_by(SCC, Callgraph). + +-spec collect_scc_data(scc(), servers()) -> scc_data(). + +collect_scc_data(SCC, {Codeserver, Callgraph, Plt}) -> SCC_Info = [{MFA, dialyzer_codeserver:lookup_mfa_code(MFA, Codeserver), dialyzer_codeserver:lookup_mod_records(M, Codeserver)} @@ -310,16 +336,15 @@ analyze_scc(SCC, #st{codeserver = Codeserver, Contracts2 = [{MFA, Contract} || {MFA, {ok, Contract}} <- Contracts1], Contracts3 = orddict:from_list(Contracts2), NextLabel = dialyzer_codeserver:get_next_core_label(Codeserver), - {SuccTypes, PltContracts, NotFixpoint} = - find_succ_types_for_scc(SCC_Info, Contracts3, NextLabel, Callgraph, Plt), - State1 = insert_into_plt(SuccTypes, State), - ContrPlt = dialyzer_plt:insert_contract_list(State1#st.plt, PltContracts), - {State1#st{plt = ContrPlt}, NotFixpoint}. - -find_succ_types_for_scc(SCC_Info, Contracts, NextLabel, Callgraph, Plt) -> - %% Assume that the PLT contains the current propagated types AllFuns = collect_fun_info([Fun || {_MFA, {_Var, Fun}, _Rec} <- SCC_Info]), PropTypes = get_fun_types_from_plt(AllFuns, Callgraph, Plt), + {SCC_Info, Contracts3, NextLabel, AllFuns, PropTypes, Callgraph, Plt}. + +-spec find_succ_types_for_scc(scc_data()) -> [mfa_or_funlbl()]. + +find_succ_types_for_scc({SCC_Info, Contracts, NextLabel, AllFuns, + PropTypes, Callgraph, Plt}) -> + %% Assume that the PLT contains the current propagated types FunTypes = dialyzer_typesig:analyze_scc(SCC_Info, NextLabel, Callgraph, Plt, PropTypes), AllFunSet = sets:from_list([X || {X, _} <- AllFuns]), @@ -337,14 +362,14 @@ find_succ_types_for_scc(SCC_Info, Contracts, NextLabel, Callgraph, Plt) -> {value, _} -> true end end, PltContracts), + insert_into_plt(FilteredFunTypes, Callgraph, Plt), + dialyzer_plt:insert_contract_list(Plt, PltContracts), case (ContractFixpoint andalso reached_fixpoint_strict(PropTypes, FilteredFunTypes)) of - true -> - {FilteredFunTypes, PltContracts, []}; + true -> []; false -> ?debug("Not fixpoint for: ~w\n", [AllFuns]), - {FilteredFunTypes, PltContracts, - ordsets:from_list([Fun || {Fun, _Arity} <- AllFuns])} + ordsets:from_list([Fun || {Fun, _Arity} <- AllFuns]) end. get_fun_types_from_plt(FunList, Callgraph, Plt) -> @@ -384,10 +409,10 @@ insert_into_doc_plt(FunTypes, Callgraph, DocPlt) -> SuccTypes = format_succ_types(FunTypes, Callgraph), dialyzer_plt:insert_list(DocPlt, SuccTypes). -insert_into_plt(SuccTypes0, #st{callgraph = Callgraph, plt = Plt} = State) -> +insert_into_plt(SuccTypes0, Callgraph, Plt) -> SuccTypes = format_succ_types(SuccTypes0, Callgraph), debug_pp_succ_typings(SuccTypes), - State#st{plt = dialyzer_plt:insert_list(Plt, SuccTypes)}. + dialyzer_plt:insert_list(Plt, SuccTypes). format_succ_types(SuccTypes, Callgraph) -> format_succ_types(dict:to_list(SuccTypes), Callgraph, []). diff --git a/lib/dialyzer/src/dialyzer_typesig_coordinator.erl b/lib/dialyzer/src/dialyzer_typesig_coordinator.erl new file mode 100644 index 0000000000..9475bc6895 --- /dev/null +++ b/lib/dialyzer/src/dialyzer_typesig_coordinator.erl @@ -0,0 +1,215 @@ +%% -*- erlang-indent-level: 2 -*- +%%----------------------------------------------------------------------- +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2006-2011. All Rights Reserved. +%% +%% The contents of this file are subject to the Erlang Public License, +%% Version 1.1, (the "License"); you may not use this file except in +%% compliance with the License. You should have received a copy of the +%% Erlang Public License along with this software. If not, it can be +%% retrieved online at http://www.erlang.org/. +%% +%% Software distributed under the License is distributed on an "AS IS" +%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +%% the License for the specific language governing rights and limitations +%% under the License. +%% +%% %CopyrightEnd% +%% + +%%%------------------------------------------------------------------- +%%% File : dialyzer_typesig_coordinator.erl +%%% Authors : Stavros Aronis +%%% +%%% Description: +%%% +%%% The parallel version of Dialyzer's typesig analysis is spread over 4 modules +%%% with the intention to both minimize the changes on the original code and use +%%% a separate module for every kind of Erlang process that will be running. +%%% +%%% There are therefore 3 kinds of processes: +%%% +%%% - The original Dialyzer backend (in succ_typings module) +%%% - The worker process for the typesig analysis (in typesig and +%%% typesig_worker) +%%% - A coordinator of the worker processes (in typesig_coordinator) +%%% +%%% Operation guidelines: +%%% +%%% - The backend requests from the coordinator to spawn a worker for each SCC +%%% - The backend notifies the coordinator when all SCC have been spawned and +%%% waits for the server to report that the PLT has been updated +%%% - Each worker is responsible to notify all those who wait for it. +%%% +%%%------------------------------------------------------------------- + +-module(dialyzer_typesig_coordinator). + +-export([ + all_spawned/1, + scc_done/3, + scc_spawn/2, + sccs_to_pids_reply/0, + sccs_to_pids_request/2, + start/1, + receive_not_fixpoint/0 + ]). + +-behaviour(gen_server). + +-export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, + code_change/3]). + +-type coordinator() :: pid(). +-type map() :: dict(). +-type scc() :: [mfa_or_funlbl()]. + +-record(state, {parent :: pid(), + spawn_count = 0 :: integer(), + all_spawned = false :: boolean(), + scc_to_pid = new_map() :: map(), + not_fixpoint = [] :: [mfa_or_funlbl()], + servers :: dialyzer_typesig:servers() + }). + +-include("dialyzer.hrl"). + +%%-------------------------------------------------------------------- + +-spec start(dialyzer_typesig:servers()) -> pid(). + +start(Servers) -> + {ok, Pid} = gen_server:start(?MODULE, {self(), Servers}, []), + Pid. + +-spec scc_spawn(scc(), coordinator()) -> ok. + +scc_spawn(SCC, Coordinator) -> + cast({scc_spawn, SCC}, Coordinator). + +-spec sccs_to_pids_request([scc()], coordinator()) -> ok. + +sccs_to_pids_request(SCCs, Coordinator) -> + cast({sccs_to_pids, SCCs, self()}, Coordinator). + +scc_to_pids_request_handle(Worker, SCCs, SCCtoPID) -> + Pids = [fetch_map(SCC, SCCtoPID) || SCC <- SCCs], + Worker ! {sccs_to_pids, Pids}, + ok. + +-spec sccs_to_pids_reply() -> [dialyzer_typesig_worker:worker()]. + +sccs_to_pids_reply() -> + receive {sccs_to_pids, Pids} -> Pids end. + +-spec scc_done(scc(), scc(), coordinator()) -> ok. + +scc_done(SCC, NotFixpoint, Coordinator) -> + cast({scc_done, SCC, NotFixpoint}, Coordinator). + +-spec all_spawned(coordinator()) -> ok. + +all_spawned(Coordinator) -> + cast(all_spawned, Coordinator). + +send_done_to_parent(#state{parent = Parent, not_fixpoint = NotFixpoint}) -> + Parent ! {not_fixpoint, NotFixpoint}. + +-spec receive_not_fixpoint() -> dialyzer_plt:plt(). + +receive_not_fixpoint() -> + receive {not_fixpoint, NotFixpoint} -> NotFixpoint end. + +%%-------------------------------------------------------------------- + +-spec init([]) -> {ok, #state{}}. + +init({Parent, Servers}) -> + {ok, #state{parent = Parent, servers = Servers}}. + +-spec handle_call(Query::term(), From::term(), #state{}) -> + {reply, Reply::term(), #state{}}. + +handle_call(_Request, _From, State) -> + {reply, ok, State}. + +-spec handle_cast(Msg::term(), #state{}) -> + {noreply, #state{}} | {stop, normal, #state{}}. + +handle_cast({scc_done, _SCC, NotFixpoint}, + #state{spawn_count = SpawnCount, + all_spawned = AllSpawned, + not_fixpoint = OldNotFixpoint + } = State) -> + NewNotFixpoint = ordsets:union(OldNotFixpoint, NotFixpoint), + UpdatedState = State#state{not_fixpoint = NewNotFixpoint}, + Action = + case AllSpawned of + false -> reduce; + true -> + case SpawnCount of + 1 -> finish; + _ -> reduce + end + end, + case Action of + reduce -> + NewState = UpdatedState#state{spawn_count = SpawnCount - 1}, + {noreply, NewState}; + finish -> + send_done_to_parent(UpdatedState), + {stop, normal, State} + end; +handle_cast(all_spawned, #state{spawn_count = SpawnCount} = State) -> + case SpawnCount of + 0 -> + send_done_to_parent(State), + {stop, normal, State}; + _ -> + NewState = State#state{all_spawned = true}, + {noreply, NewState} + end; +handle_cast({sccs_to_pids, SCCs, Worker}, + #state{scc_to_pid = SCCtoPID} = State) -> + scc_to_pids_request_handle(Worker, SCCs, SCCtoPID), + {noreply, State}; +handle_cast({scc_spawn, SCC}, + #state{servers = Servers, + spawn_count = SpawnCount, + scc_to_pid = SCCtoPID + } = State) -> + Pid = dialyzer_typesig_worker:launch(SCC, Servers), + {noreply, + State#state{spawn_count = SpawnCount + 1, + scc_to_pid = store_map(SCC, Pid, SCCtoPID)} + }. + +-spec handle_info(term(), #state{}) -> {noreply, #state{}}. + +handle_info(_Info, State) -> + {noreply, State}. + +-spec terminate(term(), #state{}) -> ok. + +terminate(_Reason, _State) -> + ok. + +-spec code_change(term(), #state{}, term()) -> {ok, #state{}}. + +code_change(_OldVsn, State, _Extra) -> + {ok, State}. + +%%-------------------------------------------------------------------- + +cast(Message, Coordinator) -> + gen_server:cast(Coordinator, Message). + +new_map() -> + dict:new(). + +store_map(Key, Value, Map) -> + dict:store(Key, Value, Map). + +fetch_map(Key, Map) -> + dict:fetch(Key, Map). diff --git a/lib/dialyzer/src/dialyzer_typesig_worker.erl b/lib/dialyzer/src/dialyzer_typesig_worker.erl new file mode 100644 index 0000000000..3a7cd22eed --- /dev/null +++ b/lib/dialyzer/src/dialyzer_typesig_worker.erl @@ -0,0 +1,142 @@ +%% -*- erlang-indent-level: 2 -*- +%%----------------------------------------------------------------------- +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2006-2010. All Rights Reserved. +%% +%% The contents of this file are subject to the Erlang Public License, +%% Version 1.1, (the "License"); you may not use this file except in +%% compliance with the License. You should have received a copy of the +%% Erlang Public License along with this software. If not, it can be +%% retrieved online at http://www.erlang.org/. +%% +%% Software distributed under the License is distributed on an "AS IS" +%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +%% the License for the specific language governing rights and limitations +%% under the License. +%% +%% %CopyrightEnd% +%% + +-module(dialyzer_typesig_worker). + +-export([launch/2]). + +-type worker() :: pid(). + +-record(state, { + scc = [] :: mfa_or_funlbl(), + depends_on = [] :: list(), + coordinator :: dialyzer_coordinator:coordinator(), + servers :: dialyzer_typesig:servers(), + scc_data :: dialyzer_typesig:scc_data() + }). + +-include("dialyzer.hrl"). + +%% -define(DEBUG, true). + +-ifdef(DEBUG). +-define(debug(X__, Y__), io:format(X__, Y__)). +-else. +-define(debug(X__, Y__), ok). +-endif. + +%%-------------------------------------------------------------------- + +-spec launch([mfa_or_funlbl()], dialyzer_typesig:servers()) -> worker(). + +launch(SCC, Servers) -> + State = #state{scc = SCC, + servers = Servers, + coordinator = self()}, + spawn(fun() -> loop(initializing, State) end). + +%%-------------------------------------------------------------------- + +loop(updating, State) -> + ?debug("Update: ~p\n",[State#state.scc]), + NextStatus = + case waits_more_success_typings(State) of + true -> waiting; + Other -> + case has_data(State) of + false -> getting_data; + true -> + case Other of + imminent -> waiting; + false -> running + end + end + end, + loop(NextStatus, State); +loop(initializing, #state{scc = SCC, servers = Servers} = State) -> + DependsOn = dialyzer_succ_typings:find_depends_on(SCC, Servers), + WithoutSelf = DependsOn -- [SCC], + ?debug("Deps ~p: ~p\n",[State#state.scc, WithoutSelf]), + loop(updating, State#state{depends_on = WithoutSelf}); +loop(waiting, State) -> + ?debug("Wait: ~p\n",[State#state.scc]), + NewState = wait_for_success_typings(State), + loop(updating, NewState); +loop(getting_data, State) -> + ?debug("Data: ~p\n",[State#state.scc]), + loop(updating, get_typesig_data(State)); +loop(running, State) -> + ?debug("Run: ~p\n",[State#state.scc]), + ok = ask_coordinator_for_callers(State), + NotFixpoint = find_succ_typings(State), + Callers = get_callers_reply_from_coordinator(), + ok = broadcast_own_succ_typings(State, Callers), + report_to_coordinator(NotFixpoint, State). + +waits_more_success_typings(#state{depends_on = Depends}) -> + case Depends of + [] -> false; + [_] -> imminent; + _ -> true + end. + +has_data(#state{scc_data = Data}) -> + case Data of + undefined -> false; + _ -> true + end. + +get_typesig_data(#state{scc = SCC, servers = Servers} = State) -> + State#state{scc_data = dialyzer_succ_typings:collect_scc_data(SCC, Servers)}. + +ask_coordinator_for_callers(#state{scc = SCC, + servers = Servers, + coordinator = Coordinator}) -> + RequiredBy = dialyzer_succ_typings:find_required_by(SCC, Servers), + WithoutSelf = RequiredBy -- [SCC], + ?debug("Waiting for me~p: ~p\n",[SCC, WithoutSelf]), + dialyzer_typesig_coordinator:sccs_to_pids_request(WithoutSelf, Coordinator). + +get_callers_reply_from_coordinator() -> + dialyzer_typesig_coordinator:sccs_to_pids_reply(). + +broadcast_own_succ_typings(#state{scc = SCC}, Callers) -> + ?debug("Sending ~p: ~p\n",[SCC, Callers]), + SendSTFun = fun(PID) -> PID ! {done, SCC} end, + lists:foreach(SendSTFun, Callers). + +wait_for_success_typings(#state{depends_on = DependsOn} = State) -> + receive + {done, SCC} -> + ?debug("GOT ~p: ~p\n",[State#state.scc, SCC]), + State#state{depends_on = DependsOn -- [SCC]} + after + 5000 -> + ?debug("Still Waiting ~p: ~p\n",[State#state.scc, DependsOn]), + State + end. + +find_succ_typings(#state{scc_data = SCCData}) -> + dialyzer_succ_typings:find_succ_types_for_scc(SCCData). + +report_to_coordinator(NotFixpoint, + #state{scc = SCC, coordinator = Coordinator}) -> + ?debug("Done: ~p\n",[SCC]), + dialyzer_typesig_coordinator:scc_done(SCC, NotFixpoint, Coordinator). -- cgit v1.2.3 From ffadf7f6f8a113fbcc42a2be36d2dd5c841d48e3 Mon Sep 17 00:00:00 2001 From: Stavros Aronis Date: Thu, 16 Feb 2012 16:03:51 +0100 Subject: Moving code between callgraph & dataflow --- lib/dialyzer/src/dialyzer_callgraph.erl | 9 +++++++++ lib/dialyzer/src/dialyzer_dataflow.erl | 21 ++++++--------------- 2 files changed, 15 insertions(+), 15 deletions(-) (limited to 'lib/dialyzer') diff --git a/lib/dialyzer/src/dialyzer_callgraph.erl b/lib/dialyzer/src/dialyzer_callgraph.erl index 1cbb83a44a..ddda27adb4 100644 --- a/lib/dialyzer/src/dialyzer_callgraph.erl +++ b/lib/dialyzer/src/dialyzer_callgraph.erl @@ -48,6 +48,7 @@ get_required_by/2, in_neighbours/2, renew_race_info/4, + renew_race_code/2, reset_from_funs/2, scan_core_tree/2, strip_module_deps/2, @@ -239,6 +240,14 @@ renew_race_info(CG, RaceCode, PublicTables, NamedTables) -> public_tables = PublicTables, named_tables = NamedTables}. +-spec renew_race_code(dialyzer_races:races(), callgraph()) -> callgraph(). + +renew_race_code(Races, #callgraph{race_code = RaceCode} = Callgraph) -> + Fun = dialyzer_races:get_curr_fun(Races), + FunArgs = dialyzer_races:get_curr_fun_args(Races), + Code = lists:reverse(dialyzer_races:get_race_list(Races)), + Callgraph#callgraph{race_code = dict:store(Fun, [FunArgs, Code], RaceCode)}. + -spec get_depends_on(scc(), callgraph()) -> [scc()]. get_depends_on(SCC, #callgraph{active_digraph = DG}) -> diff --git a/lib/dialyzer/src/dialyzer_dataflow.erl b/lib/dialyzer/src/dialyzer_dataflow.erl index bd375b04fa..ad07524fa6 100644 --- a/lib/dialyzer/src/dialyzer_dataflow.erl +++ b/lib/dialyzer/src/dialyzer_dataflow.erl @@ -358,16 +358,7 @@ analyze_loop(#state{callgraph = Callgraph, races = Races} = State) -> t_to_string(t_fun(ArgTypes, BodyType))]), NewState5 = case RaceDetection andalso RaceAnalysis of - true -> - Races1 = NewState4#state.races, - Code = lists:reverse(dialyzer_races:get_race_list(Races1)), - Callgraph1 = - renew_code(dialyzer_races:get_curr_fun(Races1), - dialyzer_races:get_curr_fun_args(Races1), - Code, - state__warning_mode(NewState4), - NewState4#state.callgraph), - NewState4#state{callgraph = Callgraph1}; + true -> renew_race_code(NewState4); false -> NewState4 end, NewState6 = @@ -3342,13 +3333,13 @@ state__records_only(#state{records = Records}) -> %%% %%% =========================================================================== -renew_code(Fun, FunArgs, Code, WarningMode, Callgraph) -> +renew_race_code(#state{races = Races, callgraph = Callgraph, + warning_mode = WarningMode} = State) -> case WarningMode of - true -> Callgraph; + true -> State; false -> - RaceCode = dialyzer_callgraph:get_race_code(Callgraph), - dialyzer_callgraph:put_race_code( - dict:store(Fun, [FunArgs, Code], RaceCode), Callgraph) + NewCallgraph = dialyzer_callgraph:renew_race_code(Races, Callgraph), + State#state{callgraph = NewCallgraph} end. renew_public_tables([Var], Table, WarningMode, Callgraph) -> -- cgit v1.2.3 From f9d9c9d12a30728de8551c982d9d07550ba4e4c1 Mon Sep 17 00:00:00 2001 From: Stavros Aronis Date: Wed, 15 Feb 2012 17:43:31 +0100 Subject: Refactorings in succ_typings --- lib/dialyzer/src/dialyzer_succ_typings.erl | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'lib/dialyzer') diff --git a/lib/dialyzer/src/dialyzer_succ_typings.erl b/lib/dialyzer/src/dialyzer_succ_typings.erl index af935f54f7..740081a33f 100644 --- a/lib/dialyzer/src/dialyzer_succ_typings.erl +++ b/lib/dialyzer/src/dialyzer_succ_typings.erl @@ -213,22 +213,22 @@ refine_one_module(M, State) -> #st{callgraph = Callgraph, codeserver = CodeServer, plt = PLT} = State, ModCode = dialyzer_codeserver:lookup_mod_code(M, CodeServer), AllFuns = collect_fun_info([ModCode]), - FunTypes = get_fun_types_from_plt(AllFuns, Callgraph, PLT), Records = dialyzer_codeserver:lookup_mod_records(M, CodeServer), {NewFunTypes, RaceCode, PublicTables, NamedTables} = dialyzer_dataflow:get_fun_types(ModCode, PLT, Callgraph, Records), NewCallgraph = dialyzer_callgraph:renew_race_info(Callgraph, RaceCode, PublicTables, NamedTables), + FunTypes = get_fun_types_from_plt(AllFuns, Callgraph, PLT), + State1 = st__renew_state_calls(NewCallgraph, State), case reached_fixpoint(FunTypes, NewFunTypes) of true -> - State1 = st__renew_state_calls(NewCallgraph, State), {State1, ordsets:new()}; {false, NotFixpoint} -> ?debug("Not fixpoint\n", []), NewPlt = insert_into_plt(dict:from_list(NotFixpoint), Callgraph, PLT), - NewState1 = st__renew_state_calls(NewCallgraph, State#st{plt = NewPlt}), - {NewState1, ordsets:from_list([FunLbl || {FunLbl,_Type} <- NotFixpoint])} + {State1#st{plt = NewPlt}, + ordsets:from_list([FunLbl || {FunLbl,_Type} <- NotFixpoint])} end. st__renew_state_calls(Callgraph, State) -> -- cgit v1.2.3 From 0ac8c2e3c9c9a6910a170c2a98319400afc25959 Mon Sep 17 00:00:00 2001 From: Stavros Aronis Date: Thu, 16 Feb 2012 11:12:07 +0100 Subject: Simplification in dataflow --- lib/dialyzer/src/dialyzer_dataflow.erl | 31 ++++++++++--------------------- 1 file changed, 10 insertions(+), 21 deletions(-) (limited to 'lib/dialyzer') diff --git a/lib/dialyzer/src/dialyzer_dataflow.erl b/lib/dialyzer/src/dialyzer_dataflow.erl index ad07524fa6..a72116d1f2 100644 --- a/lib/dialyzer/src/dialyzer_dataflow.erl +++ b/lib/dialyzer/src/dialyzer_dataflow.erl @@ -272,46 +272,39 @@ analyze_module(Tree, Plt, Callgraph, Records, GetWarnings) -> debug_pp(Tree, false), Module = cerl:atom_val(cerl:module_name(Tree)), RaceDetection = dialyzer_callgraph:get_race_detection(Callgraph), - RaceCode = dialyzer_callgraph:get_race_code(Callgraph), BehaviourTranslations = case RaceDetection of true -> dialyzer_behaviours:translatable_behaviours(Tree); false -> [] end, TopFun = cerl:ann_c_fun([{label, top}], [], Tree), - State = state__new(dialyzer_callgraph:race_code_new(Callgraph), - TopFun, Plt, Module, Records, BehaviourTranslations), + State = + state__new(Callgraph, TopFun, Plt, Module, Records, BehaviourTranslations), State1 = state__race_analysis(not GetWarnings, State), State2 = analyze_loop(State1), case GetWarnings of true -> State3 = state__set_warning_mode(State2), State4 = analyze_loop(State3), - State5 = state__restore_race_code(RaceCode, State4), %% EXPERIMENTAL: Turn all behaviour API calls into calls to the %% respective callback module's functions. case BehaviourTranslations of - [] -> dialyzer_races:race(State5); + [] -> dialyzer_races:race(State4); Behaviours -> - Callgraph2 = State5#state.callgraph, - Digraph = dialyzer_callgraph:get_digraph(Callgraph2), + Digraph = dialyzer_callgraph:get_digraph(State4#state.callgraph), TranslatedCallgraph = dialyzer_behaviours:translate_callgraph(Behaviours, Module, - Callgraph2), + Callgraph), St = - dialyzer_races:race(State5#state{callgraph = TranslatedCallgraph}), - Callgraph3 = dialyzer_callgraph:put_digraph(Digraph, - St#state.callgraph), - St#state{callgraph = Callgraph3} + dialyzer_races:race(State4#state{callgraph = TranslatedCallgraph}), + FinalCallgraph = dialyzer_callgraph:put_digraph(Digraph, + St#state.callgraph), + St#state{callgraph = FinalCallgraph} end; false -> - Callgraph1 = State2#state.callgraph, - RaceCode1 = dialyzer_callgraph:get_race_code(Callgraph1), - state__restore_race_code( - dict:merge(fun (_K, V1, _V2) -> V1 end, - RaceCode, RaceCode1), State2) + State2 end. analyze_loop(#state{callgraph = Callgraph, races = Races} = State) -> @@ -2905,10 +2898,6 @@ state__set_warning_mode(#state{tree_map = TreeMap, fun_tab = FunTab, fun_tab = FunTab, warning_mode = true, races = dialyzer_races:put_race_analysis(true, Races)}. -state__restore_race_code(RaceCode, #state{callgraph = Callgraph} = State) -> - State#state{callgraph = dialyzer_callgraph:put_race_code(RaceCode, - Callgraph)}. - state__race_analysis(Analysis, #state{races = Races} = State) -> State#state{races = dialyzer_races:put_race_analysis(Analysis, Races)}. -- cgit v1.2.3 From 7640a30bbd0a45ccea356bd9f9393c445471cbc4 Mon Sep 17 00:00:00 2001 From: Stavros Aronis Date: Thu, 16 Feb 2012 16:04:43 +0100 Subject: Remove unused race detection related code --- lib/dialyzer/src/dialyzer_callgraph.erl | 6 ++ lib/dialyzer/src/dialyzer_dataflow.erl | 142 ++++++++++++----------------- lib/dialyzer/src/dialyzer_races.erl | 7 ++ lib/dialyzer/src/dialyzer_succ_typings.erl | 40 ++++---- 4 files changed, 87 insertions(+), 108 deletions(-) (limited to 'lib/dialyzer') diff --git a/lib/dialyzer/src/dialyzer_callgraph.erl b/lib/dialyzer/src/dialyzer_callgraph.erl index ddda27adb4..fd6bc89e25 100644 --- a/lib/dialyzer/src/dialyzer_callgraph.erl +++ b/lib/dialyzer/src/dialyzer_callgraph.erl @@ -49,6 +49,7 @@ in_neighbours/2, renew_race_info/4, renew_race_code/2, + renew_race_public_tables/2, reset_from_funs/2, scan_core_tree/2, strip_module_deps/2, @@ -248,6 +249,11 @@ renew_race_code(Races, #callgraph{race_code = RaceCode} = Callgraph) -> Code = lists:reverse(dialyzer_races:get_race_list(Races)), Callgraph#callgraph{race_code = dict:store(Fun, [FunArgs, Code], RaceCode)}. +-spec renew_race_public_tables(label(), callgraph()) -> callgraph(). + +renew_race_public_tables(VarLabel, #callgraph{public_tables = PT} = CG) -> + CG#callgraph{public_tables = ordsets:add_element(VarLabel, PT)}. + -spec get_depends_on(scc(), callgraph()) -> [scc()]. get_depends_on(SCC, #callgraph{active_digraph = DG}) -> diff --git a/lib/dialyzer/src/dialyzer_dataflow.erl b/lib/dialyzer/src/dialyzer_dataflow.erl index a72116d1f2..f554143bb6 100644 --- a/lib/dialyzer/src/dialyzer_dataflow.erl +++ b/lib/dialyzer/src/dialyzer_dataflow.erl @@ -111,7 +111,7 @@ -spec get_warnings(cerl:c_module(), dialyzer_plt:plt(), dialyzer_callgraph:callgraph(), dict(), set()) -> - {[dial_warning()], dict(), dict(), [label()], [string()]}. + {[dial_warning()], dict()}. get_warnings(Tree, Plt, Callgraph, Records, NoWarnUnused) -> State1 = analyze_module(Tree, Plt, Callgraph, Records, true), @@ -119,23 +119,15 @@ get_warnings(Tree, Plt, Callgraph, Records, NoWarnUnused) -> State3 = state__renew_warnings(state__get_warnings(State2, NoWarnUnused), State2), State4 = state__get_race_warnings(State3), - Callgraph1 = State2#state.callgraph, - {State4#state.warnings, state__all_fun_types(State4), - dialyzer_callgraph:get_race_code(Callgraph1), - dialyzer_callgraph:get_public_tables(Callgraph1), - dialyzer_callgraph:get_named_tables(Callgraph1)}. + {State4#state.warnings, state__all_fun_types(State4)}. -spec get_fun_types(cerl:c_module(), dialyzer_plt:plt(), dialyzer_callgraph:callgraph(), dict()) -> - {dict(), dict(), [label()], [string()]}. + {dict(), dialyzer_callgraph:callgraph()}. get_fun_types(Tree, Plt, Callgraph, Records) -> State = analyze_module(Tree, Plt, Callgraph, Records, false), - Callgraph1 = State#state.callgraph, - {state__all_fun_types(State), - dialyzer_callgraph:get_race_code(Callgraph1), - dialyzer_callgraph:get_public_tables(Callgraph1), - dialyzer_callgraph:get_named_tables(Callgraph1)}. + {state__all_fun_types(State), State#state.callgraph}. %%-------------------------------------------------------------------- @@ -307,7 +299,7 @@ analyze_module(Tree, Plt, Callgraph, Records, GetWarnings) -> State2 end. -analyze_loop(#state{callgraph = Callgraph, races = Races} = State) -> +analyze_loop(State) -> case state__get_work(State) of none -> State; {Fun, NewState1} -> @@ -333,10 +325,9 @@ analyze_loop(#state{callgraph = Callgraph, races = Races} = State) -> Map1 = enter_type_lists(Vars, ArgTypes, Map), Body = cerl:fun_body(Fun), FunLabel = get_label(Fun), - RaceDetection = dialyzer_callgraph:get_race_detection(Callgraph), - RaceAnalysis = dialyzer_races:get_race_analysis(Races), + IsRaceAnalysisEnabled = is_race_analysis_enabled(State), NewState3 = - case RaceDetection andalso RaceAnalysis of + case IsRaceAnalysisEnabled of true -> NewState2 = state__renew_curr_fun( state__lookup_name(FunLabel, NewState1), FunLabel, @@ -350,7 +341,7 @@ analyze_loop(#state{callgraph = Callgraph, races = Races} = State) -> [state__lookup_name(get_label(Fun), State), t_to_string(t_fun(ArgTypes, BodyType))]), NewState5 = - case RaceDetection andalso RaceAnalysis of + case IsRaceAnalysisEnabled of true -> renew_race_code(NewState4); false -> NewState4 end, @@ -566,9 +557,7 @@ handle_apply_or_call([{local, external}|Left], Args, ArgTypes, Map, Tree, State, ArgTypes, t_any()); handle_apply_or_call([{TypeOfApply, {Fun, Sig, Contr, LocalRet}}|Left], Args, ArgTypes, Map, Tree, - #state{callgraph = Callgraph, races = Races, - opaques = Opaques} = State, - AccArgTypes, AccRet) -> + #state{opaques = Opaques} = State, AccArgTypes, AccRet) -> Any = t_any(), AnyArgs = [Any || _ <- Args], GenSig = {AnyArgs, fun(_) -> t_any() end}, @@ -664,8 +653,7 @@ handle_apply_or_call([{TypeOfApply, {Fun, Sig, Contr, LocalRet}}|Left], ?debug("ContrRet: ~s\n", [erl_types:t_to_string(CRange(TmpArgTypes))]), ?debug("SigRet: ~s\n", [erl_types:t_to_string(SigRange)]), State1 = - case dialyzer_callgraph:get_race_detection(Callgraph) andalso - dialyzer_races:get_race_analysis(Races) of + case is_race_analysis_enabled(State) of true -> Ann = cerl:get_ann(Tree), File = get_file(Ann), @@ -1031,20 +1019,17 @@ handle_call(Tree, Map, State) -> %%---------------------------------------- -handle_case(Tree, Map, #state{callgraph = Callgraph} = State) -> +handle_case(Tree, Map, State) -> Arg = cerl:case_arg(Tree), Clauses = filter_match_fail(cerl:case_clauses(Tree)), {State1, Map1, ArgType} = SMA = traverse(Arg, Map, State), case t_is_none_or_unit(ArgType) of true -> SMA; false -> - Races = State1#state.races, State2 = - case dialyzer_callgraph:get_race_detection(Callgraph) andalso - dialyzer_races:get_race_analysis(Races) of + case is_race_analysis_enabled(State) of true -> - RaceList = dialyzer_races:get_race_list(Races), - RaceListSize = dialyzer_races:get_race_list_size(Races), + {RaceList, RaceListSize} = get_race_list_and_size(State1), state__renew_race_list([beg_case|RaceList], RaceListSize + 1, State1); false -> State1 @@ -1078,9 +1063,8 @@ handle_cons(Tree, Map, State) -> %%---------------------------------------- -handle_let(Tree, Map, #state{callgraph = Callgraph, races = Races} = State) -> - RaceAnalysis = dialyzer_races:get_race_analysis(Races), - RaceDetection = dialyzer_callgraph:get_race_detection(Callgraph), +handle_let(Tree, Map, State) -> + IsRaceAnalysisEnabled = is_race_analysis_enabled(State), Arg = cerl:let_arg(Tree), Vars = cerl:let_vars(Tree), {Map0, State0} = @@ -1088,10 +1072,9 @@ handle_let(Tree, Map, #state{callgraph = Callgraph, races = Races} = State) -> true -> [Var] = Vars, {enter_subst(Var, Arg, Map), - case RaceDetection andalso RaceAnalysis of + case IsRaceAnalysisEnabled of true -> - RaceList = dialyzer_races:get_race_list(Races), - RaceListSize = dialyzer_races:get_race_list_size(Races), + {RaceList, RaceListSize} = get_race_list_and_size(State), state__renew_race_list( [dialyzer_races:let_tag_new(Var, Arg)|RaceList], RaceListSize + 1, State); @@ -1101,9 +1084,8 @@ handle_let(Tree, Map, #state{callgraph = Callgraph, races = Races} = State) -> end, Body = cerl:let_body(Tree), {State1, Map1, ArgTypes} = SMA = traverse(Arg, Map0, State0), - Callgraph1 = State1#state.callgraph, - Callgraph2 = - case RaceDetection andalso RaceAnalysis andalso cerl:is_c_call(Arg) of + State2 = + case IsRaceAnalysisEnabled andalso cerl:is_c_call(Arg) of true -> Mod = cerl:call_module(Arg), Name = cerl:call_name(Arg), @@ -1111,16 +1093,11 @@ handle_let(Tree, Map, #state{callgraph = Callgraph, races = Races} = State) -> cerl:concrete(Mod) =:= ets andalso cerl:is_literal(Name) andalso cerl:concrete(Name) =:= new of - true -> - NewTable = dialyzer_races:get_new_table(State1#state.races), - renew_public_tables(Vars, NewTable, - state__warning_mode(State1), - Callgraph1); - false -> Callgraph1 + true -> renew_race_public_tables(Vars, State1); + false -> State1 end; - false -> Callgraph1 + false -> State1 end, - State2 = State1#state{callgraph = Callgraph2}, case t_is_none_or_unit(ArgTypes) of true -> SMA; false -> @@ -1151,16 +1128,13 @@ handle_module(Tree, Map, State) -> %%---------------------------------------- -handle_receive(Tree, Map, - #state{callgraph = Callgraph, races = Races} = State) -> +handle_receive(Tree, Map, State) -> Clauses = filter_match_fail(cerl:receive_clauses(Tree)), Timeout = cerl:receive_timeout(Tree), State1 = - case dialyzer_callgraph:get_race_detection(Callgraph) andalso - dialyzer_races:get_race_analysis(Races) of + case is_race_analysis_enabled(State) of true -> - RaceList = dialyzer_races:get_race_list(Races), - RaceListSize = dialyzer_races:get_race_list_size(Races), + {RaceList, RaceListSize} = get_race_list_and_size(State), state__renew_race_list([beg_case|RaceList], RaceListSize + 1, State); false -> State @@ -1283,16 +1257,13 @@ handle_tuple(Tree, Map, State) -> %%---------------------------------------- %% Clauses %% -handle_clauses([C|Left], Arg, ArgType, OrigArgType, - #state{callgraph = Callgraph, races = Races} = State, - CaseTypes, MapIn, Acc, ClauseAcc) -> - RaceDetection = dialyzer_callgraph:get_race_detection(Callgraph), - RaceAnalysis = dialyzer_races:get_race_analysis(Races), +handle_clauses([C|Left], Arg, ArgType, OrigArgType, State, CaseTypes, MapIn, + Acc, ClauseAcc) -> + IsRaceAnalysisEnabled = is_race_analysis_enabled(State), State1 = - case RaceDetection andalso RaceAnalysis of + case IsRaceAnalysisEnabled of true -> - RaceList = dialyzer_races:get_race_list(Races), - RaceListSize = dialyzer_races:get_race_list_size(Races), + {RaceList, RaceListSize} = get_race_list_and_size(State), state__renew_race_list( [dialyzer_races:beg_clause_new(Arg, cerl:clause_pats(C), cerl:clause_guard(C))| @@ -1303,11 +1274,9 @@ handle_clauses([C|Left], Arg, ArgType, OrigArgType, {State2, ClauseMap, BodyType, NewArgType} = do_clause(C, Arg, ArgType, OrigArgType, MapIn, State1), {NewClauseAcc, State3} = - case RaceDetection andalso RaceAnalysis of + case IsRaceAnalysisEnabled of true -> - Races1 = State2#state.races, - RaceList1 = dialyzer_races:get_race_list(Races1), - RaceListSize1 = dialyzer_races:get_race_list_size(Races1), + {RaceList1, RaceListSize1} = get_race_list_and_size(State2), EndClause = dialyzer_races:end_clause_new(Arg, cerl:clause_pats(C), cerl:clause_guard(C)), {[EndClause|ClauseAcc], @@ -1322,30 +1291,25 @@ handle_clauses([C|Left], Arg, ArgType, OrigArgType, end, handle_clauses(Left, Arg, NewArgType, OrigArgType, State3, NewCaseTypes, MapIn, NewAcc, NewClauseAcc); -handle_clauses([], _Arg, _ArgType, _OrigArgType, - #state{callgraph = Callgraph, races = Races} = State, - CaseTypes, _MapIn, Acc, ClauseAcc) -> +handle_clauses([], _Arg, _ArgType, _OrigArgType, State, CaseTypes, _MapIn, Acc, + ClauseAcc) -> State1 = - case dialyzer_callgraph:get_race_detection(Callgraph) andalso - dialyzer_races:get_race_analysis(Races) of + case is_race_analysis_enabled(State) of true -> + {RaceList, RaceListSize} = get_race_list_and_size(State), state__renew_race_list( - [dialyzer_races:end_case_new(ClauseAcc)| - dialyzer_races:get_race_list(Races)], - dialyzer_races:get_race_list_size(Races) + 1, State); + [dialyzer_races:end_case_new(ClauseAcc)|RaceList], + RaceListSize + 1, State); false -> State end, {lists:reverse(Acc), State1, t_sup(CaseTypes)}. -do_clause(C, Arg, ArgType0, OrigArgType, Map, - #state{callgraph = Callgraph, races = Races} = State) -> +do_clause(C, Arg, ArgType0, OrigArgType, Map, State) -> Pats = cerl:clause_pats(C), Guard = cerl:clause_guard(C), Body = cerl:clause_body(C), - RaceDetection = dialyzer_callgraph:get_race_detection(Callgraph), - RaceAnalysis = dialyzer_races:get_race_analysis(Races), State1 = - case RaceDetection andalso RaceAnalysis of + case is_race_analysis_enabled(State) of true -> state__renew_fun_args(Pats, State); false -> State @@ -3322,6 +3286,14 @@ state__records_only(#state{records = Records}) -> %%% %%% =========================================================================== +is_race_analysis_enabled(#state{races = Races, callgraph = Callgraph}) -> + RaceDetection = dialyzer_callgraph:get_race_detection(Callgraph), + RaceAnalysis = dialyzer_races:get_race_analysis(Races), + RaceDetection andalso RaceAnalysis. + +get_race_list_and_size(#state{races = Races}) -> + dialyzer_races:get_race_list_and_size(Races). + renew_race_code(#state{races = Races, callgraph = Callgraph, warning_mode = WarningMode} = State) -> case WarningMode of @@ -3331,17 +3303,19 @@ renew_race_code(#state{races = Races, callgraph = Callgraph, State#state{callgraph = NewCallgraph} end. -renew_public_tables([Var], Table, WarningMode, Callgraph) -> +renew_race_public_tables([Var], #state{races = Races, callgraph = Callgraph, + warning_mode = WarningMode} = State) -> case WarningMode of - true -> Callgraph; + true -> State; false -> + Table = dialyzer_races:get_new_table(Races), case Table of - no_t -> Callgraph; - _Other -> - VarLabel = get_label(Var), - PTables = dialyzer_callgraph:get_public_tables(Callgraph), - dialyzer_callgraph:put_public_tables( - lists:usort([VarLabel|PTables]), Callgraph) + no_t -> State; + _Other -> + VarLabel = get_label(Var), + NewCallgraph = + dialyzer_callgraph:renew_race_public_tables(VarLabel, Callgraph), + State#state{callgraph = NewCallgraph} end end. diff --git a/lib/dialyzer/src/dialyzer_races.erl b/lib/dialyzer/src/dialyzer_races.erl index cc635b0eef..89b45990d9 100644 --- a/lib/dialyzer/src/dialyzer_races.erl +++ b/lib/dialyzer/src/dialyzer_races.erl @@ -36,6 +36,7 @@ -export([beg_clause_new/3, cleanup/1, end_case_new/1, end_clause_new/3, get_curr_fun/1, get_curr_fun_args/1, get_new_table/1, get_race_analysis/1, get_race_list/1, get_race_list_size/1, + get_race_list_and_size/1, let_tag_new/2, new/0, put_curr_fun/3, put_fun_args/2, put_race_analysis/2, put_race_list/3]). @@ -2434,6 +2435,12 @@ get_race_list(#races{race_list = RaceList}) -> get_race_list_size(#races{race_list_size = RaceListSize}) -> RaceListSize. +-spec get_race_list_and_size(races()) -> {code(), non_neg_integer()}. + +get_race_list_and_size(#races{race_list = RaceList, + race_list_size = RaceListSize}) -> + {RaceList, RaceListSize}. + -spec let_tag_new(var_to_map1(), var_to_map1()) -> #let_tag{}. let_tag_new(Var, Arg) -> diff --git a/lib/dialyzer/src/dialyzer_succ_typings.erl b/lib/dialyzer/src/dialyzer_succ_typings.erl index 740081a33f..21cc13d849 100644 --- a/lib/dialyzer/src/dialyzer_succ_typings.erl +++ b/lib/dialyzer/src/dialyzer_succ_typings.erl @@ -142,17 +142,14 @@ get_warnings_from_modules([M|Ms], State, DocPlt, Acc) when is_atom(M) -> %% Check if there are contracts for functions that do not exist Warnings1 = dialyzer_contracts:contracts_without_fun(Contracts, AllFuns, Callgraph), - {RawWarnings2, FunTypes, RaceCode, PublicTables, NamedTables} = - dialyzer_dataflow:get_warnings(ModCode, Plt, Callgraph, Records, NoWarnUnused), + {RawWarnings2, FunTypes} = + dialyzer_dataflow:get_warnings(ModCode, Plt, Callgraph, + Records, NoWarnUnused), {NewAcc, Warnings2} = postprocess_dataflow_warns(RawWarnings2, State, Acc), Attrs = cerl:module_attrs(ModCode), Warnings3 = dialyzer_behaviours:check_callbacks(M, Attrs, Plt, Codeserver), NewDocPlt = insert_into_doc_plt(FunTypes, Callgraph, DocPlt), - NewCallgraph = - dialyzer_callgraph:renew_race_info(Callgraph, RaceCode, PublicTables, - NamedTables), - State1 = st__renew_state_calls(NewCallgraph, State), - get_warnings_from_modules(Ms, State1, NewDocPlt, + get_warnings_from_modules(Ms, State, NewDocPlt, [Warnings1, Warnings2, Warnings3|NewAcc]); get_warnings_from_modules([], #st{plt = Plt}, DocPlt, Acc) -> {lists:flatten(Acc), Plt, DocPlt}. @@ -195,45 +192,40 @@ refine_succ_typings(ModulePostorder, State) -> refine_succ_typings(ModulePostorder, State, []). refine_succ_typings([M|Rest], State, Fixpoint) -> + #st{callgraph = Callgraph, codeserver = CodeServer, plt = PLT} = State, Msg = io_lib:format("Dataflow of module: ~w\n", [M]), send_log(State#st.parent, Msg), ?debug("~s\n", [Msg]), - {NewState, FixpointFromScc} = refine_one_module(M, State), + {NewCallgraph, FixpointFromScc} = + refine_one_module(M, Callgraph, CodeServer, PLT), NewFixpoint = ordsets:union(Fixpoint, FixpointFromScc), - refine_succ_typings(Rest, NewState, NewFixpoint); + refine_succ_typings(Rest, State#st{callgraph = NewCallgraph}, NewFixpoint); refine_succ_typings([], State, Fixpoint) -> case Fixpoint =:= [] of true -> {fixpoint, State}; false -> {not_fixpoint, Fixpoint, State} end. --spec refine_one_module(module(), #st{}) -> {#st{}, [label()]}. % ordset +-spec refine_one_module(module(), dialyzer_callgraph:callgraph(), + dialyzer_codeserver:codeserver(), dialyzer_plt:plt()) -> + {dialyzer_callgraph:callgraph(), [label()]}. % ordset -refine_one_module(M, State) -> - #st{callgraph = Callgraph, codeserver = CodeServer, plt = PLT} = State, +refine_one_module(M, Callgraph, CodeServer, PLT) -> ModCode = dialyzer_codeserver:lookup_mod_code(M, CodeServer), AllFuns = collect_fun_info([ModCode]), Records = dialyzer_codeserver:lookup_mod_records(M, CodeServer), - {NewFunTypes, RaceCode, PublicTables, NamedTables} = + {NewFunTypes, NewCallgraph} = dialyzer_dataflow:get_fun_types(ModCode, PLT, Callgraph, Records), - NewCallgraph = - dialyzer_callgraph:renew_race_info(Callgraph, RaceCode, PublicTables, - NamedTables), FunTypes = get_fun_types_from_plt(AllFuns, Callgraph, PLT), - State1 = st__renew_state_calls(NewCallgraph, State), case reached_fixpoint(FunTypes, NewFunTypes) of true -> - {State1, ordsets:new()}; + {NewCallgraph, ordsets:new()}; {false, NotFixpoint} -> ?debug("Not fixpoint\n", []), - NewPlt = insert_into_plt(dict:from_list(NotFixpoint), Callgraph, PLT), - {State1#st{plt = NewPlt}, - ordsets:from_list([FunLbl || {FunLbl,_Type} <- NotFixpoint])} + insert_into_plt(dict:from_list(NotFixpoint), Callgraph, PLT), + {NewCallgraph, ordsets:from_list([FunLbl || {FunLbl,_Type} <- NotFixpoint])} end. -st__renew_state_calls(Callgraph, State) -> - State#st{callgraph = Callgraph}. - reached_fixpoint(OldTypes, NewTypes) -> reached_fixpoint(OldTypes, NewTypes, false). -- cgit v1.2.3 From b8e96486af6b63c20cefd8af788a8e275a9d8e53 Mon Sep 17 00:00:00 2001 From: Stavros Aronis Date: Fri, 17 Feb 2012 17:18:47 +0100 Subject: Separate race related fields of callgraph --- lib/dialyzer/src/dialyzer_callgraph.erl | 216 ++++++++++++++++++++++---------- lib/dialyzer/src/dialyzer_dataflow.erl | 11 ++ lib/dialyzer/src/dialyzer_races.erl | 5 +- 3 files changed, 168 insertions(+), 64 deletions(-) (limited to 'lib/dialyzer') diff --git a/lib/dialyzer/src/dialyzer_callgraph.erl b/lib/dialyzer/src/dialyzer_callgraph.erl index fd6bc89e25..7379fbc3c4 100644 --- a/lib/dialyzer/src/dialyzer_callgraph.erl +++ b/lib/dialyzer/src/dialyzer_callgraph.erl @@ -61,7 +61,7 @@ get_race_code/1, get_race_detection/1, race_code_new/1, put_digraph/2, put_race_code/2, put_race_detection/2, put_named_tables/2, put_public_tables/2, put_behaviour_api_calls/2, - get_behaviour_api_calls/1]). + get_behaviour_api_calls/1, dispose_race_server/1, duplicate/1]). -export_type([callgraph/0, mfa_or_funlbl/0]). @@ -98,11 +98,13 @@ rec_var_map = dict:new() :: dict() | ets:tid(), self_rec = sets:new() :: set() | ets:tid(), calls = dict:new() :: dict() | ets:tid(), - race_code = dict:new() :: dict(), - public_tables = [] :: [label()], - named_tables = [] :: [string()], race_detection = false :: boolean(), - beh_api_calls = [] :: [{mfa(), mfa()}]}). + race_data_server = new_race_data_server() :: pid()}). + +-record(race_data_state, {race_code = dict:new() :: dict(), + public_tables = [] :: [label()], + named_tables = [] :: [string()], + beh_api_calls = [] :: [{mfa(), mfa()}]}). %% Exported Types @@ -233,27 +235,6 @@ find_non_local_calls([{Label1, Label2}|Left], Set) when is_integer(Label1), find_non_local_calls([], Set) -> sets:to_list(Set). --spec renew_race_info(callgraph(), dict(), [label()], [string()]) -> - callgraph(). - -renew_race_info(CG, RaceCode, PublicTables, NamedTables) -> - CG#callgraph{race_code = RaceCode, - public_tables = PublicTables, - named_tables = NamedTables}. - --spec renew_race_code(dialyzer_races:races(), callgraph()) -> callgraph(). - -renew_race_code(Races, #callgraph{race_code = RaceCode} = Callgraph) -> - Fun = dialyzer_races:get_curr_fun(Races), - FunArgs = dialyzer_races:get_curr_fun_args(Races), - Code = lists:reverse(dialyzer_races:get_race_list(Races)), - Callgraph#callgraph{race_code = dict:store(Fun, [FunArgs, Code], RaceCode)}. - --spec renew_race_public_tables(label(), callgraph()) -> callgraph(). - -renew_race_public_tables(VarLabel, #callgraph{public_tables = PT} = CG) -> - CG#callgraph{public_tables = ordsets:add_element(VarLabel, PT)}. - -spec get_depends_on(scc(), callgraph()) -> [scc()]. get_depends_on(SCC, #callgraph{active_digraph = DG}) -> @@ -605,20 +586,68 @@ digraph_reaching_subgraph(Funs, DG) -> %% Races %%---------------------------------------------------------------------- +-spec renew_race_info(callgraph(), dict(), [label()], [string()]) -> + callgraph(). + +renew_race_info(#callgraph{race_data_server = RaceDataServer} = CG, + RaceCode, PublicTables, NamedTables) -> + ok = race_data_server_cast( + {renew_race_info, {RaceCode, PublicTables, NamedTables}}, + RaceDataServer), + CG. + +renew_race_info({RaceCode, PublicTables, NamedTables}, + #race_data_state{} = State) -> + State#race_data_state{race_code = RaceCode, + public_tables = PublicTables, + named_tables = NamedTables}. + +-spec renew_race_code(dialyzer_races:races(), callgraph()) -> callgraph(). + +renew_race_code(Races, #callgraph{race_data_server = RaceDataServer} = CG) -> + Fun = dialyzer_races:get_curr_fun(Races), + FunArgs = dialyzer_races:get_curr_fun_args(Races), + Code = lists:reverse(dialyzer_races:get_race_list(Races)), + ok = race_data_server_cast( + {renew_race_code, {Fun, FunArgs, Code}}, + RaceDataServer), + CG; +renew_race_code({Fun, FunArgs, Code}, + #race_data_state{race_code = RaceCode} = State) -> + State#race_data_state{race_code = dict:store(Fun, [FunArgs, Code], RaceCode)}. + +-spec renew_race_public_tables(label(), callgraph()) -> callgraph(). + +renew_race_public_tables(VarLabel, + #callgraph{race_data_server = RaceDataServer} = CG) -> + ok = + race_data_server_cast({renew_race_public_tables, VarLabel}, RaceDataServer), + CG; +renew_race_public_tables(VarLabel, + #race_data_state{public_tables = PT} = State) -> + State#race_data_state{public_tables = ordsets:add_element(VarLabel, PT)}. + -spec cleanup(callgraph()) -> callgraph(). -cleanup(#callgraph{digraph = Digraph, - name_map = NameMap, - rev_name_map = RevNameMap, - public_tables = PublicTables, - named_tables = NamedTables, - race_code = RaceCode}) -> +cleanup(#callgraph{digraph = Digraph, + name_map = NameMap, + rev_name_map = RevNameMap, + race_data_server = RaceDataServer}) -> #callgraph{digraph = Digraph, - name_map = NameMap, - rev_name_map = RevNameMap, - public_tables = PublicTables, - named_tables = NamedTables, - race_code = RaceCode}. + name_map = NameMap, + rev_name_map = RevNameMap, + race_data_server = race_data_server_call(dup, RaceDataServer)}. + +-spec duplicate(callgraph()) -> callgraph(). + +duplicate(#callgraph{race_data_server = RaceDataServer} = Callgraph) -> + Callgraph#callgraph{ + race_data_server = race_data_server_call(dup, RaceDataServer)}. + +-spec dispose_race_server(callgraph()) -> ok. + +dispose_race_server(#callgraph{race_data_server = RaceDataServer}) -> + race_data_server_cast(stop, RaceDataServer). -spec get_digraph(callgraph()) -> digraph(). @@ -627,28 +656,34 @@ get_digraph(#callgraph{digraph = Digraph}) -> -spec get_named_tables(callgraph()) -> [string()]. -get_named_tables(#callgraph{named_tables = NamedTables}) -> - NamedTables. +get_named_tables(#callgraph{race_data_server = RaceDataServer}) -> + race_data_server_call(get_named_tables, RaceDataServer). -spec get_public_tables(callgraph()) -> [label()]. -get_public_tables(#callgraph{public_tables = PT}) -> - PT. +get_public_tables(#callgraph{race_data_server = RaceDataServer}) -> + race_data_server_call(get_public_tables, RaceDataServer). -spec get_race_code(callgraph()) -> dict(). -get_race_code(#callgraph{race_code = RaceCode}) -> - RaceCode. +get_race_code(#callgraph{race_data_server = RaceDataServer}) -> + race_data_server_call(get_race_code, RaceDataServer). -spec get_race_detection(callgraph()) -> boolean(). get_race_detection(#callgraph{race_detection = RD}) -> RD. +-spec get_behaviour_api_calls(callgraph()) -> [{mfa(), mfa()}]. + +get_behaviour_api_calls(#callgraph{race_data_server = RaceDataServer}) -> + race_data_server_call(get_behaviour_api_calls, RaceDataServer). + -spec race_code_new(callgraph()) -> callgraph(). -race_code_new(Callgraph) -> - Callgraph#callgraph{race_code = dict:new()}. +race_code_new(#callgraph{race_data_server = RaceDataServer} = CG) -> + ok = race_data_server_cast(race_code_new, RaceDataServer), + CG. -spec put_digraph(digraph(), callgraph()) -> callgraph(). @@ -657,8 +692,9 @@ put_digraph(Digraph, Callgraph) -> -spec put_race_code(dict(), callgraph()) -> callgraph(). -put_race_code(RaceCode, Callgraph) -> - Callgraph#callgraph{race_code = RaceCode}. +put_race_code(RaceCode, #callgraph{race_data_server = RaceDataServer} = CG) -> + ok = race_data_server_cast({put_race_code, RaceCode}, RaceDataServer), + CG. -spec put_race_detection(boolean(), callgraph()) -> callgraph(). @@ -667,13 +703,79 @@ put_race_detection(RaceDetection, Callgraph) -> -spec put_named_tables([string()], callgraph()) -> callgraph(). -put_named_tables(NamedTables, Callgraph) -> - Callgraph#callgraph{named_tables = NamedTables}. +put_named_tables(NamedTables, + #callgraph{race_data_server = RaceDataServer} = CG) -> + ok = race_data_server_cast({put_named_tables, NamedTables}, RaceDataServer), + CG. -spec put_public_tables([label()], callgraph()) -> callgraph(). -put_public_tables(PublicTables, Callgraph) -> - Callgraph#callgraph{public_tables = PublicTables}. +put_public_tables(PublicTables, + #callgraph{race_data_server = RaceDataServer} = CG) -> + ok = race_data_server_cast({put_public_tables, PublicTables}, RaceDataServer), + CG. + +-spec put_behaviour_api_calls([{mfa(), mfa()}], callgraph()) -> callgraph(). + +put_behaviour_api_calls(Calls, + #callgraph{race_data_server = RaceDataServer} = CG) -> + ok = race_data_server_cast({put_behaviour_api_calls, Calls}, RaceDataServer), + CG. + + +new_race_data_server() -> + spawn(fun() -> race_data_server_loop(#race_data_state{}) end). + +race_data_server_loop(State) -> + receive + {call, From, Ref, Query} -> + Reply = race_data_server_handle_call(Query, State), + From ! {Ref, Reply}, + race_data_server_loop(State); + {cast, stop} -> + ok; + {cast, Message} -> + NewState = race_data_server_handle_cast(Message, State), + race_data_server_loop(NewState) + end. + +race_data_server_call(Query, Server) -> + Ref = make_ref(), + Server ! {call, self(), Ref, Query}, + receive + {Ref, Reply} -> Reply + end. + +race_data_server_cast(Message, Server) -> + Server ! {cast, Message}, + ok. + +race_data_server_handle_cast(race_code_new, State) -> + State#race_data_state{race_code = dict:new()}; +race_data_server_handle_cast({Tag, Data}, State) -> + case Tag of + renew_race_info -> renew_race_info(Data, State); + renew_race_code -> renew_race_code(Data, State); + renew_race_public_tables -> renew_race_public_tables(Data, State); + put_race_code -> State#race_data_state{race_code = Data}; + put_public_tables -> State#race_data_state{public_tables = Data}; + put_named_tables -> State#race_data_state{named_tables = Data}; + put_behaviour_api_calls -> State#race_data_state{beh_api_calls = Data} + end. + +race_data_server_handle_call(Query, + #race_data_state{race_code = RaceCode, + public_tables = PublicTables, + named_tables = NamedTables, + beh_api_calls = BehApiCalls} + = State) -> + case Query of + dup -> spawn(fun() -> race_data_server_loop(State) end); + get_race_code -> RaceCode; + get_public_tables -> PublicTables; + get_named_tables -> NamedTables; + get_behaviour_api_calls -> BehApiCalls + end. %%============================================================================= %% Utilities for 'dot' @@ -701,15 +803,3 @@ to_ps(#callgraph{} = CG, File, Args) -> Command = io_lib:format("dot -Tps ~s -o ~s ~s", [Args, File, Dot_File]), _ = os:cmd(Command), ok. - -%------------------------------------------------------------------------------- - --spec put_behaviour_api_calls([{mfa(), mfa()}], callgraph()) -> callgraph(). - -put_behaviour_api_calls(Calls, Callgraph) -> - Callgraph#callgraph{beh_api_calls = Calls}. - --spec get_behaviour_api_calls(callgraph()) -> [{mfa(), mfa()}]. - -get_behaviour_api_calls(Callgraph) -> - Callgraph#callgraph.beh_api_calls. diff --git a/lib/dialyzer/src/dialyzer_dataflow.erl b/lib/dialyzer/src/dialyzer_dataflow.erl index f554143bb6..69575ad63a 100644 --- a/lib/dialyzer/src/dialyzer_dataflow.erl +++ b/lib/dialyzer/src/dialyzer_dataflow.erl @@ -32,6 +32,7 @@ %% Data structure interfaces. -export([state__add_warning/2, state__cleanup/1, + state__duplicate/1, dispose_state/1, state__get_callgraph/1, state__get_races/1, state__get_records/1, state__put_callgraph/2, state__put_races/2, state__records_only/1]). @@ -3249,6 +3250,16 @@ state__cleanup(#state{callgraph = Callgraph, races = dialyzer_races:cleanup(Races), records = Records}. +-spec state__duplicate(state()) -> state(). + +state__duplicate(#state{callgraph = Callgraph} = State) -> + State#state{callgraph = dialyzer_callgraph:duplicate(Callgraph)}. + +-spec dispose_state(state()) -> ok. + +dispose_state(#state{callgraph = Callgraph}) -> + dialyzer_callgraph:dispose_race_server(Callgraph). + -spec state__get_callgraph(state()) -> dialyzer_callgraph:callgraph(). state__get_callgraph(#state{callgraph = Callgraph}) -> diff --git a/lib/dialyzer/src/dialyzer_races.erl b/lib/dialyzer/src/dialyzer_races.erl index 89b45990d9..cdb9f25999 100644 --- a/lib/dialyzer/src/dialyzer_races.erl +++ b/lib/dialyzer/src/dialyzer_races.erl @@ -347,6 +347,7 @@ fixup_race_list(RaceWarnTag, WarnVarArgs, State) -> DepList2 = fixup_race_list_helper(NewParents, Calls, CurrFun, WarnVarArgs, RaceWarnTag, NewState), + dialyzer_dataflow:dispose_state(CleanState), lists:usort(cleanup_dep_calls(DepList1 ++ DepList2)). fixup_race_list_helper(Parents, Calls, CurrFun, WarnVarArgs, RaceWarnTag, @@ -381,13 +382,15 @@ fixup_race_forward_pullout(CurrFun, CurrFunLabel, Calls, Code, RaceList, InitFun, WarnVarArgs, RaceWarnTag, RaceVarMap, FunDefVars, FunCallVars, FunArgTypes, NestingLevel, State) -> + TState = dialyzer_dataflow:state__duplicate(State), {DepList, NewCurrFun, NewCurrFunLabel, NewCalls, NewCode, NewRaceList, NewRaceVarMap, NewFunDefVars, NewFunCallVars, NewFunArgTypes, NewNestingLevel} = fixup_race_forward(CurrFun, CurrFunLabel, Calls, Code, RaceList, InitFun, WarnVarArgs, RaceWarnTag, RaceVarMap, FunDefVars, FunCallVars, FunArgTypes, NestingLevel, - cleanup_race_code(State)), + cleanup_race_code(TState)), + dialyzer_dataflow:dispose_state(TState), case NewCode of [] -> DepList; [#fun_call{caller = NewCurrFun, callee = Call, arg_types = FunTypes, -- cgit v1.2.3 From 0448c64be671017a6ef25e9122f493f433e60811 Mon Sep 17 00:00:00 2001 From: Stavros Aronis Date: Fri, 17 Feb 2012 18:06:27 +0100 Subject: Prepare for parallel dataflow --- lib/dialyzer/src/dialyzer_dataflow.erl | 5 ++- lib/dialyzer/src/dialyzer_succ_typings.erl | 55 ++++++++++++++++-------------- 2 files changed, 32 insertions(+), 28 deletions(-) (limited to 'lib/dialyzer') diff --git a/lib/dialyzer/src/dialyzer_dataflow.erl b/lib/dialyzer/src/dialyzer_dataflow.erl index 69575ad63a..248ad328ab 100644 --- a/lib/dialyzer/src/dialyzer_dataflow.erl +++ b/lib/dialyzer/src/dialyzer_dataflow.erl @@ -123,12 +123,11 @@ get_warnings(Tree, Plt, Callgraph, Records, NoWarnUnused) -> {State4#state.warnings, state__all_fun_types(State4)}. -spec get_fun_types(cerl:c_module(), dialyzer_plt:plt(), - dialyzer_callgraph:callgraph(), dict()) -> - {dict(), dialyzer_callgraph:callgraph()}. + dialyzer_callgraph:callgraph(), dict()) -> dict(). get_fun_types(Tree, Plt, Callgraph, Records) -> State = analyze_module(Tree, Plt, Callgraph, Records, false), - {state__all_fun_types(State), State#state.callgraph}. + state__all_fun_types(State). %%-------------------------------------------------------------------- diff --git a/lib/dialyzer/src/dialyzer_succ_typings.erl b/lib/dialyzer/src/dialyzer_succ_typings.erl index 21cc13d849..76b6d8954b 100644 --- a/lib/dialyzer/src/dialyzer_succ_typings.erl +++ b/lib/dialyzer/src/dialyzer_succ_typings.erl @@ -196,34 +196,53 @@ refine_succ_typings([M|Rest], State, Fixpoint) -> Msg = io_lib:format("Dataflow of module: ~w\n", [M]), send_log(State#st.parent, Msg), ?debug("~s\n", [Msg]), - {NewCallgraph, FixpointFromScc} = - refine_one_module(M, Callgraph, CodeServer, PLT), + Data = collect_refine_scc_data(M, {CodeServer, Callgraph, PLT}), + FixpointFromScc = refine_one_module(Data), NewFixpoint = ordsets:union(Fixpoint, FixpointFromScc), - refine_succ_typings(Rest, State#st{callgraph = NewCallgraph}, NewFixpoint); + refine_succ_typings(Rest, State, NewFixpoint); refine_succ_typings([], State, Fixpoint) -> case Fixpoint =:= [] of true -> {fixpoint, State}; false -> {not_fixpoint, Fixpoint, State} end. --spec refine_one_module(module(), dialyzer_callgraph:callgraph(), - dialyzer_codeserver:codeserver(), dialyzer_plt:plt()) -> - {dialyzer_callgraph:callgraph(), [label()]}. % ordset +-type servers() :: term(). +-type scc_data() :: term(). +-type scc_refine_data() :: term(). +-type scc() :: [mfa_or_funlbl()] | [module()]. + +-spec find_depends_on(scc(), servers()) -> [scc()]. + +find_depends_on(SCC, {_Codeserver, Callgraph, _Plt}) -> + dialyzer_callgraph:get_depends_on(SCC, Callgraph). + +-spec find_required_by(scc(), servers()) -> [scc()]. + +find_required_by(SCC, {_Codeserver, Callgraph, _Plt}) -> + dialyzer_callgraph:get_required_by(SCC, Callgraph). -refine_one_module(M, Callgraph, CodeServer, PLT) -> +-spec collect_refine_scc_data(module(), servers()) -> scc_refine_data(). + +collect_refine_scc_data(M, {CodeServer, Callgraph, PLT}) -> ModCode = dialyzer_codeserver:lookup_mod_code(M, CodeServer), AllFuns = collect_fun_info([ModCode]), Records = dialyzer_codeserver:lookup_mod_records(M, CodeServer), - {NewFunTypes, NewCallgraph} = - dialyzer_dataflow:get_fun_types(ModCode, PLT, Callgraph, Records), FunTypes = get_fun_types_from_plt(AllFuns, Callgraph, PLT), + {ModCode, PLT, Callgraph, Records, FunTypes}. + +-spec refine_one_module(scc_refine_data()) -> + {dialyzer_callgraph:callgraph(), [label()]}. % ordset + +refine_one_module({ModCode, PLT, Callgraph, Records, FunTypes}) -> + NewFunTypes = + dialyzer_dataflow:get_fun_types(ModCode, PLT, Callgraph, Records), case reached_fixpoint(FunTypes, NewFunTypes) of true -> - {NewCallgraph, ordsets:new()}; + ordsets:new(); {false, NotFixpoint} -> ?debug("Not fixpoint\n", []), insert_into_plt(dict:from_list(NotFixpoint), Callgraph, PLT), - {NewCallgraph, ordsets:from_list([FunLbl || {FunLbl,_Type} <- NotFixpoint])} + ordsets:from_list([FunLbl || {FunLbl,_Type} <- NotFixpoint]) end. reached_fixpoint(OldTypes, NewTypes) -> @@ -302,20 +321,6 @@ find_succ_typings([], State, Coordinator) -> false -> {not_fixpoint, NotFixpoint, State} end. --type servers() :: term(). --type scc_data() :: term(). --type scc() :: [mfa_or_funlbl()]. - --spec find_depends_on(scc(), servers()) -> [scc()]. - -find_depends_on(SCC, {_Codeserver, Callgraph, _Plt}) -> - dialyzer_callgraph:get_depends_on(SCC, Callgraph). - --spec find_required_by(scc(), servers()) -> [scc()]. - -find_required_by(SCC, {_Codeserver, Callgraph, _Plt}) -> - dialyzer_callgraph:get_required_by(SCC, Callgraph). - -spec collect_scc_data(scc(), servers()) -> scc_data(). collect_scc_data(SCC, {Codeserver, Callgraph, Plt}) -> -- cgit v1.2.3 From 15529b1392c3c3d452dcae3ce6786ff41fda9f13 Mon Sep 17 00:00:00 2001 From: Stavros Aronis Date: Fri, 17 Feb 2012 18:12:58 +0100 Subject: Generalize coordinator and worker --- lib/dialyzer/src/Makefile | 4 +- lib/dialyzer/src/dialyzer_coordinator.erl | 215 ++++++++++++++++++++++ lib/dialyzer/src/dialyzer_succ_typings.erl | 8 +- lib/dialyzer/src/dialyzer_typesig_coordinator.erl | 215 ---------------------- lib/dialyzer/src/dialyzer_typesig_worker.erl | 142 -------------- lib/dialyzer/src/dialyzer_worker.erl | 142 ++++++++++++++ 6 files changed, 363 insertions(+), 363 deletions(-) create mode 100644 lib/dialyzer/src/dialyzer_coordinator.erl delete mode 100644 lib/dialyzer/src/dialyzer_typesig_coordinator.erl delete mode 100644 lib/dialyzer/src/dialyzer_typesig_worker.erl create mode 100644 lib/dialyzer/src/dialyzer_worker.erl (limited to 'lib/dialyzer') diff --git a/lib/dialyzer/src/Makefile b/lib/dialyzer/src/Makefile index 2f1eaf5754..39b1b3eb3e 100644 --- a/lib/dialyzer/src/Makefile +++ b/lib/dialyzer/src/Makefile @@ -64,8 +64,8 @@ MODULES = \ dialyzer_races \ dialyzer_succ_typings \ dialyzer_typesig \ - dialyzer_typesig_coordinator \ - dialyzer_typesig_worker \ + dialyzer_coordinator \ + dialyzer_worker \ dialyzer_utils HRL_FILES= dialyzer.hrl dialyzer_gui_wx.hrl diff --git a/lib/dialyzer/src/dialyzer_coordinator.erl b/lib/dialyzer/src/dialyzer_coordinator.erl new file mode 100644 index 0000000000..a72d383365 --- /dev/null +++ b/lib/dialyzer/src/dialyzer_coordinator.erl @@ -0,0 +1,215 @@ +%% -*- erlang-indent-level: 2 -*- +%%----------------------------------------------------------------------- +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2006-2011. All Rights Reserved. +%% +%% The contents of this file are subject to the Erlang Public License, +%% Version 1.1, (the "License"); you may not use this file except in +%% compliance with the License. You should have received a copy of the +%% Erlang Public License along with this software. If not, it can be +%% retrieved online at http://www.erlang.org/. +%% +%% Software distributed under the License is distributed on an "AS IS" +%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +%% the License for the specific language governing rights and limitations +%% under the License. +%% +%% %CopyrightEnd% +%% + +%%%------------------------------------------------------------------- +%%% File : dialyzer_coordinator.erl +%%% Authors : Stavros Aronis +%%% +%%% Description: +%%% +%%% The parallel version of Dialyzer's typesig analysis is spread over 4 modules +%%% with the intention to both minimize the changes on the original code and use +%%% a separate module for every kind of Erlang process that will be running. +%%% +%%% There are therefore 3 kinds of processes: +%%% +%%% - The original Dialyzer backend (in succ_typings module) +%%% - The worker process for the typesig analysis (in typesig and +%%% worker) +%%% - A coordinator of the worker processes (in coordinator) +%%% +%%% Operation guidelines: +%%% +%%% - The backend requests from the coordinator to spawn a worker for each SCC +%%% - The backend notifies the coordinator when all SCC have been spawned and +%%% waits for the server to report that the PLT has been updated +%%% - Each worker is responsible to notify all those who wait for it. +%%% +%%%------------------------------------------------------------------- + +-module(dialyzer_coordinator). + +-export([ + all_spawned/1, + scc_done/3, + scc_spawn/2, + sccs_to_pids_reply/0, + sccs_to_pids_request/2, + start/1, + receive_not_fixpoint/0 + ]). + +-behaviour(gen_server). + +-export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, + code_change/3]). + +-type coordinator() :: pid(). +-type map() :: dict(). +-type scc() :: [mfa_or_funlbl()]. + +-record(state, {parent :: pid(), + spawn_count = 0 :: integer(), + all_spawned = false :: boolean(), + scc_to_pid = new_map() :: map(), + not_fixpoint = [] :: [mfa_or_funlbl()], + servers :: dialyzer_typesig:servers() + }). + +-include("dialyzer.hrl"). + +%%-------------------------------------------------------------------- + +-spec start(dialyzer_typesig:servers()) -> pid(). + +start(Servers) -> + {ok, Pid} = gen_server:start(?MODULE, {self(), Servers}, []), + Pid. + +-spec scc_spawn(scc(), coordinator()) -> ok. + +scc_spawn(SCC, Coordinator) -> + cast({scc_spawn, SCC}, Coordinator). + +-spec sccs_to_pids_request([scc()], coordinator()) -> ok. + +sccs_to_pids_request(SCCs, Coordinator) -> + cast({sccs_to_pids, SCCs, self()}, Coordinator). + +scc_to_pids_request_handle(Worker, SCCs, SCCtoPID) -> + Pids = [fetch_map(SCC, SCCtoPID) || SCC <- SCCs], + Worker ! {sccs_to_pids, Pids}, + ok. + +-spec sccs_to_pids_reply() -> [dialyzer_worker:worker()]. + +sccs_to_pids_reply() -> + receive {sccs_to_pids, Pids} -> Pids end. + +-spec scc_done(scc(), scc(), coordinator()) -> ok. + +scc_done(SCC, NotFixpoint, Coordinator) -> + cast({scc_done, SCC, NotFixpoint}, Coordinator). + +-spec all_spawned(coordinator()) -> ok. + +all_spawned(Coordinator) -> + cast(all_spawned, Coordinator). + +send_done_to_parent(#state{parent = Parent, not_fixpoint = NotFixpoint}) -> + Parent ! {not_fixpoint, NotFixpoint}. + +-spec receive_not_fixpoint() -> dialyzer_plt:plt(). + +receive_not_fixpoint() -> + receive {not_fixpoint, NotFixpoint} -> NotFixpoint end. + +%%-------------------------------------------------------------------- + +-spec init([]) -> {ok, #state{}}. + +init({Parent, Servers}) -> + {ok, #state{parent = Parent, servers = Servers}}. + +-spec handle_call(Query::term(), From::term(), #state{}) -> + {reply, Reply::term(), #state{}}. + +handle_call(_Request, _From, State) -> + {reply, ok, State}. + +-spec handle_cast(Msg::term(), #state{}) -> + {noreply, #state{}} | {stop, normal, #state{}}. + +handle_cast({scc_done, _SCC, NotFixpoint}, + #state{spawn_count = SpawnCount, + all_spawned = AllSpawned, + not_fixpoint = OldNotFixpoint + } = State) -> + NewNotFixpoint = ordsets:union(OldNotFixpoint, NotFixpoint), + UpdatedState = State#state{not_fixpoint = NewNotFixpoint}, + Action = + case AllSpawned of + false -> reduce; + true -> + case SpawnCount of + 1 -> finish; + _ -> reduce + end + end, + case Action of + reduce -> + NewState = UpdatedState#state{spawn_count = SpawnCount - 1}, + {noreply, NewState}; + finish -> + send_done_to_parent(UpdatedState), + {stop, normal, State} + end; +handle_cast(all_spawned, #state{spawn_count = SpawnCount} = State) -> + case SpawnCount of + 0 -> + send_done_to_parent(State), + {stop, normal, State}; + _ -> + NewState = State#state{all_spawned = true}, + {noreply, NewState} + end; +handle_cast({sccs_to_pids, SCCs, Worker}, + #state{scc_to_pid = SCCtoPID} = State) -> + scc_to_pids_request_handle(Worker, SCCs, SCCtoPID), + {noreply, State}; +handle_cast({scc_spawn, SCC}, + #state{servers = Servers, + spawn_count = SpawnCount, + scc_to_pid = SCCtoPID + } = State) -> + Pid = dialyzer_worker:launch(SCC, Servers), + {noreply, + State#state{spawn_count = SpawnCount + 1, + scc_to_pid = store_map(SCC, Pid, SCCtoPID)} + }. + +-spec handle_info(term(), #state{}) -> {noreply, #state{}}. + +handle_info(_Info, State) -> + {noreply, State}. + +-spec terminate(term(), #state{}) -> ok. + +terminate(_Reason, _State) -> + ok. + +-spec code_change(term(), #state{}, term()) -> {ok, #state{}}. + +code_change(_OldVsn, State, _Extra) -> + {ok, State}. + +%%-------------------------------------------------------------------- + +cast(Message, Coordinator) -> + gen_server:cast(Coordinator, Message). + +new_map() -> + dict:new(). + +store_map(Key, Value, Map) -> + dict:store(Key, Value, Map). + +fetch_map(Key, Map) -> + dict:fetch(Key, Map). diff --git a/lib/dialyzer/src/dialyzer_succ_typings.erl b/lib/dialyzer/src/dialyzer_succ_typings.erl index 76b6d8954b..237adb36ea 100644 --- a/lib/dialyzer/src/dialyzer_succ_typings.erl +++ b/lib/dialyzer/src/dialyzer_succ_typings.erl @@ -303,18 +303,18 @@ compare_types_1([], [], _Strict, NotFixpoint) -> find_succ_typings(SCCs, #st{codeserver = Codeserver, callgraph = Callgraph, plt = Plt} = State) -> Servers = {Codeserver, dialyzer_callgraph:mini_callgraph(Callgraph), Plt}, - Coordinator = dialyzer_typesig_coordinator:start(Servers), + Coordinator = dialyzer_coordinator:start(Servers), find_succ_typings(SCCs, State, Coordinator). find_succ_typings([SCC|Rest], #st{parent = Parent} = State, Coordinator) -> Msg = io_lib:format("Typesig analysis for SCC: ~w\n", [format_scc(SCC)]), ?debug("~s", [Msg]), send_log(Parent, Msg), - dialyzer_typesig_coordinator:scc_spawn(SCC, Coordinator), + dialyzer_coordinator:scc_spawn(SCC, Coordinator), find_succ_typings(Rest, State, Coordinator); find_succ_typings([], State, Coordinator) -> - dialyzer_typesig_coordinator:all_spawned(Coordinator), - NotFixpoint = dialyzer_typesig_coordinator:receive_not_fixpoint(), + dialyzer_coordinator:all_spawned(Coordinator), + NotFixpoint = dialyzer_coordinator:receive_not_fixpoint(), ?debug("==================== Typesig done ====================\n\n", []), case NotFixpoint =:= [] of true -> {fixpoint, State}; diff --git a/lib/dialyzer/src/dialyzer_typesig_coordinator.erl b/lib/dialyzer/src/dialyzer_typesig_coordinator.erl deleted file mode 100644 index 9475bc6895..0000000000 --- a/lib/dialyzer/src/dialyzer_typesig_coordinator.erl +++ /dev/null @@ -1,215 +0,0 @@ -%% -*- erlang-indent-level: 2 -*- -%%----------------------------------------------------------------------- -%% %CopyrightBegin% -%% -%% Copyright Ericsson AB 2006-2011. All Rights Reserved. -%% -%% The contents of this file are subject to the Erlang Public License, -%% Version 1.1, (the "License"); you may not use this file except in -%% compliance with the License. You should have received a copy of the -%% Erlang Public License along with this software. If not, it can be -%% retrieved online at http://www.erlang.org/. -%% -%% Software distributed under the License is distributed on an "AS IS" -%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See -%% the License for the specific language governing rights and limitations -%% under the License. -%% -%% %CopyrightEnd% -%% - -%%%------------------------------------------------------------------- -%%% File : dialyzer_typesig_coordinator.erl -%%% Authors : Stavros Aronis -%%% -%%% Description: -%%% -%%% The parallel version of Dialyzer's typesig analysis is spread over 4 modules -%%% with the intention to both minimize the changes on the original code and use -%%% a separate module for every kind of Erlang process that will be running. -%%% -%%% There are therefore 3 kinds of processes: -%%% -%%% - The original Dialyzer backend (in succ_typings module) -%%% - The worker process for the typesig analysis (in typesig and -%%% typesig_worker) -%%% - A coordinator of the worker processes (in typesig_coordinator) -%%% -%%% Operation guidelines: -%%% -%%% - The backend requests from the coordinator to spawn a worker for each SCC -%%% - The backend notifies the coordinator when all SCC have been spawned and -%%% waits for the server to report that the PLT has been updated -%%% - Each worker is responsible to notify all those who wait for it. -%%% -%%%------------------------------------------------------------------- - --module(dialyzer_typesig_coordinator). - --export([ - all_spawned/1, - scc_done/3, - scc_spawn/2, - sccs_to_pids_reply/0, - sccs_to_pids_request/2, - start/1, - receive_not_fixpoint/0 - ]). - --behaviour(gen_server). - --export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, - code_change/3]). - --type coordinator() :: pid(). --type map() :: dict(). --type scc() :: [mfa_or_funlbl()]. - --record(state, {parent :: pid(), - spawn_count = 0 :: integer(), - all_spawned = false :: boolean(), - scc_to_pid = new_map() :: map(), - not_fixpoint = [] :: [mfa_or_funlbl()], - servers :: dialyzer_typesig:servers() - }). - --include("dialyzer.hrl"). - -%%-------------------------------------------------------------------- - --spec start(dialyzer_typesig:servers()) -> pid(). - -start(Servers) -> - {ok, Pid} = gen_server:start(?MODULE, {self(), Servers}, []), - Pid. - --spec scc_spawn(scc(), coordinator()) -> ok. - -scc_spawn(SCC, Coordinator) -> - cast({scc_spawn, SCC}, Coordinator). - --spec sccs_to_pids_request([scc()], coordinator()) -> ok. - -sccs_to_pids_request(SCCs, Coordinator) -> - cast({sccs_to_pids, SCCs, self()}, Coordinator). - -scc_to_pids_request_handle(Worker, SCCs, SCCtoPID) -> - Pids = [fetch_map(SCC, SCCtoPID) || SCC <- SCCs], - Worker ! {sccs_to_pids, Pids}, - ok. - --spec sccs_to_pids_reply() -> [dialyzer_typesig_worker:worker()]. - -sccs_to_pids_reply() -> - receive {sccs_to_pids, Pids} -> Pids end. - --spec scc_done(scc(), scc(), coordinator()) -> ok. - -scc_done(SCC, NotFixpoint, Coordinator) -> - cast({scc_done, SCC, NotFixpoint}, Coordinator). - --spec all_spawned(coordinator()) -> ok. - -all_spawned(Coordinator) -> - cast(all_spawned, Coordinator). - -send_done_to_parent(#state{parent = Parent, not_fixpoint = NotFixpoint}) -> - Parent ! {not_fixpoint, NotFixpoint}. - --spec receive_not_fixpoint() -> dialyzer_plt:plt(). - -receive_not_fixpoint() -> - receive {not_fixpoint, NotFixpoint} -> NotFixpoint end. - -%%-------------------------------------------------------------------- - --spec init([]) -> {ok, #state{}}. - -init({Parent, Servers}) -> - {ok, #state{parent = Parent, servers = Servers}}. - --spec handle_call(Query::term(), From::term(), #state{}) -> - {reply, Reply::term(), #state{}}. - -handle_call(_Request, _From, State) -> - {reply, ok, State}. - --spec handle_cast(Msg::term(), #state{}) -> - {noreply, #state{}} | {stop, normal, #state{}}. - -handle_cast({scc_done, _SCC, NotFixpoint}, - #state{spawn_count = SpawnCount, - all_spawned = AllSpawned, - not_fixpoint = OldNotFixpoint - } = State) -> - NewNotFixpoint = ordsets:union(OldNotFixpoint, NotFixpoint), - UpdatedState = State#state{not_fixpoint = NewNotFixpoint}, - Action = - case AllSpawned of - false -> reduce; - true -> - case SpawnCount of - 1 -> finish; - _ -> reduce - end - end, - case Action of - reduce -> - NewState = UpdatedState#state{spawn_count = SpawnCount - 1}, - {noreply, NewState}; - finish -> - send_done_to_parent(UpdatedState), - {stop, normal, State} - end; -handle_cast(all_spawned, #state{spawn_count = SpawnCount} = State) -> - case SpawnCount of - 0 -> - send_done_to_parent(State), - {stop, normal, State}; - _ -> - NewState = State#state{all_spawned = true}, - {noreply, NewState} - end; -handle_cast({sccs_to_pids, SCCs, Worker}, - #state{scc_to_pid = SCCtoPID} = State) -> - scc_to_pids_request_handle(Worker, SCCs, SCCtoPID), - {noreply, State}; -handle_cast({scc_spawn, SCC}, - #state{servers = Servers, - spawn_count = SpawnCount, - scc_to_pid = SCCtoPID - } = State) -> - Pid = dialyzer_typesig_worker:launch(SCC, Servers), - {noreply, - State#state{spawn_count = SpawnCount + 1, - scc_to_pid = store_map(SCC, Pid, SCCtoPID)} - }. - --spec handle_info(term(), #state{}) -> {noreply, #state{}}. - -handle_info(_Info, State) -> - {noreply, State}. - --spec terminate(term(), #state{}) -> ok. - -terminate(_Reason, _State) -> - ok. - --spec code_change(term(), #state{}, term()) -> {ok, #state{}}. - -code_change(_OldVsn, State, _Extra) -> - {ok, State}. - -%%-------------------------------------------------------------------- - -cast(Message, Coordinator) -> - gen_server:cast(Coordinator, Message). - -new_map() -> - dict:new(). - -store_map(Key, Value, Map) -> - dict:store(Key, Value, Map). - -fetch_map(Key, Map) -> - dict:fetch(Key, Map). diff --git a/lib/dialyzer/src/dialyzer_typesig_worker.erl b/lib/dialyzer/src/dialyzer_typesig_worker.erl deleted file mode 100644 index 3a7cd22eed..0000000000 --- a/lib/dialyzer/src/dialyzer_typesig_worker.erl +++ /dev/null @@ -1,142 +0,0 @@ -%% -*- erlang-indent-level: 2 -*- -%%----------------------------------------------------------------------- -%% %CopyrightBegin% -%% -%% Copyright Ericsson AB 2006-2010. All Rights Reserved. -%% -%% The contents of this file are subject to the Erlang Public License, -%% Version 1.1, (the "License"); you may not use this file except in -%% compliance with the License. You should have received a copy of the -%% Erlang Public License along with this software. If not, it can be -%% retrieved online at http://www.erlang.org/. -%% -%% Software distributed under the License is distributed on an "AS IS" -%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See -%% the License for the specific language governing rights and limitations -%% under the License. -%% -%% %CopyrightEnd% -%% - --module(dialyzer_typesig_worker). - --export([launch/2]). - --type worker() :: pid(). - --record(state, { - scc = [] :: mfa_or_funlbl(), - depends_on = [] :: list(), - coordinator :: dialyzer_coordinator:coordinator(), - servers :: dialyzer_typesig:servers(), - scc_data :: dialyzer_typesig:scc_data() - }). - --include("dialyzer.hrl"). - -%% -define(DEBUG, true). - --ifdef(DEBUG). --define(debug(X__, Y__), io:format(X__, Y__)). --else. --define(debug(X__, Y__), ok). --endif. - -%%-------------------------------------------------------------------- - --spec launch([mfa_or_funlbl()], dialyzer_typesig:servers()) -> worker(). - -launch(SCC, Servers) -> - State = #state{scc = SCC, - servers = Servers, - coordinator = self()}, - spawn(fun() -> loop(initializing, State) end). - -%%-------------------------------------------------------------------- - -loop(updating, State) -> - ?debug("Update: ~p\n",[State#state.scc]), - NextStatus = - case waits_more_success_typings(State) of - true -> waiting; - Other -> - case has_data(State) of - false -> getting_data; - true -> - case Other of - imminent -> waiting; - false -> running - end - end - end, - loop(NextStatus, State); -loop(initializing, #state{scc = SCC, servers = Servers} = State) -> - DependsOn = dialyzer_succ_typings:find_depends_on(SCC, Servers), - WithoutSelf = DependsOn -- [SCC], - ?debug("Deps ~p: ~p\n",[State#state.scc, WithoutSelf]), - loop(updating, State#state{depends_on = WithoutSelf}); -loop(waiting, State) -> - ?debug("Wait: ~p\n",[State#state.scc]), - NewState = wait_for_success_typings(State), - loop(updating, NewState); -loop(getting_data, State) -> - ?debug("Data: ~p\n",[State#state.scc]), - loop(updating, get_typesig_data(State)); -loop(running, State) -> - ?debug("Run: ~p\n",[State#state.scc]), - ok = ask_coordinator_for_callers(State), - NotFixpoint = find_succ_typings(State), - Callers = get_callers_reply_from_coordinator(), - ok = broadcast_own_succ_typings(State, Callers), - report_to_coordinator(NotFixpoint, State). - -waits_more_success_typings(#state{depends_on = Depends}) -> - case Depends of - [] -> false; - [_] -> imminent; - _ -> true - end. - -has_data(#state{scc_data = Data}) -> - case Data of - undefined -> false; - _ -> true - end. - -get_typesig_data(#state{scc = SCC, servers = Servers} = State) -> - State#state{scc_data = dialyzer_succ_typings:collect_scc_data(SCC, Servers)}. - -ask_coordinator_for_callers(#state{scc = SCC, - servers = Servers, - coordinator = Coordinator}) -> - RequiredBy = dialyzer_succ_typings:find_required_by(SCC, Servers), - WithoutSelf = RequiredBy -- [SCC], - ?debug("Waiting for me~p: ~p\n",[SCC, WithoutSelf]), - dialyzer_typesig_coordinator:sccs_to_pids_request(WithoutSelf, Coordinator). - -get_callers_reply_from_coordinator() -> - dialyzer_typesig_coordinator:sccs_to_pids_reply(). - -broadcast_own_succ_typings(#state{scc = SCC}, Callers) -> - ?debug("Sending ~p: ~p\n",[SCC, Callers]), - SendSTFun = fun(PID) -> PID ! {done, SCC} end, - lists:foreach(SendSTFun, Callers). - -wait_for_success_typings(#state{depends_on = DependsOn} = State) -> - receive - {done, SCC} -> - ?debug("GOT ~p: ~p\n",[State#state.scc, SCC]), - State#state{depends_on = DependsOn -- [SCC]} - after - 5000 -> - ?debug("Still Waiting ~p: ~p\n",[State#state.scc, DependsOn]), - State - end. - -find_succ_typings(#state{scc_data = SCCData}) -> - dialyzer_succ_typings:find_succ_types_for_scc(SCCData). - -report_to_coordinator(NotFixpoint, - #state{scc = SCC, coordinator = Coordinator}) -> - ?debug("Done: ~p\n",[SCC]), - dialyzer_typesig_coordinator:scc_done(SCC, NotFixpoint, Coordinator). diff --git a/lib/dialyzer/src/dialyzer_worker.erl b/lib/dialyzer/src/dialyzer_worker.erl new file mode 100644 index 0000000000..0bfdcf6bdb --- /dev/null +++ b/lib/dialyzer/src/dialyzer_worker.erl @@ -0,0 +1,142 @@ +%% -*- erlang-indent-level: 2 -*- +%%----------------------------------------------------------------------- +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2006-2010. All Rights Reserved. +%% +%% The contents of this file are subject to the Erlang Public License, +%% Version 1.1, (the "License"); you may not use this file except in +%% compliance with the License. You should have received a copy of the +%% Erlang Public License along with this software. If not, it can be +%% retrieved online at http://www.erlang.org/. +%% +%% Software distributed under the License is distributed on an "AS IS" +%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +%% the License for the specific language governing rights and limitations +%% under the License. +%% +%% %CopyrightEnd% +%% + +-module(dialyzer_worker). + +-export([launch/2]). + +-type worker() :: pid(). + +-record(state, { + scc = [] :: mfa_or_funlbl(), + depends_on = [] :: list(), + coordinator :: dialyzer_coordinator:coordinator(), + servers :: dialyzer_typesig:servers(), + scc_data :: dialyzer_typesig:scc_data() + }). + +-include("dialyzer.hrl"). + +%% -define(DEBUG, true). + +-ifdef(DEBUG). +-define(debug(X__, Y__), io:format(X__, Y__)). +-else. +-define(debug(X__, Y__), ok). +-endif. + +%%-------------------------------------------------------------------- + +-spec launch([mfa_or_funlbl()], dialyzer_typesig:servers()) -> worker(). + +launch(SCC, Servers) -> + State = #state{scc = SCC, + servers = Servers, + coordinator = self()}, + spawn(fun() -> loop(initializing, State) end). + +%%-------------------------------------------------------------------- + +loop(updating, State) -> + ?debug("Update: ~p\n",[State#state.scc]), + NextStatus = + case waits_more_success_typings(State) of + true -> waiting; + Other -> + case has_data(State) of + false -> getting_data; + true -> + case Other of + imminent -> waiting; + false -> running + end + end + end, + loop(NextStatus, State); +loop(initializing, #state{scc = SCC, servers = Servers} = State) -> + DependsOn = dialyzer_succ_typings:find_depends_on(SCC, Servers), + WithoutSelf = DependsOn -- [SCC], + ?debug("Deps ~p: ~p\n",[State#state.scc, WithoutSelf]), + loop(updating, State#state{depends_on = WithoutSelf}); +loop(waiting, State) -> + ?debug("Wait: ~p\n",[State#state.scc]), + NewState = wait_for_success_typings(State), + loop(updating, NewState); +loop(getting_data, State) -> + ?debug("Data: ~p\n",[State#state.scc]), + loop(updating, get_typesig_data(State)); +loop(running, State) -> + ?debug("Run: ~p\n",[State#state.scc]), + ok = ask_coordinator_for_callers(State), + NotFixpoint = find_succ_typings(State), + Callers = get_callers_reply_from_coordinator(), + ok = broadcast_own_succ_typings(State, Callers), + report_to_coordinator(NotFixpoint, State). + +waits_more_success_typings(#state{depends_on = Depends}) -> + case Depends of + [] -> false; + [_] -> imminent; + _ -> true + end. + +has_data(#state{scc_data = Data}) -> + case Data of + undefined -> false; + _ -> true + end. + +get_typesig_data(#state{scc = SCC, servers = Servers} = State) -> + State#state{scc_data = dialyzer_succ_typings:collect_scc_data(SCC, Servers)}. + +ask_coordinator_for_callers(#state{scc = SCC, + servers = Servers, + coordinator = Coordinator}) -> + RequiredBy = dialyzer_succ_typings:find_required_by(SCC, Servers), + WithoutSelf = RequiredBy -- [SCC], + ?debug("Waiting for me~p: ~p\n",[SCC, WithoutSelf]), + dialyzer_coordinator:sccs_to_pids_request(WithoutSelf, Coordinator). + +get_callers_reply_from_coordinator() -> + dialyzer_coordinator:sccs_to_pids_reply(). + +broadcast_own_succ_typings(#state{scc = SCC}, Callers) -> + ?debug("Sending ~p: ~p\n",[SCC, Callers]), + SendSTFun = fun(PID) -> PID ! {done, SCC} end, + lists:foreach(SendSTFun, Callers). + +wait_for_success_typings(#state{depends_on = DependsOn} = State) -> + receive + {done, SCC} -> + ?debug("GOT ~p: ~p\n",[State#state.scc, SCC]), + State#state{depends_on = DependsOn -- [SCC]} + after + 5000 -> + ?debug("Still Waiting ~p: ~p\n",[State#state.scc, DependsOn]), + State + end. + +find_succ_typings(#state{scc_data = SCCData}) -> + dialyzer_succ_typings:find_succ_types_for_scc(SCCData). + +report_to_coordinator(NotFixpoint, + #state{scc = SCC, coordinator = Coordinator}) -> + ?debug("Done: ~p\n",[SCC]), + dialyzer_coordinator:scc_done(SCC, NotFixpoint, Coordinator). -- cgit v1.2.3 From 12c5985b862c5e8e7e88033a21e909b51225d76f Mon Sep 17 00:00:00 2001 From: Stavros Aronis Date: Fri, 17 Feb 2012 18:47:58 +0100 Subject: Parallel dataflow --- lib/dialyzer/src/dialyzer_callgraph.erl | 9 ++++---- lib/dialyzer/src/dialyzer_coordinator.erl | 21 ++++++++++-------- lib/dialyzer/src/dialyzer_succ_typings.erl | 35 ++++++++++++++++++------------ lib/dialyzer/src/dialyzer_worker.erl | 35 ++++++++++++++++++++---------- 4 files changed, 60 insertions(+), 40 deletions(-) (limited to 'lib/dialyzer') diff --git a/lib/dialyzer/src/dialyzer_callgraph.erl b/lib/dialyzer/src/dialyzer_callgraph.erl index 7379fbc3c4..385f5621d6 100644 --- a/lib/dialyzer/src/dialyzer_callgraph.erl +++ b/lib/dialyzer/src/dialyzer_callgraph.erl @@ -262,9 +262,8 @@ module_postorder(#callgraph{digraph = DG}) -> MDG = digraph:new([acyclic]), MDG1 = digraph_confirm_vertices(Nodes, MDG), MDG2 = create_module_digraph(Edges, MDG1), - PostOrder = digraph_utils:postorder(MDG2), - digraph:delete(MDG2), - PostOrder. + PostOrder = digraph_utils:topsort(MDG2), + {PostOrder, MDG2}. %% The module deps of a module are modules that depend on the module -spec module_deps(callgraph()) -> dict(). @@ -341,9 +340,9 @@ reset_from_funs(Funs, #callgraph{digraph = DG, module_postorder_from_funs(Funs, #callgraph{digraph = DG} = CG) -> SubGraph = digraph_reaching_subgraph(Funs, DG), - PO = module_postorder(CG#callgraph{digraph = SubGraph}), + {PO, Active} = module_postorder(CG#callgraph{digraph = SubGraph}), digraph_delete(SubGraph), - PO. + {PO, CG#callgraph{active_digraph = Active}}. ets_lookup_dict(Key, Table) -> try ets:lookup_element(Table, Key, 2) of diff --git a/lib/dialyzer/src/dialyzer_coordinator.erl b/lib/dialyzer/src/dialyzer_coordinator.erl index a72d383365..fa78670883 100644 --- a/lib/dialyzer/src/dialyzer_coordinator.erl +++ b/lib/dialyzer/src/dialyzer_coordinator.erl @@ -52,7 +52,7 @@ scc_spawn/2, sccs_to_pids_reply/0, sccs_to_pids_request/2, - start/1, + start/2, receive_not_fixpoint/0 ]). @@ -64,8 +64,10 @@ -type coordinator() :: pid(). -type map() :: dict(). -type scc() :: [mfa_or_funlbl()]. +-type mode() :: typesig | dataflow. -record(state, {parent :: pid(), + mode :: mode(), spawn_count = 0 :: integer(), all_spawned = false :: boolean(), scc_to_pid = new_map() :: map(), @@ -77,10 +79,10 @@ %%-------------------------------------------------------------------- --spec start(dialyzer_typesig:servers()) -> pid(). +-spec start(mode(), dialyzer_typesig:servers()) -> pid(). -start(Servers) -> - {ok, Pid} = gen_server:start(?MODULE, {self(), Servers}, []), +start(Mode, Servers) -> + {ok, Pid} = gen_server:start(?MODULE, {self(), Mode, Servers}, []), Pid. -spec scc_spawn(scc(), coordinator()) -> ok. @@ -123,10 +125,10 @@ receive_not_fixpoint() -> %%-------------------------------------------------------------------- --spec init([]) -> {ok, #state{}}. +-spec init({pid(), mode(), dialyzer_succ_typings:servers()}) -> {ok, #state{}}. -init({Parent, Servers}) -> - {ok, #state{parent = Parent, servers = Servers}}. +init({Parent, Mode, Servers}) -> + {ok, #state{parent = Parent, mode = Mode, servers = Servers}}. -spec handle_call(Query::term(), From::term(), #state{}) -> {reply, Reply::term(), #state{}}. @@ -175,11 +177,12 @@ handle_cast({sccs_to_pids, SCCs, Worker}, scc_to_pids_request_handle(Worker, SCCs, SCCtoPID), {noreply, State}; handle_cast({scc_spawn, SCC}, - #state{servers = Servers, + #state{mode = Mode, + servers = Servers, spawn_count = SpawnCount, scc_to_pid = SCCtoPID } = State) -> - Pid = dialyzer_worker:launch(SCC, Servers), + Pid = dialyzer_worker:launch(Mode, SCC, Servers), {noreply, State#state{spawn_count = SpawnCount + 1, scc_to_pid = store_map(SCC, Pid, SCCtoPID)} diff --git a/lib/dialyzer/src/dialyzer_succ_typings.erl b/lib/dialyzer/src/dialyzer_succ_typings.erl index 237adb36ea..90d851bc3c 100644 --- a/lib/dialyzer/src/dialyzer_succ_typings.erl +++ b/lib/dialyzer/src/dialyzer_succ_typings.erl @@ -31,7 +31,9 @@ analyze_callgraph/4, get_warnings/6, find_succ_types_for_scc/1, + refine_one_module/1, collect_scc_data/2, + collect_refine_scc_data/2, find_required_by/2, find_depends_on/2 ]). @@ -98,9 +100,10 @@ get_refined_success_typings(SCCs, State) -> {not_fixpoint, NotFixpoint1, State1} -> Callgraph = State1#st.callgraph, NotFixpoint2 = [lookup_name(F, Callgraph) || F <- NotFixpoint1], - ModulePostorder = + {ModulePostorder, ModCallgraph} = dialyzer_callgraph:module_postorder_from_funs(NotFixpoint2, Callgraph), - case refine_succ_typings(ModulePostorder, State1) of + ModState = State1#st{callgraph = ModCallgraph}, + case refine_succ_typings(ModulePostorder, ModState) of {fixpoint, State2} -> State2; {not_fixpoint, NotFixpoint3, State2} -> @@ -187,23 +190,27 @@ postprocess_dataflow_warns([{?WARN_CONTRACT_RANGE, {CallF, CallL}, Msg}|Rest], postprocess_dataflow_warns([W|Rest], State, Wacc, Acc) -> postprocess_dataflow_warns(Rest, State, Wacc, [W|Acc]). -refine_succ_typings(ModulePostorder, State) -> +refine_succ_typings(ModulePostorder, #st{codeserver = Codeserver, + callgraph = Callgraph, + plt = Plt} = State) -> ?debug("Module postorder: ~p\n", [ModulePostorder]), - refine_succ_typings(ModulePostorder, State, []). + Servers = {Codeserver, Callgraph, Plt}, + Coordinator = dialyzer_coordinator:start(dataflow, Servers), + refine_succ_typings(ModulePostorder, State, Coordinator). -refine_succ_typings([M|Rest], State, Fixpoint) -> - #st{callgraph = Callgraph, codeserver = CodeServer, plt = PLT} = State, +refine_succ_typings([M|Rest], State, Coordinator) -> Msg = io_lib:format("Dataflow of module: ~w\n", [M]), send_log(State#st.parent, Msg), ?debug("~s\n", [Msg]), - Data = collect_refine_scc_data(M, {CodeServer, Callgraph, PLT}), - FixpointFromScc = refine_one_module(Data), - NewFixpoint = ordsets:union(Fixpoint, FixpointFromScc), - refine_succ_typings(Rest, State, NewFixpoint); -refine_succ_typings([], State, Fixpoint) -> - case Fixpoint =:= [] of + dialyzer_coordinator:scc_spawn(M, Coordinator), + refine_succ_typings(Rest, State, Coordinator); +refine_succ_typings([], State, Coordinator) -> + dialyzer_coordinator:all_spawned(Coordinator), + NotFixpoint = dialyzer_coordinator:receive_not_fixpoint(), + ?debug("==================== Dataflow done ====================\n\n", []), + case NotFixpoint =:= [] of true -> {fixpoint, State}; - false -> {not_fixpoint, Fixpoint, State} + false -> {not_fixpoint, NotFixpoint, State} end. -type servers() :: term(). @@ -303,7 +310,7 @@ compare_types_1([], [], _Strict, NotFixpoint) -> find_succ_typings(SCCs, #st{codeserver = Codeserver, callgraph = Callgraph, plt = Plt} = State) -> Servers = {Codeserver, dialyzer_callgraph:mini_callgraph(Callgraph), Plt}, - Coordinator = dialyzer_coordinator:start(Servers), + Coordinator = dialyzer_coordinator:start(typesig, Servers), find_succ_typings(SCCs, State, Coordinator). find_succ_typings([SCC|Rest], #st{parent = Parent} = State, Coordinator) -> diff --git a/lib/dialyzer/src/dialyzer_worker.erl b/lib/dialyzer/src/dialyzer_worker.erl index 0bfdcf6bdb..a2d30c27d3 100644 --- a/lib/dialyzer/src/dialyzer_worker.erl +++ b/lib/dialyzer/src/dialyzer_worker.erl @@ -20,11 +20,12 @@ -module(dialyzer_worker). --export([launch/2]). +-export([launch/3]). -type worker() :: pid(). -record(state, { + mode :: dialyzer_coordinator:mode(), scc = [] :: mfa_or_funlbl(), depends_on = [] :: list(), coordinator :: dialyzer_coordinator:coordinator(), @@ -44,10 +45,12 @@ %%-------------------------------------------------------------------- --spec launch([mfa_or_funlbl()], dialyzer_typesig:servers()) -> worker(). +-spec launch(dialyzer_coordinator:mode(), [mfa_or_funlbl()], + dialyzer_typesig:servers()) -> worker(). -launch(SCC, Servers) -> - State = #state{scc = SCC, +launch(Mode, SCC, Servers) -> + State = #state{mode = Mode, + scc = SCC, servers = Servers, coordinator = self()}, spawn(fun() -> loop(initializing, State) end). @@ -81,13 +84,13 @@ loop(waiting, State) -> loop(updating, NewState); loop(getting_data, State) -> ?debug("Data: ~p\n",[State#state.scc]), - loop(updating, get_typesig_data(State)); + loop(updating, get_data(State)); loop(running, State) -> ?debug("Run: ~p\n",[State#state.scc]), ok = ask_coordinator_for_callers(State), - NotFixpoint = find_succ_typings(State), + NotFixpoint = do_work(State), Callers = get_callers_reply_from_coordinator(), - ok = broadcast_own_succ_typings(State, Callers), + ok = broadcast_done(State, Callers), report_to_coordinator(NotFixpoint, State). waits_more_success_typings(#state{depends_on = Depends}) -> @@ -103,8 +106,13 @@ has_data(#state{scc_data = Data}) -> _ -> true end. -get_typesig_data(#state{scc = SCC, servers = Servers} = State) -> - State#state{scc_data = dialyzer_succ_typings:collect_scc_data(SCC, Servers)}. +get_data(#state{mode = Mode, scc = SCC, servers = Servers} = State) -> + Data = + case Mode of + typesig -> dialyzer_succ_typings:collect_scc_data(SCC, Servers); + dataflow -> dialyzer_succ_typings:collect_refine_scc_data(SCC, Servers) + end, + State#state{scc_data = Data}. ask_coordinator_for_callers(#state{scc = SCC, servers = Servers, @@ -117,7 +125,7 @@ ask_coordinator_for_callers(#state{scc = SCC, get_callers_reply_from_coordinator() -> dialyzer_coordinator:sccs_to_pids_reply(). -broadcast_own_succ_typings(#state{scc = SCC}, Callers) -> +broadcast_done(#state{scc = SCC}, Callers) -> ?debug("Sending ~p: ~p\n",[SCC, Callers]), SendSTFun = fun(PID) -> PID ! {done, SCC} end, lists:foreach(SendSTFun, Callers). @@ -133,8 +141,11 @@ wait_for_success_typings(#state{depends_on = DependsOn} = State) -> State end. -find_succ_typings(#state{scc_data = SCCData}) -> - dialyzer_succ_typings:find_succ_types_for_scc(SCCData). +do_work(#state{mode = Mode, scc_data = SCCData}) -> + case Mode of + typesig -> dialyzer_succ_typings:find_succ_types_for_scc(SCCData); + dataflow -> dialyzer_succ_typings:refine_one_module(SCCData) + end. report_to_coordinator(NotFixpoint, #state{scc = SCC, coordinator = Coordinator}) -> -- cgit v1.2.3 From af4247993e9b68bd123df37d08b06690c0640259 Mon Sep 17 00:00:00 2001 From: Stavros Aronis Date: Sat, 18 Feb 2012 15:59:08 +0100 Subject: Callgraph is ets based from the start --- lib/dialyzer/src/dialyzer_callgraph.erl | 120 ++++++++++++++------------------ 1 file changed, 52 insertions(+), 68 deletions(-) (limited to 'lib/dialyzer') diff --git a/lib/dialyzer/src/dialyzer_callgraph.erl b/lib/dialyzer/src/dialyzer_callgraph.erl index 385f5621d6..9eba64dd57 100644 --- a/lib/dialyzer/src/dialyzer_callgraph.erl +++ b/lib/dialyzer/src/dialyzer_callgraph.erl @@ -92,12 +92,12 @@ -record(callgraph, {digraph = digraph:new() :: digraph(), active_digraph :: digraph(), - esc = sets:new() :: set() | ets:tid(), - name_map = dict:new() :: dict() | ets:tid(), - rev_name_map = dict:new() :: dict() | ets:tid(), - rec_var_map = dict:new() :: dict() | ets:tid(), - self_rec = sets:new() :: set() | ets:tid(), - calls = dict:new() :: dict() | ets:tid(), + esc :: ets:tid(), + name_map :: ets:tid(), + rev_name_map :: ets:tid(), + rec_var_map :: ets:tid(), + self_rec :: ets:tid(), + calls :: ets:tid(), race_detection = false :: boolean(), race_data_server = new_race_data_server() :: pid()}). @@ -115,7 +115,16 @@ -spec new() -> callgraph(). new() -> - #callgraph{}. + [ETSEsc, ETSNameMap, ETSRevNameMap, ETSRecVarMap, ETSSelfRec, ETSCalls] = + [ets:new(N,[public]) || + N <- [callgraph_esc, callgraph_name_map, callgraph_rev_name_map, + callgraph_rec_var_map, callgraph_self_rec, callgraph_calls]], + #callgraph{esc = ETSEsc, + name_map = ETSNameMap, + rev_name_map = ETSRevNameMap, + rec_var_map = ETSRecVarMap, + self_rec = ETSSelfRec, + calls = ETSCalls}. -spec mini_callgraph(callgraph()) -> callgraph(). @@ -299,32 +308,9 @@ create_module_digraph([], MDG) -> -spec finalize(callgraph()) -> {[scc()], callgraph()}. -finalize(#callgraph{digraph = DG, - esc = Esc, - name_map = NameMap, - rev_name_map = RevNameMap, - rec_var_map = RecVarMap, - self_rec = SelfRec, - calls = Calls - } = CG) -> - [ETSEsc, ETSNameMap, ETSRevNameMap, ETSRecVarMap, ETSSelfRec, ETSCalls] = - [ets:new(N,[public]) || - N <- [callgraph_esc, callgraph_name_map, callgraph_rev_name_map, - callgraph_rec_var_map, callgraph_self_rec, callgraph_calls]], - [true,true] = [ets:insert(ETS, [{E} || E <- sets:to_list(Data)]) || - {ETS, Data} <- [{ETSEsc, Esc}, {ETSSelfRec, SelfRec}]], - [true, true, true, true] = - [ets:insert(ETS, dict:to_list(Data)) || - {ETS, Data} <- [{ETSNameMap, NameMap}, {ETSRevNameMap, RevNameMap}, - {ETSRecVarMap, RecVarMap}, {ETSCalls, Calls}]], +finalize(#callgraph{digraph = DG} = CG) -> {ActiveDG, Postorder} = digraph_finalize(DG), - {Postorder, CG#callgraph{active_digraph = ActiveDG, - esc = ETSEsc, - name_map = ETSNameMap, - rev_name_map = ETSRevNameMap, - rec_var_map = ETSRecVarMap, - self_rec = ETSSelfRec, - calls = ETSCalls}}. + {Postorder, CG#callgraph{active_digraph = ActiveDG}}. -spec reset_from_funs([mfa_or_funlbl()], callgraph()) -> {[scc()], callgraph()}. @@ -367,34 +353,34 @@ ets_lookup_set(Key, Table) -> -spec scan_core_tree(cerl:c_module(), callgraph()) -> callgraph(). -scan_core_tree(Tree, #callgraph{calls = OldCalls, - esc = OldEsc, - name_map = OldNameMap, - rec_var_map = OldRecVarMap, - rev_name_map = OldRevNameMap, - self_rec = OldSelfRec} = CG) -> +scan_core_tree(Tree, #callgraph{calls = ETSCalls, + esc = ETSEsc, + name_map = ETSNameMap, + rec_var_map = ETSRecVarMap, + rev_name_map = ETSRevNameMap, + self_rec = ETSSelfRec} = CG) -> %% Build name map and recursion variable maps. - {NewNameMap, NewRevNameMap, NewRecVarMap} = - build_maps(Tree, OldRecVarMap, OldNameMap, OldRevNameMap), + build_maps(Tree, ETSRecVarMap, ETSNameMap, ETSRevNameMap), %% First find the module-local dependencies. {Deps0, EscapingFuns, Calls} = dialyzer_dep:analyze(Tree), - NewCalls = dict:merge(fun(_Key, Val, Val) -> Val end, OldCalls, Calls), - NewEsc = sets:union(sets:from_list(EscapingFuns), OldEsc), + true = ets:insert(ETSCalls, dict:to_list(Calls)), + true = ets:insert(ETSEsc, [{E} || E <- EscapingFuns]), + LabelEdges = get_edges_from_deps(Deps0), %% Find the self recursive functions. Named functions get both the %% key and their name for convenience. SelfRecs0 = lists:foldl(fun({Key, Key}, Acc) -> - case dict:find(Key, NewNameMap) of + case ets_lookup_dict(Key, ETSNameMap) of error -> [Key|Acc]; {ok, Name} -> [Key, Name|Acc] end; (_, Acc) -> Acc end, [], LabelEdges), - SelfRecs = sets:union(sets:from_list(SelfRecs0), OldSelfRec), + true = ets:insert(ETSSelfRec, [{S} || S <- SelfRecs0]), - NamedEdges1 = name_edges(LabelEdges, NewNameMap), + NamedEdges1 = name_edges(LabelEdges, ETSNameMap), %% We need to scan for inter-module calls since these are not tracked %% by dialyzer_dep. Note that the caller is always recorded as the @@ -413,27 +399,25 @@ scan_core_tree(Tree, #callgraph{calls = OldCalls, NewNamedEdges1 = [E || {From, To} = E <- NamedEdges1, From =/= top, To =/= top], NamedEdges3 = NewNamedEdges1 ++ NewNamedEdges2, - CG1 = add_edges(NamedEdges3, Names3, CG), - CG1#callgraph{calls = NewCalls, - esc = NewEsc, - name_map = NewNameMap, - rec_var_map = NewRecVarMap, - rev_name_map = NewRevNameMap, - self_rec = SelfRecs}. - -build_maps(Tree, RecVarMap, NameMap, RevNameMap) -> + add_edges(NamedEdges3, Names3, CG). + +build_maps(Tree, ETSRecVarMap, ETSNameMap, ETSRevNameMap) -> %% We only care about the named (top level) functions. The anonymous %% functions will be analysed together with their parents. Defs = cerl:module_defs(Tree), Mod = cerl:atom_val(cerl:module_name(Tree)), - lists:foldl(fun({Var, Function}, {AccNameMap, AccRevNameMap, AccRecVarMap}) -> - FunName = cerl:fname_id(Var), - Arity = cerl:fname_arity(Var), - MFA = {Mod, FunName, Arity}, - {dict:store(get_label(Function), MFA, AccNameMap), - dict:store(MFA, get_label(Function), AccRevNameMap), - dict:store(get_label(Var), MFA, AccRecVarMap)} - end, {NameMap, RevNameMap, RecVarMap}, Defs). + Fun = + fun({Var, Function}) -> + FunName = cerl:fname_id(Var), + Arity = cerl:fname_arity(Var), + MFA = {Mod, FunName, Arity}, + FunLabel = get_label(Function), + VarLabel = get_label(Var), + true = ets:insert(ETSNameMap, {FunLabel, MFA}), + true = ets:insert(ETSRevNameMap, {MFA, FunLabel}), + true = ets:insert(ETSRecVarMap, {VarLabel, MFA}) + end, + lists:foreach(Fun, Defs). get_edges_from_deps(Deps) -> %% Convert the dependencies as produced by dialyzer_dep to a list of @@ -446,22 +430,22 @@ get_edges_from_deps(Deps) -> end, [], Deps), lists:flatten(Edges). -name_edges(Edges, NameMap) -> +name_edges(Edges, ETSNameMap) -> %% If a label is present in the name map it is renamed. Otherwise %% keep the label as the identity. MapFun = fun(X) -> - case dict:find(X, NameMap) of + case ets_lookup_dict(X, ETSNameMap) of error -> X; {ok, MFA} -> MFA end end, - name_edges(Edges, MapFun, NameMap, []). + name_edges(Edges, MapFun, []). -name_edges([{From, To}|Left], MapFun, NameMap, Acc) -> +name_edges([{From, To}|Left], MapFun, Acc) -> NewFrom = MapFun(From), NewTo = MapFun(To), - name_edges(Left, MapFun, NameMap, [{NewFrom, NewTo}|Acc]); -name_edges([], _MapFun, _NameMap, Acc) -> + name_edges(Left, MapFun, [{NewFrom, NewTo}|Acc]); +name_edges([], _MapFun, Acc) -> Acc. scan_core_funs(Tree) -> -- cgit v1.2.3 From 08d6fa6c97be82c4b4a480ec04aa06ae8e781783 Mon Sep 17 00:00:00 2001 From: Stavros Aronis Date: Sun, 19 Feb 2012 01:40:54 +0100 Subject: Parallel compilation of files under analysis --- lib/dialyzer/src/dialyzer_analysis_callgraph.erl | 105 +++++----- lib/dialyzer/src/dialyzer_callgraph.erl | 7 +- lib/dialyzer/src/dialyzer_coordinator.erl | 235 +++++++++++++++-------- lib/dialyzer/src/dialyzer_worker.erl | 62 ++++-- 4 files changed, 263 insertions(+), 146 deletions(-) (limited to 'lib/dialyzer') diff --git a/lib/dialyzer/src/dialyzer_analysis_callgraph.erl b/lib/dialyzer/src/dialyzer_analysis_callgraph.erl index 7060028d17..e009b8f43f 100644 --- a/lib/dialyzer/src/dialyzer_analysis_callgraph.erl +++ b/lib/dialyzer/src/dialyzer_analysis_callgraph.erl @@ -30,6 +30,11 @@ -export([start/3]). +-export([compile_coordinator_init/0, + add_to_result/3, + start_compilation/2, + continue_compilation/2]). + -include("dialyzer.hrl"). -record(analysis_state, @@ -212,33 +217,15 @@ compile_and_store(Files, #analysis_state{codeserver = CServer, Includes = [{i, D} || D <- Dirs], Defines = [{d, Macro, Val} || {Macro, Val} <- Defs], Callgraph = dialyzer_callgraph:new(), - Fun = case StartFrom of - src_code -> - fun(File, {TmpCG, TmpCServer, TmpFailed, TmpNoWarn, TmpMods}) -> - case compile_src(File, Includes, Defines, TmpCG, - TmpCServer, UseContracts) of - {error, Reason} -> - {TmpCG, TmpCServer, [{File, Reason}|TmpFailed], TmpNoWarn, - TmpMods}; - {ok, NewCG, NoWarn, NewCServer, Mod} -> - {NewCG, NewCServer, TmpFailed, NoWarn++TmpNoWarn, - [Mod|TmpMods]} - end - end; - byte_code -> - fun(File, {TmpCG, TmpCServer, TmpFailed, TmpNoWarn, TmpMods}) -> - case compile_byte(File, TmpCG, TmpCServer, UseContracts) of - {error, Reason} -> - {TmpCG, TmpCServer, [{File, Reason}|TmpFailed], TmpNoWarn, - TmpMods}; - {ok, NewCG, NoWarn, NewCServer, Mod} -> - {NewCG, NewCServer, TmpFailed, NoWarn++TmpNoWarn, - [Mod|TmpMods]} - end - end - end, - {NewCallgraph1, NewCServer, Failed, NoWarn, Modules} = - lists:foldl(Fun, {Callgraph, CServer, [], [], []}, Files), + Servers = {Callgraph, CServer, StartFrom, Includes, Defines, UseContracts}, + Coordinator = dialyzer_coordinator:start(compile, Servers), + Spawner = fun(F) -> dialyzer_coordinator:compiler_spawn(F, Coordinator) end, + lists:foreach(Spawner, Files), + dialyzer_coordinator:all_spawned(Coordinator), + {{V, E, Failed, NoWarn, Modules}, NextLabel} = + dialyzer_coordinator:receive_compilation_data(), + CServer2 = dialyzer_codeserver:set_next_core_label(NextLabel, CServer), + Callgraph = dialyzer_callgraph:add_edges(E, V, Callgraph), case Failed =:= [] of true -> NewFiles = lists:zip(lists:reverse(Modules), Files), @@ -254,11 +241,41 @@ compile_and_store(Files, #analysis_state{codeserver = CServer, {T2, _} = statistics(runtime), Msg1 = io_lib:format("done in ~.2f secs\nRemoving edges... ", [(T2-T1)/1000]), send_log(Parent, Msg1), - NewCallgraph2 = cleanup_callgraph(State, NewCServer, NewCallgraph1, Modules), + Callgraph = cleanup_callgraph(State, CServer2, Callgraph, Modules), {T3, _} = statistics(runtime), Msg2 = io_lib:format("done in ~.2f secs\n", [(T3-T2)/1000]), send_log(Parent, Msg2), - {NewCallgraph2, sets:from_list(NoWarn), NewCServer}. + {Callgraph, sets:from_list(NoWarn), CServer2}. + +-type servers() :: term(). +-type result() :: term(). +-type file_result() :: term(). +-type data() :: term(). + +-spec compile_coordinator_init() -> result(). + +compile_coordinator_init() -> {[], [], [], [], []}. + +-spec add_to_result(file:filename(), file_result(), result()) -> result(). + +add_to_result(File, NewData, {V, E, Failed, NoWarn, Mods}) -> + case NewData of + {error, Reason} -> + {[{File, Reason}|Failed], NoWarn, Mods}; + {ok, NV, NE, NewNoWarn, Mod} -> + {NV ++ V, NE ++ E, Failed, NewNoWarn ++ NoWarn, [Mod|Mods]} + end. + +-spec start_compilation(file:filename(), servers()) -> data(). + +start_compilation(File, {Callgraph, Codeserver, StartFrom, + Includes, Defines, UseContracts}) -> + case StartFrom of + src_code -> + compile_src(File, Includes, Defines, Callgraph, Codeserver, UseContracts); + byte_code -> + compile_byte(File, Callgraph, Codeserver, UseContracts) + end. cleanup_callgraph(#analysis_state{plt = InitPlt, parent = Parent, codeserver = CodeServer @@ -348,10 +365,17 @@ compile_common(File, AbstrCode, CompOpts, Callgraph, CServer, UseContracts) -> store_core(Mod, Core, NoWarn, Callgraph, CServer) -> Exp = get_exports_from_core(Core), ExpTypes = get_exported_types_from_core(Core), - CServer1 = dialyzer_codeserver:insert_exports(Exp, CServer), - CServer2 = dialyzer_codeserver:insert_temp_exported_types(ExpTypes, CServer1), - {LabeledCore, CServer3} = label_core(Core, CServer2), - store_code_and_build_callgraph(Mod, LabeledCore, Callgraph, CServer3, NoWarn). + CServer = dialyzer_codeserver:insert_exports(Exp, CServer), + CServer = dialyzer_codeserver:insert_temp_exported_types(ExpTypes, CServer), + CoreTree = cerl:from_records(Core), + {cerl_trees:size(CoreTree), {Mod, CoreTree, NoWarn, Callgraph, CServer}}. + +-spec continue_compilation(integer(), data()) -> {integer(), file_result()}. + +continue_compilation(NextLabel, {Mod, CoreTree, NoWarn, Callgraph, CServer}) -> + {LabeledTree, _NewNextLabel} = cerl_trees:label(CoreTree, NextLabel), + LabeledCore = cerl:to_records(LabeledTree), + store_code_and_build_callgraph(Mod, LabeledCore, Callgraph, NoWarn, CServer). abs_get_nowarn(Abs, M) -> Opts = lists:flatten([C || {attribute, _, compile, C} <- Abs]), @@ -384,18 +408,11 @@ get_exports_from_core(Core) -> M = cerl:atom_val(cerl:module_name(Tree)), [{M, F, A} || {F, A} <- Exports2]. -label_core(Core, CServer) -> - NextLabel = dialyzer_codeserver:get_next_core_label(CServer), - CoreTree = cerl:from_records(Core), - {LabeledTree, NewNextLabel} = cerl_trees:label(CoreTree, NextLabel), - {cerl:to_records(LabeledTree), - dialyzer_codeserver:set_next_core_label(NewNextLabel, CServer)}. - -store_code_and_build_callgraph(Mod, Core, Callgraph, CServer, NoWarn) -> +store_code_and_build_callgraph(Mod, Core, Callgraph, NoWarn, CServer) -> CoreTree = cerl:from_records(Core), - NewCallgraph = dialyzer_callgraph:scan_core_tree(CoreTree, Callgraph), - CServer2 = dialyzer_codeserver:insert(Mod, CoreTree, CServer), - {ok, NewCallgraph, NoWarn, CServer2, Mod}. + {Vertices, Edges} = dialyzer_callgraph:scan_core_tree(CoreTree, Callgraph), + CServer = dialyzer_codeserver:insert(Mod, CoreTree, CServer), + {ok, Vertices, Edges, NoWarn, Mod}. %%-------------------------------------------------------------------- %% Utilities diff --git a/lib/dialyzer/src/dialyzer_callgraph.erl b/lib/dialyzer/src/dialyzer_callgraph.erl index 9eba64dd57..bf939655d0 100644 --- a/lib/dialyzer/src/dialyzer_callgraph.erl +++ b/lib/dialyzer/src/dialyzer_callgraph.erl @@ -28,6 +28,7 @@ -module(dialyzer_callgraph). -export([add_edges/2, + add_edges/3, all_nodes/1, delete/1, finalize/1, @@ -358,10 +359,10 @@ scan_core_tree(Tree, #callgraph{calls = ETSCalls, name_map = ETSNameMap, rec_var_map = ETSRecVarMap, rev_name_map = ETSRevNameMap, - self_rec = ETSSelfRec} = CG) -> + self_rec = ETSSelfRec}) -> %% Build name map and recursion variable maps. build_maps(Tree, ETSRecVarMap, ETSNameMap, ETSRevNameMap), - + %% First find the module-local dependencies. {Deps0, EscapingFuns, Calls} = dialyzer_dep:analyze(Tree), true = ets:insert(ETSCalls, dict:to_list(Calls)), @@ -399,7 +400,7 @@ scan_core_tree(Tree, #callgraph{calls = ETSCalls, NewNamedEdges1 = [E || {From, To} = E <- NamedEdges1, From =/= top, To =/= top], NamedEdges3 = NewNamedEdges1 ++ NewNamedEdges2, - add_edges(NamedEdges3, Names3, CG). + {Names3, NamedEdges3}. build_maps(Tree, ETSRecVarMap, ETSNameMap, ETSRevNameMap) -> %% We only care about the named (top level) functions. The anonymous diff --git a/lib/dialyzer/src/dialyzer_coordinator.erl b/lib/dialyzer/src/dialyzer_coordinator.erl index fa78670883..d8a3ef2bd2 100644 --- a/lib/dialyzer/src/dialyzer_coordinator.erl +++ b/lib/dialyzer/src/dialyzer_coordinator.erl @@ -46,15 +46,26 @@ -module(dialyzer_coordinator). --export([ - all_spawned/1, - scc_done/3, - scc_spawn/2, +%%% Exports for all possible uses of coordinator +-export([start/2, + all_spawned/1]). + +%%% Exports for the typesig and dataflow analysis main process +-export([scc_spawn/2, + receive_not_fixpoint/0]). + +%%% Exports for the typesig and dataflow analysis workers +-export([scc_done/3, sccs_to_pids_reply/0, - sccs_to_pids_request/2, - start/2, - receive_not_fixpoint/0 - ]). + sccs_to_pids_request/2]). + +%%% Exports for the compilation main process +-export([compiler_spawn/2, + receive_compilation_data/0]). + +%%% Exports for the compilation workers +-export([compilation_done/3, + get_next_label/2]). -behaviour(gen_server). @@ -64,129 +75,192 @@ -type coordinator() :: pid(). -type map() :: dict(). -type scc() :: [mfa_or_funlbl()]. --type mode() :: typesig | dataflow. +-type mode() :: 'typesig' | 'dataflow' | 'compile'. -record(state, {parent :: pid(), mode :: mode(), spawn_count = 0 :: integer(), all_spawned = false :: boolean(), - scc_to_pid = new_map() :: map(), - not_fixpoint = [] :: [mfa_or_funlbl()], - servers :: dialyzer_typesig:servers() + job_to_pid :: map(), + next_label :: integer(), + result :: [mfa_or_funlbl()] | + dialyzer_analysis_callgraph:result(), + init_job_data :: dialyzer_typesig:servers() }). -include("dialyzer.hrl"). %%-------------------------------------------------------------------- --spec start(mode(), dialyzer_typesig:servers()) -> pid(). +-spec start('typesig' | 'dataflow', dialyzer_typesig:servers()) -> pid(); + ('compile', dialyzer_analysis_callgraph:servers()) -> pid(). start(Mode, Servers) -> - {ok, Pid} = gen_server:start(?MODULE, {self(), Mode, Servers}, []), - Pid. + {ok, Pid} = gen_server:start(?MODULE, {self(), Mode, Servers}, []), + Pid. -spec scc_spawn(scc(), coordinator()) -> ok. scc_spawn(SCC, Coordinator) -> - cast({scc_spawn, SCC}, Coordinator). + cast({scc_spawn, SCC}, Coordinator). -spec sccs_to_pids_request([scc()], coordinator()) -> ok. sccs_to_pids_request(SCCs, Coordinator) -> - cast({sccs_to_pids, SCCs, self()}, Coordinator). + cast({sccs_to_pids, SCCs, self()}, Coordinator). scc_to_pids_request_handle(Worker, SCCs, SCCtoPID) -> - Pids = [fetch_map(SCC, SCCtoPID) || SCC <- SCCs], - Worker ! {sccs_to_pids, Pids}, - ok. + Pids = [fetch_map(SCC, SCCtoPID) || SCC <- SCCs], + Worker ! {sccs_to_pids, Pids}, + ok. -spec sccs_to_pids_reply() -> [dialyzer_worker:worker()]. sccs_to_pids_reply() -> - receive {sccs_to_pids, Pids} -> Pids end. + receive {sccs_to_pids, Pids} -> Pids end. -spec scc_done(scc(), scc(), coordinator()) -> ok. scc_done(SCC, NotFixpoint, Coordinator) -> - cast({scc_done, SCC, NotFixpoint}, Coordinator). + cast({done, SCC, NotFixpoint}, Coordinator). + +-spec compilation_done(file:filename(), + dialyzer_analysis_callgraph:compilation_data(), + coordinator()) -> ok. + +compilation_done(Filename, CompilationData, Coordinator) -> + cast({done, Filename, CompilationData}, Coordinator). -spec all_spawned(coordinator()) -> ok. all_spawned(Coordinator) -> - cast(all_spawned, Coordinator). - -send_done_to_parent(#state{parent = Parent, not_fixpoint = NotFixpoint}) -> - Parent ! {not_fixpoint, NotFixpoint}. + cast(all_spawned, Coordinator). + +send_done_to_parent(#state{mode = Mode, + parent = Parent, + result = Result, + next_label = NextLabel}) -> + Msg = + case Mode of + X when X =:= 'typesig'; X =:= 'dataflow' -> {not_fixpoint, Result}; + 'compile' -> {compilation_data, Result, NextLabel} + end, + Parent ! Msg. -spec receive_not_fixpoint() -> dialyzer_plt:plt(). receive_not_fixpoint() -> - receive {not_fixpoint, NotFixpoint} -> NotFixpoint end. + receive {not_fixpoint, NotFixpoint} -> NotFixpoint end. -%%-------------------------------------------------------------------- +-spec receive_compilation_data() -> + {dialyzer_analysis_callgraph:result(), integer()}. + +receive_compilation_data() -> + receive {compilation_data, CompilationData, NextLabel} -> + {CompilationData, NextLabel} + end. + +-spec compiler_spawn(file:filename(), coordinator()) -> ok. --spec init({pid(), mode(), dialyzer_succ_typings:servers()}) -> {ok, #state{}}. +compiler_spawn(Filename, Coordinator) -> + cast({compiler_spawn, Filename}, Coordinator). -init({Parent, Mode, Servers}) -> - {ok, #state{parent = Parent, mode = Mode, servers = Servers}}. +-spec get_next_label(integer(), coordinator()) -> integer(). + +get_next_label(EstimatedSize, Coordinator) -> + call({get_next_label, EstimatedSize}, Coordinator). + +%%-------------------------------------------------------------------- + +-spec init({pid(), mode(), dialyzer_succ_typings:servers() | + dialyzer_analysis_callgraph:servers()}) -> {ok, #state{}}. + +init({Parent, Mode, InitJobData}) -> + InitState = #state{parent = Parent, mode = Mode, init_job_data = InitJobData}, + State = + case Mode of + X when X =:= 'typesig'; X =:= 'dataflow' -> + InitState#state{result = [], job_to_pid = new_map()}; + 'compile' -> + InitResult = dialyzer_analysis_callgraph:compile_coordinator_init(), + InitState#state{result = InitResult, next_label = 0} + end, + {ok, State}. -spec handle_call(Query::term(), From::term(), #state{}) -> - {reply, Reply::term(), #state{}}. + {reply, Reply::term(), #state{}}. -handle_call(_Request, _From, State) -> - {reply, ok, State}. +handle_call({get_next_label, EstimatedSize}, _From, + #state{next_label = NextLabel} = State) -> + {reply, NextLabel, State#state{next_label = NextLabel + EstimatedSize}}. -spec handle_cast(Msg::term(), #state{}) -> - {noreply, #state{}} | {stop, normal, #state{}}. + {noreply, #state{}} | {stop, normal, #state{}}. -handle_cast({scc_done, _SCC, NotFixpoint}, - #state{spawn_count = SpawnCount, +handle_cast({done, Job, NewData}, + #state{mode = Mode, + spawn_count = SpawnCount, all_spawned = AllSpawned, - not_fixpoint = OldNotFixpoint + result = OldResult } = State) -> - NewNotFixpoint = ordsets:union(OldNotFixpoint, NotFixpoint), - UpdatedState = State#state{not_fixpoint = NewNotFixpoint}, - Action = - case AllSpawned of - false -> reduce; - true -> - case SpawnCount of - 1 -> finish; - _ -> reduce - end - end, - case Action of - reduce -> - NewState = UpdatedState#state{spawn_count = SpawnCount - 1}, - {noreply, NewState}; - finish -> - send_done_to_parent(UpdatedState), - {stop, normal, State} - end; + NewResult = + case Mode of + X when X =:= 'typesig'; X =:= 'dataflow' -> + ordsets:union(OldResult, NewData); + 'compile' -> + dialyzer_analysis_callgraph:add_to_result(Job, NewData, OldResult) + end, + UpdatedState = State#state{result = NewResult}, + Action = + case AllSpawned of + false -> reduce; + true -> + case SpawnCount of + 1 -> finish; + _ -> reduce + end + end, + case Action of + reduce -> + NewState = UpdatedState#state{spawn_count = SpawnCount - 1}, + {noreply, NewState}; + finish -> + send_done_to_parent(UpdatedState), + {stop, normal, State} + end; handle_cast(all_spawned, #state{spawn_count = SpawnCount} = State) -> - case SpawnCount of - 0 -> - send_done_to_parent(State), - {stop, normal, State}; - _ -> - NewState = State#state{all_spawned = true}, - {noreply, NewState} - end; + case SpawnCount of + 0 -> + send_done_to_parent(State), + {stop, normal, State}; + _ -> + NewState = State#state{all_spawned = true}, + {noreply, NewState} + end; handle_cast({sccs_to_pids, SCCs, Worker}, - #state{scc_to_pid = SCCtoPID} = State) -> - scc_to_pids_request_handle(Worker, SCCs, SCCtoPID), - {noreply, State}; + #state{job_to_pid = SCCtoPID} = State) -> + scc_to_pids_request_handle(Worker, SCCs, SCCtoPID), + {noreply, State}; handle_cast({scc_spawn, SCC}, #state{mode = Mode, - servers = Servers, + init_job_data = Servers, spawn_count = SpawnCount, - scc_to_pid = SCCtoPID + job_to_pid = SCCtoPID } = State) -> - Pid = dialyzer_worker:launch(Mode, SCC, Servers), - {noreply, - State#state{spawn_count = SpawnCount + 1, - scc_to_pid = store_map(SCC, Pid, SCCtoPID)} - }. + Pid = dialyzer_worker:launch(Mode, SCC, Servers), + {noreply, + State#state{spawn_count = SpawnCount + 1, + job_to_pid = store_map(SCC, Pid, SCCtoPID)} + }; +handle_cast({compiler_spawn, Filename}, + #state{mode = Mode, + init_job_data = Servers, + spawn_count = SpawnCount + } = State) -> + dialyzer_worker:launch(Mode, Filename, Servers), + {noreply, + State#state{spawn_count = SpawnCount + 1} + }. -spec handle_info(term(), #state{}) -> {noreply, #state{}}. @@ -206,13 +280,16 @@ code_change(_OldVsn, State, _Extra) -> %%-------------------------------------------------------------------- cast(Message, Coordinator) -> - gen_server:cast(Coordinator, Message). + gen_server:cast(Coordinator, Message). + +call(Message, Coordinator) -> + gen_server:call(Coordinator, Message). new_map() -> - dict:new(). + dict:new(). store_map(Key, Value, Map) -> - dict:store(Key, Value, Map). + dict:store(Key, Value, Map). fetch_map(Key, Map) -> - dict:fetch(Key, Map). + dict:fetch(Key, Map). diff --git a/lib/dialyzer/src/dialyzer_worker.erl b/lib/dialyzer/src/dialyzer_worker.erl index a2d30c27d3..b24103c5a8 100644 --- a/lib/dialyzer/src/dialyzer_worker.erl +++ b/lib/dialyzer/src/dialyzer_worker.erl @@ -26,10 +26,11 @@ -record(state, { mode :: dialyzer_coordinator:mode(), - scc = [] :: mfa_or_funlbl(), + job :: mfa_or_funlbl() | file:filename(), depends_on = [] :: list(), coordinator :: dialyzer_coordinator:coordinator(), - servers :: dialyzer_typesig:servers(), + servers :: dialyzer_typesig:servers() | + dialyzer_analysis_callgraph:servers(), scc_data :: dialyzer_typesig:scc_data() }). @@ -48,17 +49,22 @@ -spec launch(dialyzer_coordinator:mode(), [mfa_or_funlbl()], dialyzer_typesig:servers()) -> worker(). -launch(Mode, SCC, Servers) -> +launch(Mode, Job, Servers) -> State = #state{mode = Mode, - scc = SCC, + job = Job, servers = Servers, coordinator = self()}, - spawn(fun() -> loop(initializing, State) end). + InitState = + case Mode of + X when X =:= 'typesig'; X =:= 'dataflow' -> initializing; + 'compile' -> running + end, + spawn(fun() -> loop(InitState, State) end). %%-------------------------------------------------------------------- loop(updating, State) -> - ?debug("Update: ~p\n",[State#state.scc]), + ?debug("Update: ~p\n",[State#state.job]), NextStatus = case waits_more_success_typings(State) of true -> waiting; @@ -73,20 +79,27 @@ loop(updating, State) -> end end, loop(NextStatus, State); -loop(initializing, #state{scc = SCC, servers = Servers} = State) -> +loop(initializing, #state{job = SCC, servers = Servers} = State) -> DependsOn = dialyzer_succ_typings:find_depends_on(SCC, Servers), WithoutSelf = DependsOn -- [SCC], - ?debug("Deps ~p: ~p\n",[State#state.scc, WithoutSelf]), + ?debug("Deps ~p: ~p\n",[State#state.job, WithoutSelf]), loop(updating, State#state{depends_on = WithoutSelf}); loop(waiting, State) -> - ?debug("Wait: ~p\n",[State#state.scc]), + ?debug("Wait: ~p\n",[State#state.job]), NewState = wait_for_success_typings(State), loop(updating, NewState); loop(getting_data, State) -> - ?debug("Data: ~p\n",[State#state.scc]), + ?debug("Data: ~p\n",[State#state.job]), loop(updating, get_data(State)); -loop(running, State) -> - ?debug("Run: ~p\n",[State#state.scc]), +loop(running, #state{mode = 'compile'} = State) -> + ?debug("Compile: ~s\n",[State#state.job]), + {EstimatedSize, Data} = start_compilation(State), + Label = ask_coordinator_for_label(EstimatedSize, State), + Result = continue_compilation(Label, Data), + report_to_coordinator(Result, State); +loop(running, #state{mode = Mode} = State) when + Mode =:= 'typesig'; Mode =:= 'dataflow' -> + ?debug("Run: ~p\n",[State#state.job]), ok = ask_coordinator_for_callers(State), NotFixpoint = do_work(State), Callers = get_callers_reply_from_coordinator(), @@ -106,7 +119,7 @@ has_data(#state{scc_data = Data}) -> _ -> true end. -get_data(#state{mode = Mode, scc = SCC, servers = Servers} = State) -> +get_data(#state{mode = Mode, job = SCC, servers = Servers} = State) -> Data = case Mode of typesig -> dialyzer_succ_typings:collect_scc_data(SCC, Servers); @@ -114,7 +127,7 @@ get_data(#state{mode = Mode, scc = SCC, servers = Servers} = State) -> end, State#state{scc_data = Data}. -ask_coordinator_for_callers(#state{scc = SCC, +ask_coordinator_for_callers(#state{job = SCC, servers = Servers, coordinator = Coordinator}) -> RequiredBy = dialyzer_succ_typings:find_required_by(SCC, Servers), @@ -125,7 +138,7 @@ ask_coordinator_for_callers(#state{scc = SCC, get_callers_reply_from_coordinator() -> dialyzer_coordinator:sccs_to_pids_reply(). -broadcast_done(#state{scc = SCC}, Callers) -> +broadcast_done(#state{job = SCC}, Callers) -> ?debug("Sending ~p: ~p\n",[SCC, Callers]), SendSTFun = fun(PID) -> PID ! {done, SCC} end, lists:foreach(SendSTFun, Callers). @@ -133,11 +146,11 @@ broadcast_done(#state{scc = SCC}, Callers) -> wait_for_success_typings(#state{depends_on = DependsOn} = State) -> receive {done, SCC} -> - ?debug("GOT ~p: ~p\n",[State#state.scc, SCC]), + ?debug("GOT ~p: ~p\n",[State#state.job, SCC]), State#state{depends_on = DependsOn -- [SCC]} after 5000 -> - ?debug("Still Waiting ~p: ~p\n",[State#state.scc, DependsOn]), + ?debug("Still Waiting ~p: ~p\n",[State#state.job, DependsOn]), State end. @@ -148,6 +161,15 @@ do_work(#state{mode = Mode, scc_data = SCCData}) -> end. report_to_coordinator(NotFixpoint, - #state{scc = SCC, coordinator = Coordinator}) -> - ?debug("Done: ~p\n",[SCC]), - dialyzer_coordinator:scc_done(SCC, NotFixpoint, Coordinator). + #state{job = Job, coordinator = Coordinator}) -> + ?debug("Done: ~p\n",[Job]), + dialyzer_coordinator:scc_done(Job, NotFixpoint, Coordinator). + +start_compilation(#state{job = Job, servers = Servers}) -> + dialyzer_analysis_callgraph:start_compilation(Job, Servers). + +ask_coordinator_for_label(EstimatedSize, #state{coordinator = Coordinator}) -> + dialyzer_coordinator:get_next_label(EstimatedSize, Coordinator). + +continue_compilation(Label, Data) -> + dialyzer_analysis_callgraph:continue_compilation(Label, Data). -- cgit v1.2.3 From b8ab5bc3211fdee28000e2fd35a47dafa2a4316c Mon Sep 17 00:00:00 2001 From: Stavros Aronis Date: Sun, 19 Feb 2012 22:05:47 +0100 Subject: Fix types and specs in Dialyzer --- lib/dialyzer/src/dialyzer_analysis_callgraph.erl | 19 ++- lib/dialyzer/src/dialyzer_callgraph.erl | 29 +++-- lib/dialyzer/src/dialyzer_coordinator.erl | 63 +++++---- lib/dialyzer/src/dialyzer_dataflow.erl | 159 +---------------------- lib/dialyzer/src/dialyzer_succ_typings.erl | 148 ++------------------- lib/dialyzer/src/dialyzer_worker.erl | 45 ++++--- 6 files changed, 107 insertions(+), 356 deletions(-) (limited to 'lib/dialyzer') diff --git a/lib/dialyzer/src/dialyzer_analysis_callgraph.erl b/lib/dialyzer/src/dialyzer_analysis_callgraph.erl index e009b8f43f..febaf56e40 100644 --- a/lib/dialyzer/src/dialyzer_analysis_callgraph.erl +++ b/lib/dialyzer/src/dialyzer_analysis_callgraph.erl @@ -35,6 +35,10 @@ start_compilation/2, continue_compilation/2]). +-export_type([compilation_data/0, + result/0, + servers/0]). + -include("dialyzer.hrl"). -record(analysis_state, @@ -247,10 +251,10 @@ compile_and_store(Files, #analysis_state{codeserver = CServer, send_log(Parent, Msg2), {Callgraph, sets:from_list(NoWarn), CServer2}. --type servers() :: term(). --type result() :: term(). --type file_result() :: term(). --type data() :: term(). +-type servers() :: term(). %%opaque +-type result() :: term(). %%opaque +-type file_result() :: term(). %%opaque +-type compilation_data() :: term(). %%opaque -spec compile_coordinator_init() -> result(). @@ -266,7 +270,8 @@ add_to_result(File, NewData, {V, E, Failed, NoWarn, Mods}) -> {NV ++ V, NE ++ E, Failed, NewNoWarn ++ NoWarn, [Mod|Mods]} end. --spec start_compilation(file:filename(), servers()) -> data(). +-spec start_compilation(file:filename(), servers()) -> + {error, term()} |{ok, integer(), compilation_data()}. start_compilation(File, {Callgraph, Codeserver, StartFrom, Includes, Defines, UseContracts}) -> @@ -368,9 +373,9 @@ store_core(Mod, Core, NoWarn, Callgraph, CServer) -> CServer = dialyzer_codeserver:insert_exports(Exp, CServer), CServer = dialyzer_codeserver:insert_temp_exported_types(ExpTypes, CServer), CoreTree = cerl:from_records(Core), - {cerl_trees:size(CoreTree), {Mod, CoreTree, NoWarn, Callgraph, CServer}}. + {ok, cerl_trees:size(CoreTree), {Mod, CoreTree, NoWarn, Callgraph, CServer}}. --spec continue_compilation(integer(), data()) -> {integer(), file_result()}. +-spec continue_compilation(integer(), compilation_data()) -> file_result(). continue_compilation(NextLabel, {Mod, CoreTree, NoWarn, Callgraph, CServer}) -> {LabeledTree, _NewNextLabel} = cerl_trees:label(CoreTree, NextLabel), diff --git a/lib/dialyzer/src/dialyzer_callgraph.erl b/lib/dialyzer/src/dialyzer_callgraph.erl index bf939655d0..bb96ada65f 100644 --- a/lib/dialyzer/src/dialyzer_callgraph.erl +++ b/lib/dialyzer/src/dialyzer_callgraph.erl @@ -264,7 +264,7 @@ get_required_by(SCC, #callgraph{active_digraph = DG}) -> modules(#callgraph{digraph = DG}) -> ordsets:from_list([M || {M,_F,_A} <- digraph_vertices(DG)]). --spec module_postorder(callgraph()) -> [module()]. +-spec module_postorder(callgraph()) -> {[module()], digraph()}. module_postorder(#callgraph{digraph = DG}) -> Edges = digraph_edges(DG), @@ -323,7 +323,8 @@ reset_from_funs(Funs, #callgraph{digraph = DG, digraph_delete(SubGraph), {Postorder, CG#callgraph{active_digraph = NewActiveDG}}. --spec module_postorder_from_funs([mfa_or_funlbl()], callgraph()) -> [module()]. +-spec module_postorder_from_funs([mfa_or_funlbl()], callgraph()) -> + {[module()], callgraph()}. module_postorder_from_funs(Funs, #callgraph{digraph = DG} = CG) -> SubGraph = digraph_reaching_subgraph(Funs, DG), @@ -352,7 +353,8 @@ ets_lookup_set(Key, Table) -> %% The set of labels in the tree must be disjoint from the set of %% labels already occuring in the callgraph. --spec scan_core_tree(cerl:c_module(), callgraph()) -> callgraph(). +-spec scan_core_tree(cerl:c_module(), callgraph()) -> + {[mfa_or_funlbl()], [callgraph_edge()]}. scan_core_tree(Tree, #callgraph{calls = ETSCalls, esc = ETSEsc, @@ -595,9 +597,10 @@ renew_race_code(Races, #callgraph{race_data_server = RaceDataServer} = CG) -> ok = race_data_server_cast( {renew_race_code, {Fun, FunArgs, Code}}, RaceDataServer), - CG; -renew_race_code({Fun, FunArgs, Code}, - #race_data_state{race_code = RaceCode} = State) -> + CG. + +renew_race_code_handler({Fun, FunArgs, Code}, + #race_data_state{race_code = RaceCode} = State) -> State#race_data_state{race_code = dict:store(Fun, [FunArgs, Code], RaceCode)}. -spec renew_race_public_tables(label(), callgraph()) -> callgraph(). @@ -606,9 +609,11 @@ renew_race_public_tables(VarLabel, #callgraph{race_data_server = RaceDataServer} = CG) -> ok = race_data_server_cast({renew_race_public_tables, VarLabel}, RaceDataServer), - CG; -renew_race_public_tables(VarLabel, - #race_data_state{public_tables = PT} = State) -> + CG. + +renew_race_public_tables_handler(VarLabel, + #race_data_state{public_tables = PT} + = State) -> State#race_data_state{public_tables = ordsets:add_element(VarLabel, PT)}. -spec cleanup(callgraph()) -> callgraph(). @@ -739,8 +744,8 @@ race_data_server_handle_cast(race_code_new, State) -> race_data_server_handle_cast({Tag, Data}, State) -> case Tag of renew_race_info -> renew_race_info(Data, State); - renew_race_code -> renew_race_code(Data, State); - renew_race_public_tables -> renew_race_public_tables(Data, State); + renew_race_code -> renew_race_code_handler(Data, State); + renew_race_public_tables -> renew_race_public_tables_handler(Data, State); put_race_code -> State#race_data_state{race_code = Data}; put_public_tables -> State#race_data_state{public_tables = Data}; put_named_tables -> State#race_data_state{named_tables = Data}; @@ -775,7 +780,7 @@ to_dot(#callgraph{digraph = DG, esc = Esc} = CG, File) -> end end, Escaping = [{Fun(L), {color, red}} - || L <- sets:to_list(Esc), L =/= external], + || L <- [E || {E} <- ets:tab2list(Esc)], L =/= external], Vertices = digraph_edges(DG), hipe_dot:translate_list(Vertices, File, "CG", Escaping). diff --git a/lib/dialyzer/src/dialyzer_coordinator.erl b/lib/dialyzer/src/dialyzer_coordinator.erl index d8a3ef2bd2..9bcd7e4c63 100644 --- a/lib/dialyzer/src/dialyzer_coordinator.erl +++ b/lib/dialyzer/src/dialyzer_coordinator.erl @@ -46,17 +46,19 @@ -module(dialyzer_coordinator). -%%% Exports for all possible uses of coordinator +%%% Exports for all possible uses of coordinator from main process -export([start/2, all_spawned/1]). +%%% Exports for all possible workers +-export([job_done/3]). + %%% Exports for the typesig and dataflow analysis main process -export([scc_spawn/2, receive_not_fixpoint/0]). %%% Exports for the typesig and dataflow analysis workers --export([scc_done/3, - sccs_to_pids_reply/0, +-export([sccs_to_pids_reply/0, sccs_to_pids_request/2]). %%% Exports for the compilation main process @@ -67,39 +69,44 @@ -export([compilation_done/3, get_next_label/2]). +-export_type([coordinator/0, mode/0]). + -behaviour(gen_server). -export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]). --type coordinator() :: pid(). --type map() :: dict(). --type scc() :: [mfa_or_funlbl()]. --type mode() :: 'typesig' | 'dataflow' | 'compile'. - --record(state, {parent :: pid(), - mode :: mode(), - spawn_count = 0 :: integer(), - all_spawned = false :: boolean(), - job_to_pid :: map(), - next_label :: integer(), - result :: [mfa_or_funlbl()] | - dialyzer_analysis_callgraph:result(), - init_job_data :: dialyzer_typesig:servers() +-type coordinator() :: pid(). %%opaque + +-type map() :: dict(). +-type scc() :: [mfa_or_funlbl()]. +-type mode() :: 'typesig' | 'dataflow' | 'compile'. +-type servers() :: dialyzer_succ_typings:servers() | + dialyzer_analysis_callgraph:servers(). + +-record(state, {parent :: pid(), + mode :: mode(), + spawn_count = 0 :: integer(), + all_spawned = false :: boolean(), + job_to_pid :: map(), + next_label :: integer(), + result :: [mfa_or_funlbl()] | + dialyzer_analysis_callgraph:result(), + init_job_data :: servers() }). -include("dialyzer.hrl"). %%-------------------------------------------------------------------- --spec start('typesig' | 'dataflow', dialyzer_typesig:servers()) -> pid(); +-spec start('typesig' | 'dataflow', dialyzer_succ_typings:servers()) -> pid(); ('compile', dialyzer_analysis_callgraph:servers()) -> pid(). start(Mode, Servers) -> {ok, Pid} = gen_server:start(?MODULE, {self(), Mode, Servers}, []), Pid. --spec scc_spawn(scc(), coordinator()) -> ok. +-spec scc_spawn(scc() | module(), coordinator()) -> ok. scc_spawn(SCC, Coordinator) -> cast({scc_spawn, SCC}, Coordinator). @@ -119,10 +126,10 @@ scc_to_pids_request_handle(Worker, SCCs, SCCtoPID) -> sccs_to_pids_reply() -> receive {sccs_to_pids, Pids} -> Pids end. --spec scc_done(scc(), scc(), coordinator()) -> ok. +-spec job_done(scc() | file:filename(), term(), coordinator()) -> ok. -scc_done(SCC, NotFixpoint, Coordinator) -> - cast({done, SCC, NotFixpoint}, Coordinator). +job_done(Job, Result, Coordinator) -> + cast({done, Job, Result}, Coordinator). -spec compilation_done(file:filename(), dialyzer_analysis_callgraph:compilation_data(), @@ -145,9 +152,10 @@ send_done_to_parent(#state{mode = Mode, X when X =:= 'typesig'; X =:= 'dataflow' -> {not_fixpoint, Result}; 'compile' -> {compilation_data, Result, NextLabel} end, - Parent ! Msg. + Parent ! Msg, + ok. --spec receive_not_fixpoint() -> dialyzer_plt:plt(). +-spec receive_not_fixpoint() -> [mfa_or_funlbl()]. receive_not_fixpoint() -> receive {not_fixpoint, NotFixpoint} -> NotFixpoint end. @@ -172,8 +180,7 @@ get_next_label(EstimatedSize, Coordinator) -> %%-------------------------------------------------------------------- --spec init({pid(), mode(), dialyzer_succ_typings:servers() | - dialyzer_analysis_callgraph:servers()}) -> {ok, #state{}}. +-spec init({pid(), mode(), servers()}) -> {ok, #state{}}. init({Parent, Mode, InitJobData}) -> InitState = #state{parent = Parent, mode = Mode, init_job_data = InitJobData}, @@ -247,7 +254,7 @@ handle_cast({scc_spawn, SCC}, spawn_count = SpawnCount, job_to_pid = SCCtoPID } = State) -> - Pid = dialyzer_worker:launch(Mode, SCC, Servers), + Pid = dialyzer_worker:launch(Mode, SCC, Servers, self()), {noreply, State#state{spawn_count = SpawnCount + 1, job_to_pid = store_map(SCC, Pid, SCCtoPID)} @@ -257,7 +264,7 @@ handle_cast({compiler_spawn, Filename}, init_job_data = Servers, spawn_count = SpawnCount } = State) -> - dialyzer_worker:launch(Mode, Filename, Servers), + dialyzer_worker:launch(Mode, Filename, Servers, self()), {noreply, State#state{spawn_count = SpawnCount + 1} }. diff --git a/lib/dialyzer/src/dialyzer_dataflow.erl b/lib/dialyzer/src/dialyzer_dataflow.erl index 248ad328ab..cb376daf68 100644 --- a/lib/dialyzer/src/dialyzer_dataflow.erl +++ b/lib/dialyzer/src/dialyzer_dataflow.erl @@ -37,9 +37,6 @@ state__get_records/1, state__put_callgraph/2, state__put_races/2, state__records_only/1]). -%% Debug and test interfaces. --export([get_top_level_signatures/2, pp/1]). - -export_type([state/0]). -include("dialyzer.hrl"). @@ -68,7 +65,7 @@ %%-define(DEBUG, true). %%-define(DEBUG_PP, true). -%%-define(DOT, true). +%%-define(DEBUG_TIME, true). -ifdef(DEBUG). -import(erl_types, [t_to_string/1]). @@ -129,137 +126,12 @@ get_fun_types(Tree, Plt, Callgraph, Records) -> State = analyze_module(Tree, Plt, Callgraph, Records, false), state__all_fun_types(State). -%%-------------------------------------------------------------------- - --spec pp(file:filename()) -> 'ok'. - -pp(File) -> - {ok, Code} = dialyzer_utils:get_core_from_src(File, [no_copt]), - Plt = get_def_plt(), - AnnTree = annotate_module(Code, Plt), - io:put_chars(cerl_prettypr:format(AnnTree, [{hook, cerl_typean:pp_hook()}])), - io:nl(). - -%%-------------------------------------------------------------------- -%% This is used in the testsuite. - --spec get_top_level_signatures(cerl:c_module(), dict()) -> - [{{atom(), arity()}, erl_types:erl_type()}]. - -get_top_level_signatures(Code, Records) -> - {Tree, _} = cerl_trees:label(cerl:from_records(Code)), - Callgraph0 = dialyzer_callgraph:new(), - Callgraph1 = dialyzer_callgraph:scan_core_tree(Tree, Callgraph0), - {Callgraph2, _} = dialyzer_callgraph:remove_external(Callgraph1), - Callgraph = dialyzer_callgraph:finalize(Callgraph2), - to_dot(Callgraph), - Plt = get_def_plt(), - FunTypes = get_fun_types(Tree, Plt, Callgraph, Records), - FunTypes1 = lists:foldl(fun({V, F}, Acc) -> - Label = get_label(F), - case dict:find(Label, Acc) of - error -> - Arity = cerl:fname_arity(V), - Type = t_fun(lists:duplicate(Arity, - t_none()), - t_none()), - dict:store(Label, Type, Acc); - {ok, _} -> Acc - end - end, FunTypes, cerl:module_defs(Tree)), - dialyzer_callgraph:delete(Callgraph), - Sigs = [{{cerl:fname_id(V), cerl:fname_arity(V)}, - dict:fetch(get_label(F), FunTypes1)} - || {V, F} <- cerl:module_defs(Tree)], - ordsets:from_list(Sigs). - -get_def_plt() -> - try - dialyzer_plt:from_file(dialyzer_plt:get_default_plt()) - catch - throw:{dialyzer_error, _} -> dialyzer_plt:new() - end. - -%%% =========================================================================== -%%% -%%% Annotate all top level funs. -%%% -%%% =========================================================================== - -annotate_module(Code, Plt) -> - {Tree, _} = cerl_trees:label(cerl:from_records(Code)), - Callgraph0 = dialyzer_callgraph:new(), - Callgraph1 = dialyzer_callgraph:scan_core_tree(Tree, Callgraph0), - {Callgraph2, _} = dialyzer_callgraph:remove_external(Callgraph1), - Callgraph = dialyzer_callgraph:finalize(Callgraph2), - State = analyze_module(Tree, Plt, Callgraph), - Res = annotate(Tree, State), - dialyzer_callgraph:delete(Callgraph), - Res. - -annotate(Tree, State) -> - case cerl:subtrees(Tree) of - [] -> set_type(Tree, State); - List -> - NewSubTrees = [[annotate(Subtree, State) || Subtree <- Group] - || Group <- List], - NewTree = cerl:update_tree(Tree, NewSubTrees), - set_type(NewTree, State) - end. - -set_type(Tree, State) -> - case cerl:type(Tree) of - 'fun' -> - Type = state__fun_type(Tree, State), - case t_is_any(Type) of - true -> - cerl:set_ann(Tree, delete_ann(typesig, cerl:get_ann(Tree))); - false -> - cerl:set_ann(Tree, append_ann(typesig, Type, cerl:get_ann(Tree))) - end; - apply -> - case state__find_apply_return(Tree, State) of - unknown -> Tree; - ReturnType -> - case t_is_any(ReturnType) of - true -> - cerl:set_ann(Tree, delete_ann(type, cerl:get_ann(Tree))); - false -> - cerl:set_ann(Tree, append_ann(type, ReturnType, - cerl:get_ann(Tree))) - end - end; - _ -> - Tree - end. - -append_ann(Tag, Val, [X | Xs]) -> - if tuple_size(X) >= 1, element(1, X) =:= Tag -> - append_ann(Tag, Val, Xs); - true -> - [X | append_ann(Tag, Val, Xs)] - end; -append_ann(Tag, Val, []) -> - [{Tag, Val}]. - -delete_ann(Tag, [X | Xs]) -> - if tuple_size(X) >= 1, element(1, X) =:= Tag -> - delete_ann(Tag, Xs); - true -> - [X | delete_ann(Tag, Xs)] - end; -delete_ann(_, []) -> - []. - %%% =========================================================================== %%% %%% The analysis. %%% %%% =========================================================================== -analyze_module(Tree, Plt, Callgraph) -> - analyze_module(Tree, Plt, Callgraph, dict:new(), false). - analyze_module(Tree, Plt, Callgraph, Records, GetWarnings) -> debug_pp(Tree, false), Module = cerl:atom_val(cerl:module_name(Tree)), @@ -3204,21 +3076,6 @@ state__fun_info(Fun, #state{callgraph = CG, fun_tab = FunTab, plt = PLT}) -> ?debug("LocalRet: ~s\n", [t_to_string(LocalRet)]), {Fun, Sig, Contract, LocalRet}. -state__find_apply_return(Tree, #state{callgraph = Callgraph} = State) -> - Apply = get_label(Tree), - case dialyzer_callgraph:lookup_call_site(Apply, Callgraph) of - error -> - unknown; - {ok, List} -> - case lists:member(external, List) of - true -> t_any(); - false -> - FunTypes = [state__fun_type(F, State) || F <- List], - Returns = [t_fun_range(F) || F <- FunTypes], - t_sup(Returns) - end - end. - forward_args(Fun, ArgTypes, #state{work = Work, fun_tab = FunTab} = State) -> {OldArgTypes, OldOut, Fixpoint} = case dict:find(Fun, FunTab) of @@ -3652,17 +3509,3 @@ strip_annotations(Tree) -> debug_pp(_Tree, _UseHook) -> ok. -endif. - -%%---------------------------------------------------------------------------- - --spec to_dot(dialyzer_callgraph:callgraph()) -> 'ok'. - --ifdef(DOT). -to_dot(CG) -> - dialyzer_callgraph:to_dot(CG). --else. -to_dot(_CG) -> - ok. --endif. - -%%---------------------------------------------------------------------------- diff --git a/lib/dialyzer/src/dialyzer_succ_typings.erl b/lib/dialyzer/src/dialyzer_succ_typings.erl index 90d851bc3c..38f3d47353 100644 --- a/lib/dialyzer/src/dialyzer_succ_typings.erl +++ b/lib/dialyzer/src/dialyzer_succ_typings.erl @@ -29,7 +29,10 @@ -export([analyze_callgraph/3, analyze_callgraph/4, - get_warnings/6, + get_warnings/6 + ]). + +-export([ find_succ_types_for_scc/1, refine_one_module/1, collect_scc_data/2, @@ -38,12 +41,9 @@ find_depends_on/2 ]). -%% These are only intended as debug functions. --export([doit/1, - get_top_level_signatures/3]). +-export_type([servers/0, scc_data/0, scc_refine_data/0]). %%-define(DEBUG, true). -%%-define(DEBUG_PP, true). -ifdef(DEBUG). -define(debug(X__, Y__), io:format(X__, Y__)). @@ -213,9 +213,9 @@ refine_succ_typings([], State, Coordinator) -> false -> {not_fixpoint, NotFixpoint, State} end. --type servers() :: term(). --type scc_data() :: term(). --type scc_refine_data() :: term(). +-type servers() :: term(). %%opaque +-type scc_data() :: term(). %%opaque +-type scc_refine_data() :: term(). %%opaque -type scc() :: [mfa_or_funlbl()] | [module()]. -spec find_depends_on(scc(), servers()) -> [scc()]. @@ -237,18 +237,17 @@ collect_refine_scc_data(M, {CodeServer, Callgraph, PLT}) -> FunTypes = get_fun_types_from_plt(AllFuns, Callgraph, PLT), {ModCode, PLT, Callgraph, Records, FunTypes}. --spec refine_one_module(scc_refine_data()) -> - {dialyzer_callgraph:callgraph(), [label()]}. % ordset +-spec refine_one_module(scc_refine_data()) -> [label()]. % ordset -refine_one_module({ModCode, PLT, Callgraph, Records, FunTypes}) -> +refine_one_module({ModCode, Plt, Callgraph, Records, FunTypes}) -> NewFunTypes = - dialyzer_dataflow:get_fun_types(ModCode, PLT, Callgraph, Records), + dialyzer_dataflow:get_fun_types(ModCode, Plt, Callgraph, Records), case reached_fixpoint(FunTypes, NewFunTypes) of true -> ordsets:new(); {false, NotFixpoint} -> ?debug("Not fixpoint\n", []), - insert_into_plt(dict:from_list(NotFixpoint), Callgraph, PLT), + Plt = insert_into_plt(dict:from_list(NotFixpoint), Callgraph, Plt), ordsets:from_list([FunLbl || {FunLbl,_Type} <- NotFixpoint]) end. @@ -366,8 +365,8 @@ find_succ_types_for_scc({SCC_Info, Contracts, NextLabel, AllFuns, {value, _} -> true end end, PltContracts), - insert_into_plt(FilteredFunTypes, Callgraph, Plt), - dialyzer_plt:insert_contract_list(Plt, PltContracts), + Plt = insert_into_plt(FilteredFunTypes, Callgraph, Plt), + Plt = dialyzer_plt:insert_contract_list(Plt, PltContracts), case (ContractFixpoint andalso reached_fixpoint_strict(PropTypes, FilteredFunTypes)) of true -> []; @@ -460,122 +459,3 @@ send_log(Parent, Msg) -> format_scc(SCC) -> [MFA || {_M, _F, _A} = MFA <- SCC]. - -%% ============================================================================ -%% -%% Debug interface. -%% -%% ============================================================================ - --spec doit(atom() | file:filename()) -> 'ok'. - -doit(Module) -> - {ok, AbstrCode} = dialyzer_utils:get_abstract_code_from_src(Module), - {ok, Code} = dialyzer_utils:get_core_from_abstract_code(AbstrCode), - {ok, Records} = dialyzer_utils:get_record_and_type_info(AbstrCode), - %% contract typing info in dictionary format - {ok, Contracts, _Callbacks} = - dialyzer_utils:get_spec_info(cerl:concrete(cerl:module_name(Code)), - AbstrCode, Records), - Sigs0 = get_top_level_signatures(Code, Records, Contracts), - M = if is_atom(Module) -> - list_to_atom(filename:basename(atom_to_list(Module))); - is_list(Module) -> - list_to_atom(filename:basename(Module)) - end, - Sigs1 = [{{M, F, A}, Type} || {{F, A}, Type} <- Sigs0], - Sigs = ordsets:from_list(Sigs1), - io:format("==================== Final result ====================\n\n", []), - pp_signatures(Sigs, Records), - ok. - --spec get_top_level_signatures(cerl:c_module(), dict(), dict()) -> - [{{atom(), arity()}, erl_types:erl_type()}]. - -get_top_level_signatures(Code, Records, Contracts) -> - Tree = cerl:from_records(Code), - {LabeledTree, NextLabel} = cerl_trees:label(Tree), - Plt = get_def_plt(), - ModuleName = cerl:atom_val(cerl:module_name(LabeledTree)), - Plt1 = dialyzer_plt:delete_module(Plt, ModuleName), - Plt2 = analyze_module(LabeledTree, NextLabel, Plt1, Records, Contracts), - M = cerl:concrete(cerl:module_name(Tree)), - Functions = [{M, cerl:fname_id(V), cerl:fname_arity(V)} - || {V, _F} <- cerl:module_defs(LabeledTree)], - %% First contracts check - AllContracts = dict:fetch_keys(Contracts), - ErrorContracts = AllContracts -- Functions, - lists:foreach(fun(C) -> - io:format("Contract for non-existing function: ~w\n",[C]) - end, ErrorContracts), - Types = [{MFA, dialyzer_plt:lookup(Plt2, MFA)} || MFA <- Functions], - Sigs = [{{F, A}, erl_types:t_fun(ArgT, RetT)} - || {{_M, F, A}, {value, {RetT, ArgT}}} <- Types], - ordsets:from_list(Sigs). - -get_def_plt() -> - try - dialyzer_plt:from_file(dialyzer_plt:get_default_plt()) - catch - error:no_such_file -> dialyzer_plt:new(); - throw:{dialyzer_error, _} -> dialyzer_plt:new() - end. - -pp_signatures([{{_, module_info, 0}, _}|Left], Records) -> - pp_signatures(Left, Records); -pp_signatures([{{_, module_info, 1}, _}|Left], Records) -> - pp_signatures(Left, Records); -pp_signatures([{{M, F, _A}, Type}|Left], Records) -> - TypeString = - case cerl:is_literal(Type) of -%% Commented out so that dialyzer does not complain -%% false -> -%% "fun(" ++ String = erl_types:t_to_string(Type, Records), -%% string:substr(String, 1, length(String)-1); - true -> - io_lib:format("~w", [cerl:concrete(Type)]) - end, - io:format("~w:~w~s\n", [M, F, TypeString]), - pp_signatures(Left, Records); -pp_signatures([], _Records) -> - ok. - --ifdef(DEBUG_PP). -debug_pp(Tree, _Map) -> - Tree1 = strip_annotations(Tree), - io:put_chars(cerl_prettypr:format(Tree1)), - io:nl(). - -strip_annotations(Tree) -> - cerl_trees:map(fun(T) -> - case cerl:is_literal(T) orelse cerl:is_c_values(T) of - true -> cerl:set_ann(T, []); - false -> - Label = cerl_trees:get_label(T), - cerl:set_ann(T, [{'label', Label}]) - end - end, Tree). --else. -debug_pp(_Tree, _Map) -> - ok. --endif. % DEBUG_PP - -%% -%% Analysis of a single module -%% -analyze_module(LabeledTree, NextLbl, Plt, Records, Contracts) -> - debug_pp(LabeledTree, dict:new()), - CallGraph1 = dialyzer_callgraph:new(), - CallGraph2 = dialyzer_callgraph:scan_core_tree(LabeledTree, CallGraph1), - {CallGraph3, _Ext} = dialyzer_callgraph:remove_external(CallGraph2), - CallGraph4 = dialyzer_callgraph:finalize(CallGraph3), - CodeServer1 = dialyzer_codeserver:new(), - Mod = cerl:concrete(cerl:module_name(LabeledTree)), - CodeServer2 = dialyzer_codeserver:insert(Mod, LabeledTree, CodeServer1), - CodeServer3 = dialyzer_codeserver:set_next_core_label(NextLbl, CodeServer2), - CodeServer4 = dialyzer_codeserver:store_records(Mod, Records, CodeServer3), - CodeServer5 = dialyzer_codeserver:store_contracts(Mod, Contracts, CodeServer4), - Res = analyze_callgraph(CallGraph4, Plt, CodeServer5), - dialyzer_callgraph:delete(CallGraph4), - dialyzer_codeserver:delete(CodeServer5), - Res. diff --git a/lib/dialyzer/src/dialyzer_worker.erl b/lib/dialyzer/src/dialyzer_worker.erl index b24103c5a8..48dae7cb56 100644 --- a/lib/dialyzer/src/dialyzer_worker.erl +++ b/lib/dialyzer/src/dialyzer_worker.erl @@ -20,18 +20,26 @@ -module(dialyzer_worker). --export([launch/3]). +-export([launch/4]). --type worker() :: pid(). +-export_type([worker/0]). + +-type worker() :: pid(). %%opaque + +-type mode() :: dialyzer_coordinator:mode(). +-type coordinator() :: dialyzer_coordinator:coordinator(). +-type servers() :: dialyzer_succ_typings:servers() | + dialyzer_analysis_callgraph:servers(). +-type data() :: dialyzer_succ_typings:scc_data() | + dialyzer_succ_typings:scc_refine_data(). -record(state, { - mode :: dialyzer_coordinator:mode(), + mode :: mode(), job :: mfa_or_funlbl() | file:filename(), + coordinator :: coordinator(), + servers :: servers(), depends_on = [] :: list(), - coordinator :: dialyzer_coordinator:coordinator(), - servers :: dialyzer_typesig:servers() | - dialyzer_analysis_callgraph:servers(), - scc_data :: dialyzer_typesig:scc_data() + scc_data :: data() }). -include("dialyzer.hrl"). @@ -46,14 +54,13 @@ %%-------------------------------------------------------------------- --spec launch(dialyzer_coordinator:mode(), [mfa_or_funlbl()], - dialyzer_typesig:servers()) -> worker(). +-spec launch(mode(), [mfa_or_funlbl()], servers(), coordinator()) -> worker(). -launch(Mode, Job, Servers) -> +launch(Mode, Job, Servers, Coordinator) -> State = #state{mode = Mode, job = Job, servers = Servers, - coordinator = self()}, + coordinator = Coordinator}, InitState = case Mode of X when X =:= 'typesig'; X =:= 'dataflow' -> initializing; @@ -93,9 +100,14 @@ loop(getting_data, State) -> loop(updating, get_data(State)); loop(running, #state{mode = 'compile'} = State) -> ?debug("Compile: ~s\n",[State#state.job]), - {EstimatedSize, Data} = start_compilation(State), - Label = ask_coordinator_for_label(EstimatedSize, State), - Result = continue_compilation(Label, Data), + Result = + case start_compilation(State) of + {ok, EstimatedSize, Data} -> + Label = ask_coordinator_for_label(EstimatedSize, State), + continue_compilation(Label, Data); + {error, _Reason} = Error -> + Error + end, report_to_coordinator(Result, State); loop(running, #state{mode = Mode} = State) when Mode =:= 'typesig'; Mode =:= 'dataflow' -> @@ -160,10 +172,9 @@ do_work(#state{mode = Mode, scc_data = SCCData}) -> dataflow -> dialyzer_succ_typings:refine_one_module(SCCData) end. -report_to_coordinator(NotFixpoint, - #state{job = Job, coordinator = Coordinator}) -> +report_to_coordinator(Result, #state{job = Job, coordinator = Coordinator}) -> ?debug("Done: ~p\n",[Job]), - dialyzer_coordinator:scc_done(Job, NotFixpoint, Coordinator). + dialyzer_coordinator:job_done(Job, Result, Coordinator). start_compilation(#state{job = Job, servers = Servers}) -> dialyzer_analysis_callgraph:start_compilation(Job, Servers). -- cgit v1.2.3 From 6b841074f3e8989ca8349d8298f6237c150bc2c8 Mon Sep 17 00:00:00 2001 From: Stavros Aronis Date: Mon, 20 Feb 2012 00:58:51 +0100 Subject: Keep mini plt for warnings pass --- lib/dialyzer/src/dialyzer_behaviours.erl | 5 ++-- lib/dialyzer/src/dialyzer_plt.erl | 48 +++++++++++++----------------- lib/dialyzer/src/dialyzer_succ_typings.erl | 41 ++++++++++++++----------- 3 files changed, 47 insertions(+), 47 deletions(-) (limited to 'lib/dialyzer') diff --git a/lib/dialyzer/src/dialyzer_behaviours.erl b/lib/dialyzer/src/dialyzer_behaviours.erl index 127e906135..f86cc3d3d6 100644 --- a/lib/dialyzer/src/dialyzer_behaviours.erl +++ b/lib/dialyzer/src/dialyzer_behaviours.erl @@ -92,8 +92,9 @@ get_warnings(Module, [Behaviour|Rest], State, Acc) -> check_behaviour(Module, Behaviour, #state{plt = Plt} = State, Acc) -> case dialyzer_plt:lookup_callbacks(Plt, Behaviour) of - [] -> [{callback_info_missing, [Behaviour]}|Acc]; - Callbacks -> check_all_callbacks(Module, Behaviour, Callbacks, State, Acc) + none -> [{callback_info_missing, [Behaviour]}|Acc]; + {value, Callbacks} -> + check_all_callbacks(Module, Behaviour, Callbacks, State, Acc) end. check_all_callbacks(_Module, _Behaviour, [], _State, Acc) -> diff --git a/lib/dialyzer/src/dialyzer_plt.erl b/lib/dialyzer/src/dialyzer_plt.erl index 0c0773e977..35bee913c2 100644 --- a/lib/dialyzer/src/dialyzer_plt.erl +++ b/lib/dialyzer/src/dialyzer_plt.erl @@ -87,7 +87,8 @@ exported_types = sets:new() :: set()}). -record(mini_plt, {info :: ets:tid(), - contracts :: ets:tid() + contracts :: ets:tid(), + callbacks :: ets:tid() }). -opaque plt() :: #plt{} | #mini_plt{}. @@ -140,8 +141,6 @@ delete_list(#plt{info = Info, types = Types, -spec insert_contract_list(plt(), dialyzer_contracts:plt_contracts()) -> plt(). -insert_contract_list(#plt{contracts = Contracts} = PLT, List) -> - PLT#plt{contracts = table_insert_list(Contracts, List)}; insert_contract_list(#mini_plt{contracts = Contracts} = PLT, List) -> true = ets:insert(Contracts, List), PLT. @@ -154,9 +153,6 @@ insert_callbacks(#plt{callbacks = Callbacks} = Plt, Codeserver) -> -spec lookup_contract(plt(), mfa_patt()) -> 'none' | {'value', #contract{}}. -lookup_contract(#plt{contracts = Contracts}, - {M, F, _} = MFA) when is_atom(M), is_atom(F) -> - table_lookup(Contracts, MFA); lookup_contract(#mini_plt{contracts = ETSContracts}, {M, F, _} = MFA) when is_atom(M), is_atom(F) -> ets_table_lookup(ETSContracts, MFA). @@ -164,15 +160,13 @@ lookup_contract(#mini_plt{contracts = ETSContracts}, -spec lookup_callbacks(plt(), module()) -> [{mfa(), {{Filename::string(), Line::pos_integer()}, #contract{}}}]. -lookup_callbacks(#plt{callbacks = Callbacks}, Mod) when is_atom(Mod) -> - table_filter_module(Callbacks, Mod). +lookup_callbacks(#mini_plt{callbacks = ETSCallbacks}, Mod) when is_atom(Mod) -> + ets_table_lookup(ETSCallbacks, Mod). -type ret_args_types() :: {erl_types:erl_type(), [erl_types:erl_type()]}. -spec insert_list(plt(), [{mfa() | integer(), ret_args_types()}]) -> plt(). -insert_list(#plt{info = Info} = PLT, List) -> - PLT#plt{info = table_insert_list(Info, List)}; insert_list(#mini_plt{info = Info} = PLT, List) -> true = ets:insert(Info, List), PLT. @@ -185,8 +179,6 @@ lookup(Plt, {M, F, _} = MFA) when is_atom(M), is_atom(F) -> lookup(Plt, Label) when is_integer(Label) -> lookup_1(Plt, Label). -lookup_1(#plt{info = Info}, MFAorLabel) -> - table_lookup(Info, MFAorLabel); lookup_1(#mini_plt{info = Info}, MFAorLabel) -> ets_table_lookup(Info, MFAorLabel). @@ -517,12 +509,20 @@ init_md5_list_1(Md5List, [], Acc) -> -spec get_mini_plt(plt()) -> plt(). -get_mini_plt(#plt{info = Info, contracts = Contracts}) -> - ETSInfo = ets:new(plt_info, [public]), - ETSContracts = ets:new(plt_contracts, [public]), - true = ets:insert(ETSInfo, dict:to_list(Info)), - true = ets:insert(ETSContracts, dict:to_list(Contracts)), - #mini_plt{info = ETSInfo, contracts = ETSContracts}. +get_mini_plt(#plt{info = Info, contracts = Contracts, callbacks = Callbacks}) -> + [ETSInfo, ETSContracts, ETSCallbacks] = + [ets:new(Name, [public]) || Name <- [plt_info, plt_contracts, plt_callbacks]], + CallbackList = dict:to_list(Callbacks), + CallbacksByModule = + [{M, [Cb || {{M1,_,_},_} = Cb <- CallbackList, M1 =:= M]} || + M <- lists:usort([M || {{M,_,_},_} <- CallbackList])], + [true, true] = + [ets:insert(ETS, dict:to_list(Data)) || + {ETS, Data} <- [{ETSInfo, Info}, {ETSContracts, Contracts}]], + true = ets:insert(ETSCallbacks, CallbacksByModule), + #mini_plt{info = ETSInfo, contracts = ETSContracts, callbacks = ETSCallbacks}; +get_mini_plt(undefined) -> + undefined. -spec restore_full_plt(plt(), plt()) -> plt(). @@ -531,7 +531,9 @@ restore_full_plt(#mini_plt{info = ETSInfo, contracts = ETSContracts}, Plt) -> Contracts = dict:from_list(ets:tab2list(ETSContracts)), ets:delete(ETSContracts), ets:delete(ETSInfo), - Plt#plt{info = Info, contracts = Contracts}. + Plt#plt{info = Info, contracts = Contracts}; +restore_full_plt(undefined, undefined) -> + undefined. %%--------------------------------------------------------------------------- %% Edoc @@ -645,14 +647,6 @@ table_lookup_module(Plt, Mod) -> false -> {value, List} end. -table_filter_module(Plt, Mod) -> - FunModFilter = - fun({M, _F, _A}, _Val) -> M =:= Mod; - ( _Key, _Val) -> false - end, - ModCallbacks = dict:filter(FunModFilter, Plt), - dict:to_list(ModCallbacks). - table_all_modules(Plt) -> Fold = fun({M, _F, _A}, _Val, Acc) -> sets:add_element(M, Acc); diff --git a/lib/dialyzer/src/dialyzer_succ_typings.erl b/lib/dialyzer/src/dialyzer_succ_typings.erl index 38f3d47353..4d813e06bb 100644 --- a/lib/dialyzer/src/dialyzer_succ_typings.erl +++ b/lib/dialyzer/src/dialyzer_succ_typings.erl @@ -126,13 +126,16 @@ get_refined_success_typings(SCCs, State) -> get_warnings(Callgraph, Plt, DocPlt, Codeserver, NoWarnUnused, Parent) -> InitState = init_state_and_get_success_typings(Callgraph, Plt, Codeserver, Parent), - NewState = - InitState#st{no_warn_unused = NoWarnUnused, - plt = dialyzer_plt:restore_full_plt(InitState#st.plt, Plt)}, + NewState = InitState#st{no_warn_unused = NoWarnUnused}, Mods = dialyzer_callgraph:modules(NewState#st.callgraph), CWarns = dialyzer_contracts:get_invalid_contract_warnings(Mods, Codeserver, NewState#st.plt), - get_warnings_from_modules(Mods, NewState, DocPlt, CWarns). + MiniDocPlt = dialyzer_plt:get_mini_plt(DocPlt), + {Warnings, FinalPlt, FinalDocPlt} = + get_warnings_from_modules(Mods, NewState, MiniDocPlt, CWarns), + {postprocess_warnings(Warnings, Codeserver), + dialyzer_plt:restore_full_plt(FinalPlt, Plt), + dialyzer_plt:restore_full_plt(FinalDocPlt, DocPlt)}. get_warnings_from_modules([M|Ms], State, DocPlt, Acc) when is_atom(M) -> send_log(State#st.parent, io_lib:format("Getting warnings for ~w\n", [M])), @@ -145,25 +148,29 @@ get_warnings_from_modules([M|Ms], State, DocPlt, Acc) when is_atom(M) -> %% Check if there are contracts for functions that do not exist Warnings1 = dialyzer_contracts:contracts_without_fun(Contracts, AllFuns, Callgraph), - {RawWarnings2, FunTypes} = + {Warnings2, FunTypes} = dialyzer_dataflow:get_warnings(ModCode, Plt, Callgraph, Records, NoWarnUnused), - {NewAcc, Warnings2} = postprocess_dataflow_warns(RawWarnings2, State, Acc), Attrs = cerl:module_attrs(ModCode), Warnings3 = dialyzer_behaviours:check_callbacks(M, Attrs, Plt, Codeserver), NewDocPlt = insert_into_doc_plt(FunTypes, Callgraph, DocPlt), get_warnings_from_modules(Ms, State, NewDocPlt, - [Warnings1, Warnings2, Warnings3|NewAcc]); + [Warnings1, Warnings2, Warnings3|Acc]); get_warnings_from_modules([], #st{plt = Plt}, DocPlt, Acc) -> {lists:flatten(Acc), Plt, DocPlt}. -postprocess_dataflow_warns(RawWarnings, State, WarnAcc) -> - postprocess_dataflow_warns(RawWarnings, State, WarnAcc, []). +postprocess_warnings(RawWarnings, Codeserver) -> + Pred = + fun({?WARN_CONTRACT_RANGE, _, _}) -> true; + (_) -> false + end, + {CRWarns, NonCRWarns} = lists:partition(Pred, RawWarnings), + postprocess_dataflow_warns(CRWarns, Codeserver, NonCRWarns, []). -postprocess_dataflow_warns([], _State, WAcc, Acc) -> - {WAcc, lists:reverse(Acc)}; +postprocess_dataflow_warns([], _Callgraph, WAcc, Acc) -> + lists:reverse(Acc, WAcc); postprocess_dataflow_warns([{?WARN_CONTRACT_RANGE, {CallF, CallL}, Msg}|Rest], - #st{codeserver = Codeserver} = State, WAcc, Acc) -> + Codeserver, WAcc, Acc) -> {contract_range, [Contract, M, F, A, ArgStrings, CRet]} = Msg, case dialyzer_codeserver:lookup_mfa_contract({M,F,A}, Codeserver) of {ok, {{ContrF, _ContrL} = FileLine, _C}} -> @@ -176,19 +183,17 @@ postprocess_dataflow_warns([{?WARN_CONTRACT_RANGE, {CallF, CallL}, Msg}|Rest], (_) -> true end, FilterWAcc = lists:filter(Filter, WAcc), - postprocess_dataflow_warns(Rest, State, FilterWAcc, [W|Acc]); + postprocess_dataflow_warns(Rest, Codeserver, FilterWAcc, [W|Acc]); false -> - postprocess_dataflow_warns(Rest, State, WAcc, Acc) + postprocess_dataflow_warns(Rest, Codeserver, WAcc, Acc) end; error -> %% The contract is not in a module that is currently under analysis. %% We display the warning in the file/line of the call. NewMsg = {contract_range, [Contract, M, F, ArgStrings, CallL, CRet]}, W = {?WARN_CONTRACT_RANGE, {CallF, CallL}, NewMsg}, - postprocess_dataflow_warns(Rest, State, WAcc, [W|Acc]) - end; -postprocess_dataflow_warns([W|Rest], State, Wacc, Acc) -> - postprocess_dataflow_warns(Rest, State, Wacc, [W|Acc]). + postprocess_dataflow_warns(Rest, Codeserver, WAcc, [W|Acc]) + end. refine_succ_typings(ModulePostorder, #st{codeserver = Codeserver, callgraph = Callgraph, -- cgit v1.2.3 From c8f8c90f908ae06a3b2114b77447699b7d690496 Mon Sep 17 00:00:00 2001 From: Stavros Aronis Date: Mon, 20 Feb 2012 15:51:54 +0100 Subject: Fix a spec in plt --- lib/dialyzer/src/dialyzer_plt.erl | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'lib/dialyzer') diff --git a/lib/dialyzer/src/dialyzer_plt.erl b/lib/dialyzer/src/dialyzer_plt.erl index 35bee913c2..5f64099210 100644 --- a/lib/dialyzer/src/dialyzer_plt.erl +++ b/lib/dialyzer/src/dialyzer_plt.erl @@ -158,7 +158,9 @@ lookup_contract(#mini_plt{contracts = ETSContracts}, ets_table_lookup(ETSContracts, MFA). -spec lookup_callbacks(plt(), module()) -> - [{mfa(), {{Filename::string(), Line::pos_integer()}, #contract{}}}]. + 'none' | {'value', [{mfa(), {{Filename::string(), + Line::pos_integer()}, + #contract{}}}]}. lookup_callbacks(#mini_plt{callbacks = ETSCallbacks}, Mod) when is_atom(Mod) -> ets_table_lookup(ETSCallbacks, Mod). -- cgit v1.2.3 From 60e682897f98d9374b96c6324759f302170b2a17 Mon Sep 17 00:00:00 2001 From: Stavros Aronis Date: Mon, 20 Feb 2012 13:56:25 +0100 Subject: Add --time option to Dialyzer --- lib/dialyzer/src/Makefile | 1 + lib/dialyzer/src/dialyzer.hrl | 9 +++ lib/dialyzer/src/dialyzer_analysis_callgraph.erl | 3 +- lib/dialyzer/src/dialyzer_cl.erl | 2 + lib/dialyzer/src/dialyzer_cl_parse.erl | 9 ++- lib/dialyzer/src/dialyzer_options.erl | 2 + lib/dialyzer/src/dialyzer_succ_typings.erl | 7 +- lib/dialyzer/src/dialyzer_timing.erl | 88 ++++++++++++++++++++++++ 8 files changed, 116 insertions(+), 5 deletions(-) create mode 100644 lib/dialyzer/src/dialyzer_timing.erl (limited to 'lib/dialyzer') diff --git a/lib/dialyzer/src/Makefile b/lib/dialyzer/src/Makefile index 39b1b3eb3e..09fb02bda2 100644 --- a/lib/dialyzer/src/Makefile +++ b/lib/dialyzer/src/Makefile @@ -63,6 +63,7 @@ MODULES = \ dialyzer_plt \ dialyzer_races \ dialyzer_succ_typings \ + dialyzer_timing \ dialyzer_typesig \ dialyzer_coordinator \ dialyzer_worker \ diff --git a/lib/dialyzer/src/dialyzer.hrl b/lib/dialyzer/src/dialyzer.hrl index 44b1ebeabd..e2edd3f0af 100644 --- a/lib/dialyzer/src/dialyzer.hrl +++ b/lib/dialyzer/src/dialyzer.hrl @@ -132,6 +132,7 @@ -record(options, {files = [] :: [file:filename()], files_rec = [] :: [file:filename()], analysis_type = succ_typings :: anal_type1(), + timing = false :: boolean(), defines = [] :: [dial_define()], from = byte_code :: start_from(), get_warnings = maybe :: boolean() | 'maybe', @@ -153,3 +154,11 @@ forms = [] :: [{_, _}]}). %%-------------------------------------------------------------------- + +-define(timing(Msg,Expr), + begin + dialyzer_timing:start_stamp(Msg), + __T = Expr, + dialyzer_timing:end_stamp(), + __T + end). diff --git a/lib/dialyzer/src/dialyzer_analysis_callgraph.erl b/lib/dialyzer/src/dialyzer_analysis_callgraph.erl index febaf56e40..30eb087fcf 100644 --- a/lib/dialyzer/src/dialyzer_analysis_callgraph.erl +++ b/lib/dialyzer/src/dialyzer_analysis_callgraph.erl @@ -134,7 +134,8 @@ analysis_start(Parent, Analysis) -> use_contracts = Analysis#analysis.use_contracts }, Files = ordsets:from_list(Analysis#analysis.files), - {Callgraph, NoWarn, TmpCServer0} = compile_and_store(Files, State), + {Callgraph, NoWarn, TmpCServer0} = + ?timing("compile",compile_and_store(Files, State)), %% Remote type postprocessing NewCServer = try diff --git a/lib/dialyzer/src/dialyzer_cl.erl b/lib/dialyzer/src/dialyzer_cl.erl index 6d3b1f0531..482f428e02 100644 --- a/lib/dialyzer/src/dialyzer_cl.erl +++ b/lib/dialyzer/src/dialyzer_cl.erl @@ -400,8 +400,10 @@ do_analysis(Files, Options, Plt, PltInfo) -> callgraph_file = Options#options.callgraph_file}, State3 = start_analysis(State2, InitAnalysis), {T1, _} = statistics(wall_clock), + ok = dialyzer_timing:init(Options#options.timing), Return = cl_loop(State3), {T2, _} = statistics(wall_clock), + ok = dialyzer_timing:stop(), report_elapsed_time(T1, T2, Options), Return. diff --git a/lib/dialyzer/src/dialyzer_cl_parse.erl b/lib/dialyzer/src/dialyzer_cl_parse.erl index ff8fc39a5e..5baba7d11b 100644 --- a/lib/dialyzer/src/dialyzer_cl_parse.erl +++ b/lib/dialyzer/src/dialyzer_cl_parse.erl @@ -164,6 +164,9 @@ cl(["--src"|T]) -> cl(["--no_spec"|T]) -> put(dialyzer_options_use_contracts, false), cl(T); +cl(["--time"|T]) -> + put(dialyzer_timing, true), + cl(T); cl(["-v"|_]) -> io:format("Dialyzer version "++?VSN++"\n"), erlang:halt(?RET_NOTHING_SUSPICIOUS); @@ -250,6 +253,7 @@ init() -> put(dialyzer_output_format, formatted), put(dialyzer_filename_opt, basename), put(dialyzer_options_check_plt, DefaultOpts#options.check_plt), + put(dialyzer_timing, DefaultOpts#options.timing), ok. append_defines([Def, Val]) -> @@ -290,6 +294,7 @@ cl_options() -> {filename_opt, get(dialyzer_filename_opt)}, {analysis_type, get(dialyzer_options_analysis_type)}, {get_warnings, get(dialyzer_options_get_warnings)}, + {timing, get(dialyzer_timing)}, {callgraph_file, get(dialyzer_callgraph_file)} |common_options()]. @@ -351,7 +356,7 @@ help_message() -> [--apps applications] [-o outfile] [--build_plt] [--add_to_plt] [--remove_from_plt] [--check_plt] [--no_check_plt] [--plt_info] [--get_warnings] - [--no_native] [--fullpath] + [--no_native] [--fullpath] [--time] Options: files_or_dirs (for backwards compatibility also as: -c files_or_dirs) Use Dialyzer from the command line to detect defects in the @@ -418,6 +423,8 @@ Options: Make Dialyzer a bit more quiet. --verbose Make Dialyzer a bit more verbose. + --time + Print time information --build_plt The analysis starts from an empty plt and creates a new one from the files specified with -c and -r. Only works for beam files. diff --git a/lib/dialyzer/src/dialyzer_options.erl b/lib/dialyzer/src/dialyzer_options.erl index 866650a0b2..a1e316d6cc 100644 --- a/lib/dialyzer/src/dialyzer_options.erl +++ b/lib/dialyzer/src/dialyzer_options.erl @@ -194,6 +194,8 @@ build_options([{OptionName, Value} = Term|Rest], Options) -> callgraph_file -> assert_filename(Value), build_options(Rest, Options#options{callgraph_file = Value}); + timing -> + build_options(Rest, Options#options{timing = Value}); _ -> bad_option("Unknown dialyzer command line option", Term) end; diff --git a/lib/dialyzer/src/dialyzer_succ_typings.erl b/lib/dialyzer/src/dialyzer_succ_typings.erl index 4d813e06bb..e901677ce2 100644 --- a/lib/dialyzer/src/dialyzer_succ_typings.erl +++ b/lib/dialyzer/src/dialyzer_succ_typings.erl @@ -132,7 +132,8 @@ get_warnings(Callgraph, Plt, DocPlt, Codeserver, NoWarnUnused, Parent) -> NewState#st.plt), MiniDocPlt = dialyzer_plt:get_mini_plt(DocPlt), {Warnings, FinalPlt, FinalDocPlt} = - get_warnings_from_modules(Mods, NewState, MiniDocPlt, CWarns), + ?timing("warning", + get_warnings_from_modules(Mods, NewState, MiniDocPlt, CWarns)), {postprocess_warnings(Warnings, Codeserver), dialyzer_plt:restore_full_plt(FinalPlt, Plt), dialyzer_plt:restore_full_plt(FinalDocPlt, DocPlt)}. @@ -201,7 +202,7 @@ refine_succ_typings(ModulePostorder, #st{codeserver = Codeserver, ?debug("Module postorder: ~p\n", [ModulePostorder]), Servers = {Codeserver, Callgraph, Plt}, Coordinator = dialyzer_coordinator:start(dataflow, Servers), - refine_succ_typings(ModulePostorder, State, Coordinator). + ?timing("refine",refine_succ_typings(ModulePostorder, State, Coordinator)). refine_succ_typings([M|Rest], State, Coordinator) -> Msg = io_lib:format("Dataflow of module: ~w\n", [M]), @@ -315,7 +316,7 @@ find_succ_typings(SCCs, #st{codeserver = Codeserver, callgraph = Callgraph, plt = Plt} = State) -> Servers = {Codeserver, dialyzer_callgraph:mini_callgraph(Callgraph), Plt}, Coordinator = dialyzer_coordinator:start(typesig, Servers), - find_succ_typings(SCCs, State, Coordinator). + ?timing("typesig", find_succ_typings(SCCs, State, Coordinator)). find_succ_typings([SCC|Rest], #st{parent = Parent} = State, Coordinator) -> Msg = io_lib:format("Typesig analysis for SCC: ~w\n", [format_scc(SCC)]), diff --git a/lib/dialyzer/src/dialyzer_timing.erl b/lib/dialyzer/src/dialyzer_timing.erl new file mode 100644 index 0000000000..73dbbf5a34 --- /dev/null +++ b/lib/dialyzer/src/dialyzer_timing.erl @@ -0,0 +1,88 @@ +%% -*- erlang-indent-level: 2 -*- +%%------------------------------------------------------------------- +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2006-2012. All Rights Reserved. +%% +%% The contents of this file are subject to the Erlang Public License, +%% Version 1.1, (the "License"); you may not use this file except in +%% compliance with the License. You should have received a copy of the +%% Erlang Public License along with this software. If not, it can be +%% retrieved online at http://www.erlang.org/. +%% +%% Software distributed under the License is distributed on an "AS IS" +%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +%% the License for the specific language governing rights and limitations +%% under the License. +%% +%% %CopyrightEnd% +%% + +%%%------------------------------------------------------------------- +%%% File : dialyzer_timing.erl +%%% Authors : Stavros Aronis +%%% Description : Timing reports for Dialyzer +%%%------------------------------------------------------------------- + +-module(dialyzer_timing). + +-export([init/1, start_stamp/1, end_stamp/0, stop/0]). + +-spec init(boolean()) -> ok. + +init(Active) -> + Pid = spawn(fun() -> loop_init(Active) end), + case whereis(?MODULE) of + undefined -> ok; + _ -> unregister(?MODULE) + end, + register(?MODULE, Pid), + ok. + +loop_init(Active) -> + case Active of + true -> + io:format("\n"), + loop(now()); + false -> dummy_loop() + end. + +dummy_loop() -> + receive + {Pid, stop, _Now} -> Pid ! ok; + _ -> dummy_loop() + end. + +loop(LastNow) -> + receive + {stamp, Msg, Now} -> + io:format("~s\t(+~6.2fs): ", [Msg, diff(Now, LastNow)]), + loop(Now); + {stamp, Now} -> + io:format("~6.2fs\n", [diff(Now, LastNow)]), + loop(Now); + {Pid, stop, Now} -> + io:format("\t(+~6.2fs)\n", [diff(Now, LastNow)]), + Pid ! ok + end. + +-spec start_stamp(string()) -> ok. + +start_stamp(Msg) -> + ?MODULE ! {stamp, Msg, now()}, + ok. + +-spec end_stamp() -> ok. + +end_stamp() -> + ?MODULE ! {stamp, now()}, + ok. + +-spec stop() -> ok. + +stop() -> + ?MODULE ! {self(), stop, now()}, + receive ok -> ok end. + +diff(T2, T1) -> + timer:now_diff(T2,T1) / 1000000. -- cgit v1.2.3 From 009710cec5e8b9786a5219dc3f682d6010352160 Mon Sep 17 00:00:00 2001 From: Stavros Aronis Date: Mon, 20 Feb 2012 15:54:43 +0100 Subject: Parallel warning generation --- lib/dialyzer/src/dialyzer_coordinator.erl | 38 +++++++++++++++++-------- lib/dialyzer/src/dialyzer_succ_typings.erl | 45 ++++++++++++++++++------------ lib/dialyzer/src/dialyzer_worker.erl | 9 +++++- 3 files changed, 61 insertions(+), 31 deletions(-) (limited to 'lib/dialyzer') diff --git a/lib/dialyzer/src/dialyzer_coordinator.erl b/lib/dialyzer/src/dialyzer_coordinator.erl index 9bcd7e4c63..93159ce7df 100644 --- a/lib/dialyzer/src/dialyzer_coordinator.erl +++ b/lib/dialyzer/src/dialyzer_coordinator.erl @@ -53,9 +53,14 @@ %%% Exports for all possible workers -export([job_done/3]). -%%% Exports for the typesig and dataflow analysis main process --export([scc_spawn/2, - receive_not_fixpoint/0]). +%%% Export for the typesig, dataflow and warnings main process +-export([scc_spawn/2]). + +%%% Export for the typesig and dataflow analysis main process +-export([receive_not_fixpoint/0]). + +%%% Export for warning main process +-export([receive_warnings/0]). %%% Exports for the typesig and dataflow analysis workers -export([sccs_to_pids_reply/0, @@ -79,10 +84,11 @@ -type coordinator() :: pid(). %%opaque -type map() :: dict(). --type scc() :: [mfa_or_funlbl()]. --type mode() :: 'typesig' | 'dataflow' | 'compile'. +-type scc() :: [mfa_or_funlbl()] | module(). +-type mode() :: 'typesig' | 'dataflow' | 'compile' | 'warnings'. -type servers() :: dialyzer_succ_typings:servers() | - dialyzer_analysis_callgraph:servers(). + dialyzer_analysis_callgraph:servers() | + dialyzer_succ_typings:warning_servers(). -record(state, {parent :: pid(), mode :: mode(), @@ -91,7 +97,8 @@ job_to_pid :: map(), next_label :: integer(), result :: [mfa_or_funlbl()] | - dialyzer_analysis_callgraph:result(), + dialyzer_analysis_callgraph:result() | + [dial_warning()], init_job_data :: servers() }). @@ -99,8 +106,7 @@ %%-------------------------------------------------------------------- --spec start('typesig' | 'dataflow', dialyzer_succ_typings:servers()) -> pid(); - ('compile', dialyzer_analysis_callgraph:servers()) -> pid(). +-spec start(mode(), servers()) -> coordinator(). start(Mode, Servers) -> {ok, Pid} = gen_server:start(?MODULE, {self(), Mode, Servers}, []), @@ -150,7 +156,8 @@ send_done_to_parent(#state{mode = Mode, Msg = case Mode of X when X =:= 'typesig'; X =:= 'dataflow' -> {not_fixpoint, Result}; - 'compile' -> {compilation_data, Result, NextLabel} + 'compile' -> {compilation_data, Result, NextLabel}; + 'warnings' -> {warnings, Result} end, Parent ! Msg, ok. @@ -168,6 +175,11 @@ receive_compilation_data() -> {CompilationData, NextLabel} end. +-spec receive_warnings() -> [dial_warning()]. + +receive_warnings() -> + receive {warnings, Warnings} -> Warnings end. + -spec compiler_spawn(file:filename(), coordinator()) -> ok. compiler_spawn(Filename, Coordinator) -> @@ -186,7 +198,7 @@ init({Parent, Mode, InitJobData}) -> InitState = #state{parent = Parent, mode = Mode, init_job_data = InitJobData}, State = case Mode of - X when X =:= 'typesig'; X =:= 'dataflow' -> + X when X =:= 'typesig'; X =:= 'dataflow'; X =:= 'warnings' -> InitState#state{result = [], job_to_pid = new_map()}; 'compile' -> InitResult = dialyzer_analysis_callgraph:compile_coordinator_init(), @@ -215,7 +227,9 @@ handle_cast({done, Job, NewData}, X when X =:= 'typesig'; X =:= 'dataflow' -> ordsets:union(OldResult, NewData); 'compile' -> - dialyzer_analysis_callgraph:add_to_result(Job, NewData, OldResult) + dialyzer_analysis_callgraph:add_to_result(Job, NewData, OldResult); + 'warnings' -> + NewData ++ OldResult end, UpdatedState = State#state{result = NewResult}, Action = diff --git a/lib/dialyzer/src/dialyzer_succ_typings.erl b/lib/dialyzer/src/dialyzer_succ_typings.erl index e901677ce2..fbd333f905 100644 --- a/lib/dialyzer/src/dialyzer_succ_typings.erl +++ b/lib/dialyzer/src/dialyzer_succ_typings.erl @@ -38,10 +38,11 @@ collect_scc_data/2, collect_refine_scc_data/2, find_required_by/2, - find_depends_on/2 + find_depends_on/2, + collect_warnings/2 ]). --export_type([servers/0, scc_data/0, scc_refine_data/0]). +-export_type([servers/0, scc_data/0, scc_refine_data/0, warning_servers/0]). %%-define(DEBUG, true). @@ -128,20 +129,31 @@ get_warnings(Callgraph, Plt, DocPlt, Codeserver, NoWarnUnused, Parent) -> init_state_and_get_success_typings(Callgraph, Plt, Codeserver, Parent), NewState = InitState#st{no_warn_unused = NoWarnUnused}, Mods = dialyzer_callgraph:modules(NewState#st.callgraph), - CWarns = dialyzer_contracts:get_invalid_contract_warnings(Mods, Codeserver, - NewState#st.plt), + MiniPlt = NewState#st.plt, + CWarns = + dialyzer_contracts:get_invalid_contract_warnings(Mods, Codeserver, MiniPlt), MiniDocPlt = dialyzer_plt:get_mini_plt(DocPlt), - {Warnings, FinalPlt, FinalDocPlt} = - ?timing("warning", - get_warnings_from_modules(Mods, NewState, MiniDocPlt, CWarns)), - {postprocess_warnings(Warnings, Codeserver), - dialyzer_plt:restore_full_plt(FinalPlt, Plt), - dialyzer_plt:restore_full_plt(FinalDocPlt, DocPlt)}. - -get_warnings_from_modules([M|Ms], State, DocPlt, Acc) when is_atom(M) -> - send_log(State#st.parent, io_lib:format("Getting warnings for ~w\n", [M])), + ModWarns = + ?timing("warning", get_warnings_from_modules(Mods, NewState, MiniDocPlt)), + {postprocess_warnings(CWarns ++ ModWarns, Codeserver), + dialyzer_plt:restore_full_plt(MiniPlt, Plt), + dialyzer_plt:restore_full_plt(MiniDocPlt, DocPlt)}. + +get_warnings_from_modules(Mods, State, DocPlt) -> #st{callgraph = Callgraph, codeserver = Codeserver, no_warn_unused = NoWarnUnused, plt = Plt} = State, + Servers = {Callgraph, Codeserver, NoWarnUnused, Plt, DocPlt}, + Coordinator = dialyzer_coordinator:start('warnings', Servers), + Spawner = fun(M) -> dialyzer_coordinator:scc_spawn(M, Coordinator) end, + lists:foreach(Spawner, Mods), + dialyzer_coordinator:all_spawned(Coordinator), + dialyzer_coordinator:receive_warnings(). + +-type warning_servers() :: term(). + +-spec collect_warnings(module(), warning_servers()) -> [dial_warning()]. + +collect_warnings(M, {Callgraph, Codeserver, NoWarnUnused, Plt, DocPlt}) -> ModCode = dialyzer_codeserver:lookup_mod_code(M, Codeserver), Records = dialyzer_codeserver:lookup_mod_records(M, Codeserver), Contracts = dialyzer_codeserver:lookup_mod_contracts(M, Codeserver), @@ -154,11 +166,8 @@ get_warnings_from_modules([M|Ms], State, DocPlt, Acc) when is_atom(M) -> Records, NoWarnUnused), Attrs = cerl:module_attrs(ModCode), Warnings3 = dialyzer_behaviours:check_callbacks(M, Attrs, Plt, Codeserver), - NewDocPlt = insert_into_doc_plt(FunTypes, Callgraph, DocPlt), - get_warnings_from_modules(Ms, State, NewDocPlt, - [Warnings1, Warnings2, Warnings3|Acc]); -get_warnings_from_modules([], #st{plt = Plt}, DocPlt, Acc) -> - {lists:flatten(Acc), Plt, DocPlt}. + DocPlt = insert_into_doc_plt(FunTypes, Callgraph, DocPlt), + lists:flatten([Warnings1, Warnings2, Warnings3]). postprocess_warnings(RawWarnings, Codeserver) -> Pred = diff --git a/lib/dialyzer/src/dialyzer_worker.erl b/lib/dialyzer/src/dialyzer_worker.erl index 48dae7cb56..6608b78f0a 100644 --- a/lib/dialyzer/src/dialyzer_worker.erl +++ b/lib/dialyzer/src/dialyzer_worker.erl @@ -64,7 +64,7 @@ launch(Mode, Job, Servers, Coordinator) -> InitState = case Mode of X when X =:= 'typesig'; X =:= 'dataflow' -> initializing; - 'compile' -> running + X when X =:= 'compile'; X =:= 'warnings' -> running end, spawn(fun() -> loop(InitState, State) end). @@ -109,6 +109,10 @@ loop(running, #state{mode = 'compile'} = State) -> Error end, report_to_coordinator(Result, State); +loop(running, #state{mode = 'warnings'} = State) -> + ?debug("Warning: ~s\n",[State#state.job]), + Result = collect_warnings(State), + report_to_coordinator(Result, State); loop(running, #state{mode = Mode} = State) when Mode =:= 'typesig'; Mode =:= 'dataflow' -> ?debug("Run: ~p\n",[State#state.job]), @@ -184,3 +188,6 @@ ask_coordinator_for_label(EstimatedSize, #state{coordinator = Coordinator}) -> continue_compilation(Label, Data) -> dialyzer_analysis_callgraph:continue_compilation(Label, Data). + +collect_warnings(#state{job = Job, servers = Servers}) -> + dialyzer_succ_typings:collect_warnings(Job, Servers). -- cgit v1.2.3 From a6ab15d61cd4b36ba9d4eab7a3d290330b5c3798 Mon Sep 17 00:00:00 2001 From: Stavros Aronis Date: Mon, 20 Feb 2012 16:45:54 +0100 Subject: Add better timing labels --- lib/dialyzer/src/dialyzer.hrl | 7 +++--- lib/dialyzer/src/dialyzer_analysis_callgraph.erl | 15 ++++++----- lib/dialyzer/src/dialyzer_succ_typings.erl | 32 +++++++++++++++--------- lib/dialyzer/src/dialyzer_timing.erl | 6 ++--- 4 files changed, 36 insertions(+), 24 deletions(-) (limited to 'lib/dialyzer') diff --git a/lib/dialyzer/src/dialyzer.hrl b/lib/dialyzer/src/dialyzer.hrl index e2edd3f0af..f282bbf73c 100644 --- a/lib/dialyzer/src/dialyzer.hrl +++ b/lib/dialyzer/src/dialyzer.hrl @@ -155,10 +155,11 @@ %%-------------------------------------------------------------------- --define(timing(Msg,Expr), +-define(timing(Msg,Var,Expr), begin dialyzer_timing:start_stamp(Msg), - __T = Expr, + Var = Expr, dialyzer_timing:end_stamp(), - __T + Var end). +-define(timing(Msg,Expr),?timing(Msg,_T,Expr)). diff --git a/lib/dialyzer/src/dialyzer_analysis_callgraph.erl b/lib/dialyzer/src/dialyzer_analysis_callgraph.erl index 30eb087fcf..02108d6e38 100644 --- a/lib/dialyzer/src/dialyzer_analysis_callgraph.erl +++ b/lib/dialyzer/src/dialyzer_analysis_callgraph.erl @@ -134,8 +134,7 @@ analysis_start(Parent, Analysis) -> use_contracts = Analysis#analysis.use_contracts }, Files = ordsets:from_list(Analysis#analysis.files), - {Callgraph, NoWarn, TmpCServer0} = - ?timing("compile",compile_and_store(Files, State)), + {Callgraph, NoWarn, TmpCServer0} = compile_and_store(Files, State), %% Remote type postprocessing NewCServer = try @@ -156,7 +155,8 @@ analysis_start(Parent, Analysis) -> dialyzer_codeserver:insert_temp_exported_types(MergedExpTypes, TmpCServer1), TmpCServer3 = dialyzer_utils:process_record_remote_types(TmpCServer2), - dialyzer_contracts:process_contract_remote_types(TmpCServer3) + ?timing("remote", + dialyzer_contracts:process_contract_remote_types(TmpCServer3)) catch throw:{error, _ErrorMsg} = Error -> exit(Error) end, @@ -228,9 +228,10 @@ compile_and_store(Files, #analysis_state{codeserver = CServer, lists:foreach(Spawner, Files), dialyzer_coordinator:all_spawned(Coordinator), {{V, E, Failed, NoWarn, Modules}, NextLabel} = - dialyzer_coordinator:receive_compilation_data(), + ?timing("compile", _C1, dialyzer_coordinator:receive_compilation_data()), CServer2 = dialyzer_codeserver:set_next_core_label(NextLabel, CServer), - Callgraph = dialyzer_callgraph:add_edges(E, V, Callgraph), + Callgraph = + ?timing("digraph", _C2, dialyzer_callgraph:add_edges(E, V, Callgraph)), case Failed =:= [] of true -> NewFiles = lists:zip(lists:reverse(Modules), Files), @@ -246,7 +247,9 @@ compile_and_store(Files, #analysis_state{codeserver = CServer, {T2, _} = statistics(runtime), Msg1 = io_lib:format("done in ~.2f secs\nRemoving edges... ", [(T2-T1)/1000]), send_log(Parent, Msg1), - Callgraph = cleanup_callgraph(State, CServer2, Callgraph, Modules), + Callgraph = + ?timing("clean", _C3, + cleanup_callgraph(State, CServer2, Callgraph, Modules)), {T3, _} = statistics(runtime), Msg2 = io_lib:format("done in ~.2f secs\n", [(T3-T2)/1000]), send_log(Parent, Msg2), diff --git a/lib/dialyzer/src/dialyzer_succ_typings.erl b/lib/dialyzer/src/dialyzer_succ_typings.erl index fbd333f905..c1c437f9a4 100644 --- a/lib/dialyzer/src/dialyzer_succ_typings.erl +++ b/lib/dialyzer/src/dialyzer_succ_typings.erl @@ -90,7 +90,8 @@ analyze_callgraph(Callgraph, Plt, Codeserver, Parent) -> %%-------------------------------------------------------------------- init_state_and_get_success_typings(Callgraph, Plt, Codeserver, Parent) -> - {SCCs, Callgraph1} = dialyzer_callgraph:finalize(Callgraph), + {SCCs, Callgraph1} = + ?timing("order", dialyzer_callgraph:finalize(Callgraph)), State = #st{callgraph = Callgraph1, plt = dialyzer_plt:get_mini_plt(Plt), codeserver = Codeserver, parent = Parent}, get_refined_success_typings(SCCs, State). @@ -102,7 +103,10 @@ get_refined_success_typings(SCCs, State) -> Callgraph = State1#st.callgraph, NotFixpoint2 = [lookup_name(F, Callgraph) || F <- NotFixpoint1], {ModulePostorder, ModCallgraph} = - dialyzer_callgraph:module_postorder_from_funs(NotFixpoint2, Callgraph), + ?timing( + "order", _C1, + dialyzer_callgraph:module_postorder_from_funs(NotFixpoint2, + Callgraph)), ModState = State1#st{callgraph = ModCallgraph}, case refine_succ_typings(ModulePostorder, ModState) of {fixpoint, State2} -> @@ -112,7 +116,9 @@ get_refined_success_typings(SCCs, State) -> %% Need to reset the callgraph. NotFixpoint4 = [lookup_name(F, Callgraph1) || F <- NotFixpoint3], {NewSCCs, Callgraph2} = - dialyzer_callgraph:reset_from_funs(NotFixpoint4, Callgraph1), + ?timing("order", _C2, + dialyzer_callgraph:reset_from_funs(NotFixpoint4, + Callgraph1)), NewState = State2#st{callgraph = Callgraph2}, get_refined_success_typings(NewSCCs, NewState) end @@ -325,7 +331,15 @@ find_succ_typings(SCCs, #st{codeserver = Codeserver, callgraph = Callgraph, plt = Plt} = State) -> Servers = {Codeserver, dialyzer_callgraph:mini_callgraph(Callgraph), Plt}, Coordinator = dialyzer_coordinator:start(typesig, Servers), - ?timing("typesig", find_succ_typings(SCCs, State, Coordinator)). + ?timing("spawn", _C1, find_succ_typings(SCCs, State, Coordinator)), + dialyzer_coordinator:all_spawned(Coordinator), + NotFixpoint = + ?timing("typesig", _C2, dialyzer_coordinator:receive_not_fixpoint()), + ?debug("==================== Typesig done ====================\n\n", []), + case NotFixpoint =:= [] of + true -> {fixpoint, State}; + false -> {not_fixpoint, NotFixpoint, State} + end. find_succ_typings([SCC|Rest], #st{parent = Parent} = State, Coordinator) -> Msg = io_lib:format("Typesig analysis for SCC: ~w\n", [format_scc(SCC)]), @@ -333,14 +347,8 @@ find_succ_typings([SCC|Rest], #st{parent = Parent} = State, Coordinator) -> send_log(Parent, Msg), dialyzer_coordinator:scc_spawn(SCC, Coordinator), find_succ_typings(Rest, State, Coordinator); -find_succ_typings([], State, Coordinator) -> - dialyzer_coordinator:all_spawned(Coordinator), - NotFixpoint = dialyzer_coordinator:receive_not_fixpoint(), - ?debug("==================== Typesig done ====================\n\n", []), - case NotFixpoint =:= [] of - true -> {fixpoint, State}; - false -> {not_fixpoint, NotFixpoint, State} - end. +find_succ_typings([], _State, _Coordinator) -> + ok. -spec collect_scc_data(scc(), servers()) -> scc_data(). diff --git a/lib/dialyzer/src/dialyzer_timing.erl b/lib/dialyzer/src/dialyzer_timing.erl index 73dbbf5a34..368c1715ce 100644 --- a/lib/dialyzer/src/dialyzer_timing.erl +++ b/lib/dialyzer/src/dialyzer_timing.erl @@ -56,13 +56,13 @@ dummy_loop() -> loop(LastNow) -> receive {stamp, Msg, Now} -> - io:format("~s\t(+~6.2fs): ", [Msg, diff(Now, LastNow)]), + io:format(" ~-10s (+~4.2fs):", [Msg, diff(Now, LastNow)]), loop(Now); {stamp, Now} -> - io:format("~6.2fs\n", [diff(Now, LastNow)]), + io:format("~7.2fs\n", [diff(Now, LastNow)]), loop(Now); {Pid, stop, Now} -> - io:format("\t(+~6.2fs)\n", [diff(Now, LastNow)]), + io:format(" ~-10s (+~4.2fs)\n", ["",diff(Now, LastNow)]), Pid ! ok end. -- cgit v1.2.3 From cf573e2ea378bae4c43007fb457dcd8379caf547 Mon Sep 17 00:00:00 2001 From: Stavros Aronis Date: Mon, 20 Feb 2012 21:29:22 +0100 Subject: Simplify behaviour checking code --- lib/dialyzer/src/dialyzer_behaviours.erl | 19 ++++++++----------- lib/dialyzer/src/dialyzer_coordinator.erl | 14 ++++++++------ lib/dialyzer/src/dialyzer_succ_typings.erl | 3 ++- 3 files changed, 18 insertions(+), 18 deletions(-) (limited to 'lib/dialyzer') diff --git a/lib/dialyzer/src/dialyzer_behaviours.erl b/lib/dialyzer/src/dialyzer_behaviours.erl index f86cc3d3d6..d40caae8b4 100644 --- a/lib/dialyzer/src/dialyzer_behaviours.erl +++ b/lib/dialyzer/src/dialyzer_behaviours.erl @@ -30,7 +30,7 @@ -module(dialyzer_behaviours). --export([check_callbacks/4, get_behaviour_apis/1, +-export([check_callbacks/5, get_behaviour_apis/1, translate_behaviour_api_call/5, translatable_behaviours/1, translate_callgraph/3]). @@ -47,15 +47,16 @@ -record(state, {plt :: dialyzer_plt:plt(), codeserver :: dialyzer_codeserver:codeserver(), filename :: file:filename(), - behlines :: [{behaviour(), non_neg_integer()}]}). + behlines :: [{behaviour(), non_neg_integer()}], + records :: dict()}). %%-------------------------------------------------------------------- --spec check_callbacks(module(), [{cerl:cerl(), cerl:cerl()}], +-spec check_callbacks(module(), [{cerl:cerl(), cerl:cerl()}], dict(), dialyzer_plt:plt(), dialyzer_codeserver:codeserver()) -> [dial_warning()]. -check_callbacks(Module, Attrs, Plt, Codeserver) -> +check_callbacks(Module, Attrs, Records, Plt, Codeserver) -> {Behaviours, BehLines} = get_behaviours(Attrs), case Behaviours of [] -> []; @@ -64,7 +65,7 @@ check_callbacks(Module, Attrs, Plt, Codeserver) -> {_Var,Code} = dialyzer_codeserver:lookup_mfa_code(MFA, Codeserver), File = get_file(cerl:get_ann(Code)), State = #state{plt = Plt, filename = File, behlines = BehLines, - codeserver = Codeserver}, + codeserver = Codeserver, records = Records}, Warnings = get_warnings(Module, Behaviours, State), [add_tag_file_line(Module, W, State) || W <- Warnings] end. @@ -100,17 +101,13 @@ check_behaviour(Module, Behaviour, #state{plt = Plt} = State, Acc) -> check_all_callbacks(_Module, _Behaviour, [], _State, Acc) -> Acc; check_all_callbacks(Module, Behaviour, [Cb|Rest], - #state{plt = Plt, codeserver = Codeserver} = State, Acc) -> + #state{plt = Plt, codeserver = Codeserver, + records = Records} = State, Acc) -> {{Behaviour, Function, Arity}, {{_BehFile, _BehLine}, Callback}} = Cb, CbMFA = {Module, Function, Arity}, CbReturnType = dialyzer_contracts:get_contract_return(Callback), CbArgTypes = dialyzer_contracts:get_contract_args(Callback), - Records = - case dict:find(Module, dialyzer_codeserver:get_records(Codeserver)) of - {ok, V} -> V; - error -> dict:new() - end, Acc0 = Acc, Acc1 = case dialyzer_plt:lookup(Plt, CbMFA) of diff --git a/lib/dialyzer/src/dialyzer_coordinator.erl b/lib/dialyzer/src/dialyzer_coordinator.erl index 93159ce7df..437af69f5a 100644 --- a/lib/dialyzer/src/dialyzer_coordinator.erl +++ b/lib/dialyzer/src/dialyzer_coordinator.erl @@ -220,18 +220,20 @@ handle_cast({done, Job, NewData}, #state{mode = Mode, spawn_count = SpawnCount, all_spawned = AllSpawned, - result = OldResult + result = OldResult, + job_to_pid = JobToPID } = State) -> - NewResult = + {NewResult, NewJobToPID} = case Mode of X when X =:= 'typesig'; X =:= 'dataflow' -> - ordsets:union(OldResult, NewData); + {ordsets:union(OldResult, NewData), dict:erase(Job, JobToPID)}; 'compile' -> - dialyzer_analysis_callgraph:add_to_result(Job, NewData, OldResult); + {dialyzer_analysis_callgraph:add_to_result(Job, NewData, OldResult), + JobToPID}; 'warnings' -> - NewData ++ OldResult + {NewData ++ OldResult, dict:erase(Job, JobToPID)} end, - UpdatedState = State#state{result = NewResult}, + UpdatedState = State#state{result = NewResult, job_to_pid = NewJobToPID}, Action = case AllSpawned of false -> reduce; diff --git a/lib/dialyzer/src/dialyzer_succ_typings.erl b/lib/dialyzer/src/dialyzer_succ_typings.erl index c1c437f9a4..9a37a54206 100644 --- a/lib/dialyzer/src/dialyzer_succ_typings.erl +++ b/lib/dialyzer/src/dialyzer_succ_typings.erl @@ -171,7 +171,8 @@ collect_warnings(M, {Callgraph, Codeserver, NoWarnUnused, Plt, DocPlt}) -> dialyzer_dataflow:get_warnings(ModCode, Plt, Callgraph, Records, NoWarnUnused), Attrs = cerl:module_attrs(ModCode), - Warnings3 = dialyzer_behaviours:check_callbacks(M, Attrs, Plt, Codeserver), + Warnings3 = + dialyzer_behaviours:check_callbacks(M, Attrs, Records, Plt, Codeserver), DocPlt = insert_into_doc_plt(FunTypes, Callgraph, DocPlt), lists:flatten([Warnings1, Warnings2, Warnings3]). -- cgit v1.2.3 From 0bae6c82503b348e62f11edfbfc880145ef06794 Mon Sep 17 00:00:00 2001 From: Stavros Aronis Date: Tue, 21 Feb 2012 16:01:50 +0100 Subject: Avoid digraph_utils:condensation and ordering in typesig --- lib/dialyzer/src/dialyzer_callgraph.erl | 85 ++++++++++++++++++++++++------- lib/dialyzer/src/dialyzer_coordinator.erl | 15 ++++-- lib/dialyzer/src/dialyzer_worker.erl | 28 ++++++---- 3 files changed, 95 insertions(+), 33 deletions(-) (limited to 'lib/dialyzer') diff --git a/lib/dialyzer/src/dialyzer_callgraph.erl b/lib/dialyzer/src/dialyzer_callgraph.erl index bb96ada65f..0c3e8ca311 100644 --- a/lib/dialyzer/src/dialyzer_callgraph.erl +++ b/lib/dialyzer/src/dialyzer_callgraph.erl @@ -92,7 +92,7 @@ %%----------------------------------------------------------------------------- -record(callgraph, {digraph = digraph:new() :: digraph(), - active_digraph :: digraph(), + active_digraph :: active_digraph(), esc :: ets:tid(), name_map :: ets:tid(), rev_name_map :: ets:tid(), @@ -111,6 +111,8 @@ -type callgraph() :: #callgraph{}. +-type active_digraph() :: {'d', digraph()} | {'e', ets:tid(), ets:tid()}. + %%---------------------------------------------------------------------- -spec new() -> callgraph(). @@ -247,12 +249,22 @@ find_non_local_calls([], Set) -> -spec get_depends_on(scc(), callgraph()) -> [scc()]. -get_depends_on(SCC, #callgraph{active_digraph = DG}) -> +get_depends_on(SCC, #callgraph{active_digraph = {'e', Out, _In}}) -> + case ets_lookup_dict(SCC, Out) of + {ok, Value} -> Value; + error -> [] + end; +get_depends_on(SCC, #callgraph{active_digraph = {'d', DG}}) -> digraph:out_neighbours(DG, SCC). -spec get_required_by(scc(), callgraph()) -> [scc()]. -get_required_by(SCC, #callgraph{active_digraph = DG}) -> +get_required_by(SCC, #callgraph{active_digraph = {'e', _Out, In}}) -> + case ets_lookup_dict(SCC, In) of + {ok, Value} -> Value; + error -> [] + end; +get_required_by(SCC, #callgraph{active_digraph = {'d', DG}}) -> digraph:in_neighbours(DG, SCC). %%---------------------------------------------------------------------- @@ -273,7 +285,7 @@ module_postorder(#callgraph{digraph = DG}) -> MDG1 = digraph_confirm_vertices(Nodes, MDG), MDG2 = create_module_digraph(Edges, MDG1), PostOrder = digraph_utils:topsort(MDG2), - {PostOrder, MDG2}. + {PostOrder, {'d', MDG2}}. %% The module deps of a module are modules that depend on the module -spec module_deps(callgraph()) -> dict(). @@ -310,23 +322,24 @@ create_module_digraph([], MDG) -> -spec finalize(callgraph()) -> {[scc()], callgraph()}. finalize(#callgraph{digraph = DG} = CG) -> - {ActiveDG, Postorder} = digraph_finalize(DG), + {ActiveDG, Postorder} = condensation(DG), {Postorder, CG#callgraph{active_digraph = ActiveDG}}. -spec reset_from_funs([mfa_or_funlbl()], callgraph()) -> {[scc()], callgraph()}. -reset_from_funs(Funs, #callgraph{digraph = DG, - active_digraph = OldActiveDG} = CG) -> - digraph_delete(OldActiveDG), +reset_from_funs(Funs, #callgraph{digraph = DG, active_digraph = ADG} = CG) -> + active_digraph_delete(ADG), SubGraph = digraph_reaching_subgraph(Funs, DG), - {NewActiveDG, Postorder} = digraph_finalize(SubGraph), + {NewActiveDG, Postorder} = condensation(SubGraph), digraph_delete(SubGraph), {Postorder, CG#callgraph{active_digraph = NewActiveDG}}. -spec module_postorder_from_funs([mfa_or_funlbl()], callgraph()) -> {[module()], callgraph()}. -module_postorder_from_funs(Funs, #callgraph{digraph = DG} = CG) -> +module_postorder_from_funs(Funs, #callgraph{digraph = DG, + active_digraph = ADG} = CG) -> + active_digraph_delete(ADG), SubGraph = digraph_reaching_subgraph(Funs, DG), {PO, Active} = module_postorder(CG#callgraph{digraph = SubGraph}), digraph_delete(SubGraph), @@ -544,6 +557,12 @@ remove_unconfirmed([], DG, Unconfirmed) -> digraph_delete(DG) -> digraph:delete(DG). +active_digraph_delete({'d', DG}) -> + digraph:delete(DG); +active_digraph_delete({'e', Out, In}) -> + ets:delete(Out), + ets:delete(In). + digraph_edges(DG) -> digraph:edges(DG). @@ -556,14 +575,6 @@ digraph_in_neighbours(V, DG) -> List -> List end. -digraph_postorder(Digraph) -> - digraph_utils:topsort(Digraph). - -digraph_finalize(DG) -> - DG1 = digraph_utils:condensation(DG), - Postorder = digraph_postorder(DG1), - {DG1, Postorder}. - digraph_reaching_subgraph(Funs, DG) -> Vertices = digraph_utils:reaching(Funs, DG), digraph_utils:subgraph(DG, Vertices). @@ -792,3 +803,41 @@ to_ps(#callgraph{} = CG, File, Args) -> Command = io_lib:format("dot -Tps ~s -o ~s ~s", [Args, File, Dot_File]), _ = os:cmd(Command), ok. + +condensation(G) -> + SCs = digraph_utils:strong_components(G), + V2I = ets:new(condensation, []), + I2C = ets:new(condensation, []), + I2I = ets:new(condensation, [bag]), + CFun = + fun(SC, N) -> + lists:foreach(fun(V) -> true = ets:insert(V2I, {V,N}) end, SC), + true = ets:insert(I2C, {N, SC}), + N + 1 + end, + lists:foldl(CFun, 1, SCs), + Fun1 = + fun({V1, V2}) -> + I1 = ets:lookup_element(V2I, V1, 2), + I2 = ets:lookup_element(V2I, V2, 2), + case I1 =:= I2 of + true -> true; + false -> ets:insert(I2I, {I1, I2}) + end + end, + lists:foreach(Fun1, digraph:edges(G)), + Fun3 = + fun({I1, I2}, {Out, In}) -> + SC1 = ets:lookup_element(I2C, I1, 2), + SC2 = ets:lookup_element(I2C, I2, 2), + {dict:append(SC1, SC2, Out), dict:append(SC2, SC1, In)} + end, + {OutDict, InDict} = ets:foldl(Fun3, {dict:new(), dict:new()}, I2I), + OutETS = ets:new(out,[]), + InETS = ets:new(in,[]), + ets:insert(OutETS, dict:to_list(OutDict)), + ets:insert(InETS, dict:to_list(InDict)), + ets:delete(V2I), + ets:delete(I2C), + ets:delete(I2I), + {{'e', OutETS, InETS}, SCs}. diff --git a/lib/dialyzer/src/dialyzer_coordinator.erl b/lib/dialyzer/src/dialyzer_coordinator.erl index 437af69f5a..9d31c739d0 100644 --- a/lib/dialyzer/src/dialyzer_coordinator.erl +++ b/lib/dialyzer/src/dialyzer_coordinator.erl @@ -123,8 +123,8 @@ sccs_to_pids_request(SCCs, Coordinator) -> cast({sccs_to_pids, SCCs, self()}, Coordinator). scc_to_pids_request_handle(Worker, SCCs, SCCtoPID) -> - Pids = [fetch_map(SCC, SCCtoPID) || SCC <- SCCs], - Worker ! {sccs_to_pids, Pids}, + Result = map_lookup(SCCs, SCCtoPID), + Worker ! {sccs_to_pids, Result}, ok. -spec sccs_to_pids_reply() -> [dialyzer_worker:worker()]. @@ -314,5 +314,12 @@ new_map() -> store_map(Key, Value, Map) -> dict:store(Key, Value, Map). -fetch_map(Key, Map) -> - dict:fetch(Key, Map). +map_lookup(SCCs, Map) -> + Fold = + fun(Key, {Mapped, Unknown}) -> + case dict:find(Key, Map) of + {ok, Value} -> {[Value|Mapped], Unknown}; + error -> {Mapped, [Key|Unknown]} + end + end, + lists:foldl(Fold, {[], []}, SCCs). diff --git a/lib/dialyzer/src/dialyzer_worker.erl b/lib/dialyzer/src/dialyzer_worker.erl index 6608b78f0a..0efa5d9b01 100644 --- a/lib/dialyzer/src/dialyzer_worker.erl +++ b/lib/dialyzer/src/dialyzer_worker.erl @@ -88,9 +88,8 @@ loop(updating, State) -> loop(NextStatus, State); loop(initializing, #state{job = SCC, servers = Servers} = State) -> DependsOn = dialyzer_succ_typings:find_depends_on(SCC, Servers), - WithoutSelf = DependsOn -- [SCC], - ?debug("Deps ~p: ~p\n",[State#state.job, WithoutSelf]), - loop(updating, State#state{depends_on = WithoutSelf}); + ?debug("Deps ~p: ~p\n",[State#state.job, DependsOn]), + loop(updating, State#state{depends_on = DependsOn}); loop(waiting, State) -> ?debug("Wait: ~p\n",[State#state.job]), NewState = wait_for_success_typings(State), @@ -118,8 +117,7 @@ loop(running, #state{mode = Mode} = State) when ?debug("Run: ~p\n",[State#state.job]), ok = ask_coordinator_for_callers(State), NotFixpoint = do_work(State), - Callers = get_callers_reply_from_coordinator(), - ok = broadcast_done(State, Callers), + ok = broadcast_done(State), report_to_coordinator(NotFixpoint, State). waits_more_success_typings(#state{depends_on = Depends}) -> @@ -147,18 +145,26 @@ ask_coordinator_for_callers(#state{job = SCC, servers = Servers, coordinator = Coordinator}) -> RequiredBy = dialyzer_succ_typings:find_required_by(SCC, Servers), - WithoutSelf = RequiredBy -- [SCC], - ?debug("Waiting for me~p: ~p\n",[SCC, WithoutSelf]), - dialyzer_coordinator:sccs_to_pids_request(WithoutSelf, Coordinator). + ?debug("Waiting for me~p: ~p\n",[SCC, RequiredBy]), + dialyzer_coordinator:sccs_to_pids_request(RequiredBy, Coordinator). -get_callers_reply_from_coordinator() -> - dialyzer_coordinator:sccs_to_pids_reply(). +broadcast_done(#state{job = SCC, coordinator = Coordinator}) -> + {Callers, Unknown} = dialyzer_coordinator:sccs_to_pids_reply(), + send_done(Callers, SCC), + continue_broadcast_done(Unknown, SCC, Coordinator). -broadcast_done(#state{job = SCC}, Callers) -> +send_done(Callers, SCC) -> ?debug("Sending ~p: ~p\n",[SCC, Callers]), SendSTFun = fun(PID) -> PID ! {done, SCC} end, lists:foreach(SendSTFun, Callers). +continue_broadcast_done([], _SCC, _Coordinator) -> ok; +continue_broadcast_done(Rest, SCC, Coordinator) -> + dialyzer_coordinator:sccs_to_pids_request(Rest, Coordinator), + {Callers, Unknown} = dialyzer_coordinator:sccs_to_pids_reply(), + send_done(Callers, SCC), + continue_broadcast_done(Unknown, SCC, Coordinator). + wait_for_success_typings(#state{depends_on = DependsOn} = State) -> receive {done, SCC} -> -- cgit v1.2.3 From 7b622408befa56eefb2dc821c195727fe840d055 Mon Sep 17 00:00:00 2001 From: Stavros Aronis Date: Wed, 22 Feb 2012 12:18:32 +0100 Subject: Add read_concurrency option to some ETS tables --- lib/dialyzer/src/dialyzer_callgraph.erl | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'lib/dialyzer') diff --git a/lib/dialyzer/src/dialyzer_callgraph.erl b/lib/dialyzer/src/dialyzer_callgraph.erl index 0c3e8ca311..cd465b26dc 100644 --- a/lib/dialyzer/src/dialyzer_callgraph.erl +++ b/lib/dialyzer/src/dialyzer_callgraph.erl @@ -119,7 +119,7 @@ new() -> [ETSEsc, ETSNameMap, ETSRevNameMap, ETSRecVarMap, ETSSelfRec, ETSCalls] = - [ets:new(N,[public]) || + [ets:new(N,[public, {read_concurrency, true}]) || N <- [callgraph_esc, callgraph_name_map, callgraph_rev_name_map, callgraph_rec_var_map, callgraph_self_rec, callgraph_calls]], #callgraph{esc = ETSEsc, @@ -833,8 +833,8 @@ condensation(G) -> {dict:append(SC1, SC2, Out), dict:append(SC2, SC1, In)} end, {OutDict, InDict} = ets:foldl(Fun3, {dict:new(), dict:new()}, I2I), - OutETS = ets:new(out,[]), - InETS = ets:new(in,[]), + [OutETS, InETS] = + [ets:new(Name,[{read_concurrency, true}]) || Name <- [out, in]], ets:insert(OutETS, dict:to_list(OutDict)), ets:insert(InETS, dict:to_list(InDict)), ets:delete(V2I), -- cgit v1.2.3 From e200c6519651319ddcb874d0aff23cc21e1a9c83 Mon Sep 17 00:00:00 2001 From: Stavros Aronis Date: Wed, 22 Feb 2012 16:04:20 +0100 Subject: Fix specs --- lib/dialyzer/src/dialyzer_callgraph.erl | 6 +++--- lib/dialyzer/src/dialyzer_coordinator.erl | 2 +- lib/dialyzer/src/dialyzer_succ_typings.erl | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) (limited to 'lib/dialyzer') diff --git a/lib/dialyzer/src/dialyzer_callgraph.erl b/lib/dialyzer/src/dialyzer_callgraph.erl index cd465b26dc..80f2c48676 100644 --- a/lib/dialyzer/src/dialyzer_callgraph.erl +++ b/lib/dialyzer/src/dialyzer_callgraph.erl @@ -247,7 +247,7 @@ find_non_local_calls([{Label1, Label2}|Left], Set) when is_integer(Label1), find_non_local_calls([], Set) -> sets:to_list(Set). --spec get_depends_on(scc(), callgraph()) -> [scc()]. +-spec get_depends_on(scc() | module(), callgraph()) -> [scc()]. get_depends_on(SCC, #callgraph{active_digraph = {'e', Out, _In}}) -> case ets_lookup_dict(SCC, Out) of @@ -257,7 +257,7 @@ get_depends_on(SCC, #callgraph{active_digraph = {'e', Out, _In}}) -> get_depends_on(SCC, #callgraph{active_digraph = {'d', DG}}) -> digraph:out_neighbours(DG, SCC). --spec get_required_by(scc(), callgraph()) -> [scc()]. +-spec get_required_by(scc() | module(), callgraph()) -> [scc()]. get_required_by(SCC, #callgraph{active_digraph = {'e', _Out, In}}) -> case ets_lookup_dict(SCC, In) of @@ -276,7 +276,7 @@ get_required_by(SCC, #callgraph{active_digraph = {'d', DG}}) -> modules(#callgraph{digraph = DG}) -> ordsets:from_list([M || {M,_F,_A} <- digraph_vertices(DG)]). --spec module_postorder(callgraph()) -> {[module()], digraph()}. +-spec module_postorder(callgraph()) -> {[module()], {'d', digraph()}}. module_postorder(#callgraph{digraph = DG}) -> Edges = digraph_edges(DG), diff --git a/lib/dialyzer/src/dialyzer_coordinator.erl b/lib/dialyzer/src/dialyzer_coordinator.erl index 9d31c739d0..1dc3e97f7c 100644 --- a/lib/dialyzer/src/dialyzer_coordinator.erl +++ b/lib/dialyzer/src/dialyzer_coordinator.erl @@ -127,7 +127,7 @@ scc_to_pids_request_handle(Worker, SCCs, SCCtoPID) -> Worker ! {sccs_to_pids, Result}, ok. --spec sccs_to_pids_reply() -> [dialyzer_worker:worker()]. +-spec sccs_to_pids_reply() -> {[dialyzer_worker:worker()], [scc()]}. sccs_to_pids_reply() -> receive {sccs_to_pids, Pids} -> Pids end. diff --git a/lib/dialyzer/src/dialyzer_succ_typings.erl b/lib/dialyzer/src/dialyzer_succ_typings.erl index 9a37a54206..3b8e3f890b 100644 --- a/lib/dialyzer/src/dialyzer_succ_typings.erl +++ b/lib/dialyzer/src/dialyzer_succ_typings.erl @@ -240,12 +240,12 @@ refine_succ_typings([], State, Coordinator) -> -type scc_refine_data() :: term(). %%opaque -type scc() :: [mfa_or_funlbl()] | [module()]. --spec find_depends_on(scc(), servers()) -> [scc()]. +-spec find_depends_on(scc() | module(), servers()) -> [scc()]. find_depends_on(SCC, {_Codeserver, Callgraph, _Plt}) -> dialyzer_callgraph:get_depends_on(SCC, Callgraph). --spec find_required_by(scc(), servers()) -> [scc()]. +-spec find_required_by(scc() | module(), servers()) -> [scc()]. find_required_by(SCC, {_Codeserver, Callgraph, _Plt}) -> dialyzer_callgraph:get_required_by(SCC, Callgraph). -- cgit v1.2.3 From b3f3b14737ca72e218f344120a72293aed1bb00f Mon Sep 17 00:00:00 2001 From: Stavros Aronis Date: Wed, 22 Feb 2012 16:05:01 +0100 Subject: Remove data prefetching --- lib/dialyzer/src/dialyzer_succ_typings.erl | 52 +++++++++++------------------- lib/dialyzer/src/dialyzer_worker.erl | 39 +++------------------- 2 files changed, 23 insertions(+), 68 deletions(-) (limited to 'lib/dialyzer') diff --git a/lib/dialyzer/src/dialyzer_succ_typings.erl b/lib/dialyzer/src/dialyzer_succ_typings.erl index 3b8e3f890b..a8bdf9aee7 100644 --- a/lib/dialyzer/src/dialyzer_succ_typings.erl +++ b/lib/dialyzer/src/dialyzer_succ_typings.erl @@ -33,16 +33,14 @@ ]). -export([ - find_succ_types_for_scc/1, - refine_one_module/1, - collect_scc_data/2, - collect_refine_scc_data/2, + find_succ_types_for_scc/2, + refine_one_module/2, find_required_by/2, find_depends_on/2, collect_warnings/2 ]). --export_type([servers/0, scc_data/0, scc_refine_data/0, warning_servers/0]). +-export_type([servers/0, warning_servers/0]). %%-define(DEBUG, true). @@ -62,6 +60,9 @@ %% State record -- local to this module -type parent() :: 'none' | pid(). +-type servers() :: term(). %%opaque +-type scc() :: [mfa_or_funlbl()] | [module()]. + -record(st, {callgraph :: dialyzer_callgraph:callgraph(), codeserver :: dialyzer_codeserver:codeserver(), @@ -235,11 +236,6 @@ refine_succ_typings([], State, Coordinator) -> false -> {not_fixpoint, NotFixpoint, State} end. --type servers() :: term(). %%opaque --type scc_data() :: term(). %%opaque --type scc_refine_data() :: term(). %%opaque --type scc() :: [mfa_or_funlbl()] | [module()]. - -spec find_depends_on(scc() | module(), servers()) -> [scc()]. find_depends_on(SCC, {_Codeserver, Callgraph, _Plt}) -> @@ -250,18 +246,13 @@ find_depends_on(SCC, {_Codeserver, Callgraph, _Plt}) -> find_required_by(SCC, {_Codeserver, Callgraph, _Plt}) -> dialyzer_callgraph:get_required_by(SCC, Callgraph). --spec collect_refine_scc_data(module(), servers()) -> scc_refine_data(). +-spec refine_one_module(module(), servers()) -> [label()]. % ordset -collect_refine_scc_data(M, {CodeServer, Callgraph, PLT}) -> +refine_one_module(M, {CodeServer, Callgraph, Plt}) -> ModCode = dialyzer_codeserver:lookup_mod_code(M, CodeServer), AllFuns = collect_fun_info([ModCode]), Records = dialyzer_codeserver:lookup_mod_records(M, CodeServer), - FunTypes = get_fun_types_from_plt(AllFuns, Callgraph, PLT), - {ModCode, PLT, Callgraph, Records, FunTypes}. - --spec refine_one_module(scc_refine_data()) -> [label()]. % ordset - -refine_one_module({ModCode, Plt, Callgraph, Records, FunTypes}) -> + FunTypes = get_fun_types_from_plt(AllFuns, Callgraph, Plt), NewFunTypes = dialyzer_dataflow:get_fun_types(ModCode, Plt, Callgraph, Records), case reached_fixpoint(FunTypes, NewFunTypes) of @@ -351,9 +342,9 @@ find_succ_typings([SCC|Rest], #st{parent = Parent} = State, Coordinator) -> find_succ_typings([], _State, _Coordinator) -> ok. --spec collect_scc_data(scc(), servers()) -> scc_data(). +-spec find_succ_types_for_scc(scc(), servers()) -> [mfa_or_funlbl()]. -collect_scc_data(SCC, {Codeserver, Callgraph, Plt}) -> +find_succ_types_for_scc(SCC, {Codeserver, Callgraph, Plt}) -> SCC_Info = [{MFA, dialyzer_codeserver:lookup_mfa_code(MFA, Codeserver), dialyzer_codeserver:lookup_mod_records(M, Codeserver)} @@ -362,25 +353,18 @@ collect_scc_data(SCC, {Codeserver, Callgraph, Plt}) -> || {_, _, _} = MFA <- SCC], Contracts2 = [{MFA, Contract} || {MFA, {ok, Contract}} <- Contracts1], Contracts3 = orddict:from_list(Contracts2), - NextLabel = dialyzer_codeserver:get_next_core_label(Codeserver), + Label = dialyzer_codeserver:get_next_core_label(Codeserver), AllFuns = collect_fun_info([Fun || {_MFA, {_Var, Fun}, _Rec} <- SCC_Info]), PropTypes = get_fun_types_from_plt(AllFuns, Callgraph, Plt), - {SCC_Info, Contracts3, NextLabel, AllFuns, PropTypes, Callgraph, Plt}. - --spec find_succ_types_for_scc(scc_data()) -> [mfa_or_funlbl()]. - -find_succ_types_for_scc({SCC_Info, Contracts, NextLabel, AllFuns, - PropTypes, Callgraph, Plt}) -> %% Assume that the PLT contains the current propagated types - FunTypes = dialyzer_typesig:analyze_scc(SCC_Info, NextLabel, - Callgraph, Plt, PropTypes), + FunTypes = + dialyzer_typesig:analyze_scc(SCC_Info, Label, Callgraph, Plt, PropTypes), AllFunSet = sets:from_list([X || {X, _} <- AllFuns]), - FilteredFunTypes = dict:filter(fun(X, _) -> - sets:is_element(X, AllFunSet) - end, FunTypes), + FilteredFunTypes = + dict:filter(fun(X, _) -> sets:is_element(X, AllFunSet) end, FunTypes), %% Check contracts - PltContracts = dialyzer_contracts:check_contracts(Contracts, Callgraph, - FilteredFunTypes), + PltContracts = + dialyzer_contracts:check_contracts(Contracts3, Callgraph, FilteredFunTypes), ContractFixpoint = lists:all(fun({MFA, _C}) -> %% Check the non-deleted PLT diff --git a/lib/dialyzer/src/dialyzer_worker.erl b/lib/dialyzer/src/dialyzer_worker.erl index 0efa5d9b01..74e227f587 100644 --- a/lib/dialyzer/src/dialyzer_worker.erl +++ b/lib/dialyzer/src/dialyzer_worker.erl @@ -30,16 +30,13 @@ -type coordinator() :: dialyzer_coordinator:coordinator(). -type servers() :: dialyzer_succ_typings:servers() | dialyzer_analysis_callgraph:servers(). --type data() :: dialyzer_succ_typings:scc_data() | - dialyzer_succ_typings:scc_refine_data(). -record(state, { mode :: mode(), job :: mfa_or_funlbl() | file:filename(), coordinator :: coordinator(), servers :: servers(), - depends_on = [] :: list(), - scc_data :: data() + depends_on = [] :: list() }). -include("dialyzer.hrl"). @@ -75,15 +72,7 @@ loop(updating, State) -> NextStatus = case waits_more_success_typings(State) of true -> waiting; - Other -> - case has_data(State) of - false -> getting_data; - true -> - case Other of - imminent -> waiting; - false -> running - end - end + false -> running end, loop(NextStatus, State); loop(initializing, #state{job = SCC, servers = Servers} = State) -> @@ -94,9 +83,6 @@ loop(waiting, State) -> ?debug("Wait: ~p\n",[State#state.job]), NewState = wait_for_success_typings(State), loop(updating, NewState); -loop(getting_data, State) -> - ?debug("Data: ~p\n",[State#state.job]), - loop(updating, get_data(State)); loop(running, #state{mode = 'compile'} = State) -> ?debug("Compile: ~s\n",[State#state.job]), Result = @@ -123,24 +109,9 @@ loop(running, #state{mode = Mode} = State) when waits_more_success_typings(#state{depends_on = Depends}) -> case Depends of [] -> false; - [_] -> imminent; _ -> true end. -has_data(#state{scc_data = Data}) -> - case Data of - undefined -> false; - _ -> true - end. - -get_data(#state{mode = Mode, job = SCC, servers = Servers} = State) -> - Data = - case Mode of - typesig -> dialyzer_succ_typings:collect_scc_data(SCC, Servers); - dataflow -> dialyzer_succ_typings:collect_refine_scc_data(SCC, Servers) - end, - State#state{scc_data = Data}. - ask_coordinator_for_callers(#state{job = SCC, servers = Servers, coordinator = Coordinator}) -> @@ -176,10 +147,10 @@ wait_for_success_typings(#state{depends_on = DependsOn} = State) -> State end. -do_work(#state{mode = Mode, scc_data = SCCData}) -> +do_work(#state{mode = Mode, job = Job, servers = Servers}) -> case Mode of - typesig -> dialyzer_succ_typings:find_succ_types_for_scc(SCCData); - dataflow -> dialyzer_succ_typings:refine_one_module(SCCData) + typesig -> dialyzer_succ_typings:find_succ_types_for_scc(Job, Servers); + dataflow -> dialyzer_succ_typings:refine_one_module(Job, Servers) end. report_to_coordinator(Result, #state{job = Job, coordinator = Coordinator}) -> -- cgit v1.2.3 From 44b23610234bf4028aedd326388bec503aee1026 Mon Sep 17 00:00:00 2001 From: Stavros Aronis Date: Wed, 22 Feb 2012 16:48:42 +0100 Subject: Coordinator translates functions requiring further analysis --- lib/dialyzer/src/dialyzer_coordinator.erl | 6 ++++-- lib/dialyzer/src/dialyzer_succ_typings.erl | 22 ++++++++++++---------- 2 files changed, 16 insertions(+), 12 deletions(-) (limited to 'lib/dialyzer') diff --git a/lib/dialyzer/src/dialyzer_coordinator.erl b/lib/dialyzer/src/dialyzer_coordinator.erl index 1dc3e97f7c..1921f96b78 100644 --- a/lib/dialyzer/src/dialyzer_coordinator.erl +++ b/lib/dialyzer/src/dialyzer_coordinator.erl @@ -221,12 +221,14 @@ handle_cast({done, Job, NewData}, spawn_count = SpawnCount, all_spawned = AllSpawned, result = OldResult, - job_to_pid = JobToPID + job_to_pid = JobToPID, + init_job_data = Servers } = State) -> {NewResult, NewJobToPID} = case Mode of X when X =:= 'typesig'; X =:= 'dataflow' -> - {ordsets:union(OldResult, NewData), dict:erase(Job, JobToPID)}; + FinalData = dialyzer_succ_typings:lookup_names(NewData, Servers), + {ordsets:union(OldResult, FinalData), dict:erase(Job, JobToPID)}; 'compile' -> {dialyzer_analysis_callgraph:add_to_result(Job, NewData, OldResult), JobToPID}; diff --git a/lib/dialyzer/src/dialyzer_succ_typings.erl b/lib/dialyzer/src/dialyzer_succ_typings.erl index a8bdf9aee7..2856a26169 100644 --- a/lib/dialyzer/src/dialyzer_succ_typings.erl +++ b/lib/dialyzer/src/dialyzer_succ_typings.erl @@ -37,7 +37,8 @@ refine_one_module/2, find_required_by/2, find_depends_on/2, - collect_warnings/2 + collect_warnings/2, + lookup_names/2 ]). -export_type([servers/0, warning_servers/0]). @@ -97,29 +98,25 @@ init_state_and_get_success_typings(Callgraph, Plt, Codeserver, Parent) -> codeserver = Codeserver, parent = Parent}, get_refined_success_typings(SCCs, State). -get_refined_success_typings(SCCs, State) -> +get_refined_success_typings(SCCs, #st{callgraph = Callgraph} = State) -> case find_succ_typings(SCCs, State) of {fixpoint, State1} -> State1; {not_fixpoint, NotFixpoint1, State1} -> - Callgraph = State1#st.callgraph, - NotFixpoint2 = [lookup_name(F, Callgraph) || F <- NotFixpoint1], {ModulePostorder, ModCallgraph} = ?timing( "order", _C1, - dialyzer_callgraph:module_postorder_from_funs(NotFixpoint2, + dialyzer_callgraph:module_postorder_from_funs(NotFixpoint1, Callgraph)), ModState = State1#st{callgraph = ModCallgraph}, case refine_succ_typings(ModulePostorder, ModState) of {fixpoint, State2} -> State2; - {not_fixpoint, NotFixpoint3, State2} -> - Callgraph1 = State2#st.callgraph, + {not_fixpoint, NotFixpoint2, State2} -> %% Need to reset the callgraph. - NotFixpoint4 = [lookup_name(F, Callgraph1) || F <- NotFixpoint3], {NewSCCs, Callgraph2} = ?timing("order", _C2, - dialyzer_callgraph:reset_from_funs(NotFixpoint4, - Callgraph1)), + dialyzer_callgraph:reset_from_funs(NotFixpoint2, + ModCallgraph)), NewState = State2#st{callgraph = Callgraph2}, get_refined_success_typings(NewSCCs, NewState) end @@ -246,6 +243,11 @@ find_depends_on(SCC, {_Codeserver, Callgraph, _Plt}) -> find_required_by(SCC, {_Codeserver, Callgraph, _Plt}) -> dialyzer_callgraph:get_required_by(SCC, Callgraph). +-spec lookup_names([label()], servers()) -> [mfa_or_funlbl()]. + +lookup_names(Labels, {_Codeserver, Callgraph, _Plt}) -> + [lookup_name(F, Callgraph) || F <- Labels]. + -spec refine_one_module(module(), servers()) -> [label()]. % ordset refine_one_module(M, {CodeServer, Callgraph, Plt}) -> -- cgit v1.2.3 From dd1d17c1798a601dbe6378795c9a2fdd0effcc15 Mon Sep 17 00:00:00 2001 From: Stavros Aronis Date: Thu, 23 Feb 2012 13:46:51 +0100 Subject: Worker PIDs are stored in an ETS table --- lib/dialyzer/src/dialyzer_coordinator.erl | 89 +++++++++++++------------------ lib/dialyzer/src/dialyzer_worker.erl | 25 ++++----- 2 files changed, 46 insertions(+), 68 deletions(-) (limited to 'lib/dialyzer') diff --git a/lib/dialyzer/src/dialyzer_coordinator.erl b/lib/dialyzer/src/dialyzer_coordinator.erl index 1921f96b78..af85dfe82d 100644 --- a/lib/dialyzer/src/dialyzer_coordinator.erl +++ b/lib/dialyzer/src/dialyzer_coordinator.erl @@ -63,8 +63,7 @@ -export([receive_warnings/0]). %%% Exports for the typesig and dataflow analysis workers --export([sccs_to_pids_reply/0, - sccs_to_pids_request/2]). +-export([sccs_to_pids/1]). %%% Exports for the compilation main process -export([compiler_spawn/2, @@ -81,9 +80,12 @@ -export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]). +%%-------------------------------------------------------------------- + +-define(MAP, dialyzer_coordinator_map). + -type coordinator() :: pid(). %%opaque --type map() :: dict(). -type scc() :: [mfa_or_funlbl()] | module(). -type mode() :: 'typesig' | 'dataflow' | 'compile' | 'warnings'. -type servers() :: dialyzer_succ_typings:servers() | @@ -94,7 +96,6 @@ mode :: mode(), spawn_count = 0 :: integer(), all_spawned = false :: boolean(), - job_to_pid :: map(), next_label :: integer(), result :: [mfa_or_funlbl()] | dialyzer_analysis_callgraph:result() | @@ -117,20 +118,17 @@ start(Mode, Servers) -> scc_spawn(SCC, Coordinator) -> cast({scc_spawn, SCC}, Coordinator). --spec sccs_to_pids_request([scc()], coordinator()) -> ok. +-spec sccs_to_pids([scc()]) -> {[dialyzer_worker:worker()], [scc()]}. -sccs_to_pids_request(SCCs, Coordinator) -> - cast({sccs_to_pids, SCCs, self()}, Coordinator). - -scc_to_pids_request_handle(Worker, SCCs, SCCtoPID) -> - Result = map_lookup(SCCs, SCCtoPID), - Worker ! {sccs_to_pids, Result}, - ok. +sccs_to_pids(SCCs) -> + lists:foldl(fun pid_partition/2, {[], []}, SCCs). --spec sccs_to_pids_reply() -> {[dialyzer_worker:worker()], [scc()]}. - -sccs_to_pids_reply() -> - receive {sccs_to_pids, Pids} -> Pids end. +pid_partition(SCC, {Pids, Unknown}) -> + try ets:lookup_element(?MAP, SCC, 2) of + Result -> {[Result|Pids], Unknown} + catch + _:_ -> {Pids, [SCC|Unknown]} + end. -spec job_done(scc() | file:filename(), term(), coordinator()) -> ok. @@ -198,8 +196,11 @@ init({Parent, Mode, InitJobData}) -> InitState = #state{parent = Parent, mode = Mode, init_job_data = InitJobData}, State = case Mode of - X when X =:= 'typesig'; X =:= 'dataflow'; X =:= 'warnings' -> - InitState#state{result = [], job_to_pid = new_map()}; + X when X =:= 'typesig'; X =:= 'dataflow' -> + ?MAP = ets:new(?MAP, [named_table, {read_concurrency, true}]), + InitState#state{result = []}; + 'warnings' -> + InitState#state{result = []}; 'compile' -> InitResult = dialyzer_analysis_callgraph:compile_coordinator_init(), InitState#state{result = InitResult, next_label = 0} @@ -221,21 +222,19 @@ handle_cast({done, Job, NewData}, spawn_count = SpawnCount, all_spawned = AllSpawned, result = OldResult, - job_to_pid = JobToPID, init_job_data = Servers } = State) -> - {NewResult, NewJobToPID} = + NewResult = case Mode of X when X =:= 'typesig'; X =:= 'dataflow' -> FinalData = dialyzer_succ_typings:lookup_names(NewData, Servers), - {ordsets:union(OldResult, FinalData), dict:erase(Job, JobToPID)}; + ordsets:union(OldResult, FinalData); 'compile' -> - {dialyzer_analysis_callgraph:add_to_result(Job, NewData, OldResult), - JobToPID}; + dialyzer_analysis_callgraph:add_to_result(Job, NewData, OldResult); 'warnings' -> - {NewData ++ OldResult, dict:erase(Job, JobToPID)} + NewData ++ OldResult end, - UpdatedState = State#state{result = NewResult, job_to_pid = NewJobToPID}, + UpdatedState = State#state{result = NewResult}, Action = case AllSpawned of false -> reduce; @@ -262,21 +261,17 @@ handle_cast(all_spawned, #state{spawn_count = SpawnCount} = State) -> NewState = State#state{all_spawned = true}, {noreply, NewState} end; -handle_cast({sccs_to_pids, SCCs, Worker}, - #state{job_to_pid = SCCtoPID} = State) -> - scc_to_pids_request_handle(Worker, SCCs, SCCtoPID), - {noreply, State}; handle_cast({scc_spawn, SCC}, #state{mode = Mode, init_job_data = Servers, - spawn_count = SpawnCount, - job_to_pid = SCCtoPID - } = State) -> + spawn_count = SpawnCount} = State) -> Pid = dialyzer_worker:launch(Mode, SCC, Servers, self()), - {noreply, - State#state{spawn_count = SpawnCount + 1, - job_to_pid = store_map(SCC, Pid, SCCtoPID)} - }; + case Mode of + X when X =:= 'typesig'; X =:= 'dataflow' -> + true = ets:insert(?MAP, {SCC, Pid}); + _ -> true + end, + {noreply, State#state{spawn_count = SpawnCount + 1}}; handle_cast({compiler_spawn, Filename}, #state{mode = Mode, init_job_data = Servers, @@ -294,7 +289,11 @@ handle_info(_Info, State) -> -spec terminate(term(), #state{}) -> ok. -terminate(_Reason, _State) -> +terminate(_Reason, #state{mode = Mode}) -> + case Mode of + X when X =:= 'typesig'; X =:= 'dataflow' -> ets:delete(?MAP); + _ -> true + end, ok. -spec code_change(term(), #state{}, term()) -> {ok, #state{}}. @@ -309,19 +308,3 @@ cast(Message, Coordinator) -> call(Message, Coordinator) -> gen_server:call(Coordinator, Message). - -new_map() -> - dict:new(). - -store_map(Key, Value, Map) -> - dict:store(Key, Value, Map). - -map_lookup(SCCs, Map) -> - Fold = - fun(Key, {Mapped, Unknown}) -> - case dict:find(Key, Map) of - {ok, Value} -> {[Value|Mapped], Unknown}; - error -> {Mapped, [Key|Unknown]} - end - end, - lists:foldl(Fold, {[], []}, SCCs). diff --git a/lib/dialyzer/src/dialyzer_worker.erl b/lib/dialyzer/src/dialyzer_worker.erl index 74e227f587..37ca6854b1 100644 --- a/lib/dialyzer/src/dialyzer_worker.erl +++ b/lib/dialyzer/src/dialyzer_worker.erl @@ -101,7 +101,6 @@ loop(running, #state{mode = 'warnings'} = State) -> loop(running, #state{mode = Mode} = State) when Mode =:= 'typesig'; Mode =:= 'dataflow' -> ?debug("Run: ~p\n",[State#state.job]), - ok = ask_coordinator_for_callers(State), NotFixpoint = do_work(State), ok = broadcast_done(State), report_to_coordinator(NotFixpoint, State). @@ -112,29 +111,25 @@ waits_more_success_typings(#state{depends_on = Depends}) -> _ -> true end. -ask_coordinator_for_callers(#state{job = SCC, - servers = Servers, - coordinator = Coordinator}) -> +broadcast_done(#state{job = SCC, servers = Servers}) -> RequiredBy = dialyzer_succ_typings:find_required_by(SCC, Servers), - ?debug("Waiting for me~p: ~p\n",[SCC, RequiredBy]), - dialyzer_coordinator:sccs_to_pids_request(RequiredBy, Coordinator). - -broadcast_done(#state{job = SCC, coordinator = Coordinator}) -> - {Callers, Unknown} = dialyzer_coordinator:sccs_to_pids_reply(), + {Callers, Unknown} = dialyzer_coordinator:sccs_to_pids(RequiredBy), send_done(Callers, SCC), - continue_broadcast_done(Unknown, SCC, Coordinator). + continue_broadcast_done(Unknown, SCC). send_done(Callers, SCC) -> ?debug("Sending ~p: ~p\n",[SCC, Callers]), SendSTFun = fun(PID) -> PID ! {done, SCC} end, lists:foreach(SendSTFun, Callers). -continue_broadcast_done([], _SCC, _Coordinator) -> ok; -continue_broadcast_done(Rest, SCC, Coordinator) -> - dialyzer_coordinator:sccs_to_pids_request(Rest, Coordinator), - {Callers, Unknown} = dialyzer_coordinator:sccs_to_pids_reply(), +continue_broadcast_done([], _SCC) -> ok; +continue_broadcast_done(Rest, SCC) -> + %% This time limit should be greater than the time required + %% by the coordinator to spawn all processes. + timer:sleep(500), + {Callers, Unknown} = dialyzer_coordinator:sccs_to_pids(Rest), send_done(Callers, SCC), - continue_broadcast_done(Unknown, SCC, Coordinator). + continue_broadcast_done(Unknown, SCC). wait_for_success_typings(#state{depends_on = DependsOn} = State) -> receive -- cgit v1.2.3 From 8f56878ce7b2d9a4f2118663838287c6d2fa9502 Mon Sep 17 00:00:00 2001 From: Stavros Aronis Date: Tue, 28 Feb 2012 11:04:46 +0100 Subject: Infinity timeout for coordinator calls --- lib/dialyzer/src/dialyzer_coordinator.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib/dialyzer') diff --git a/lib/dialyzer/src/dialyzer_coordinator.erl b/lib/dialyzer/src/dialyzer_coordinator.erl index af85dfe82d..fa0b5dcd9d 100644 --- a/lib/dialyzer/src/dialyzer_coordinator.erl +++ b/lib/dialyzer/src/dialyzer_coordinator.erl @@ -307,4 +307,4 @@ cast(Message, Coordinator) -> gen_server:cast(Coordinator, Message). call(Message, Coordinator) -> - gen_server:call(Coordinator, Message). + gen_server:call(Coordinator, Message, infinity). -- cgit v1.2.3 From 0ecc1f181062da8b019f226ae2c567078ee2e860 Mon Sep 17 00:00:00 2001 From: Stavros Aronis Date: Fri, 24 Feb 2012 18:31:31 +0100 Subject: Ticket-based regulation of memory consumption --- lib/dialyzer/src/dialyzer_coordinator.erl | 113 +++++++++++++++++++++++------- lib/dialyzer/src/dialyzer_worker.erl | 7 ++ 2 files changed, 96 insertions(+), 24 deletions(-) (limited to 'lib/dialyzer') diff --git a/lib/dialyzer/src/dialyzer_coordinator.erl b/lib/dialyzer/src/dialyzer_coordinator.erl index fa0b5dcd9d..91a0dd662d 100644 --- a/lib/dialyzer/src/dialyzer_coordinator.erl +++ b/lib/dialyzer/src/dialyzer_coordinator.erl @@ -47,11 +47,10 @@ -module(dialyzer_coordinator). %%% Exports for all possible uses of coordinator from main process --export([start/2, - all_spawned/1]). +-export([start/2, all_spawned/1]). %%% Exports for all possible workers --export([job_done/3]). +-export([wait_activation/0, job_done/3]). %%% Export for the typesig, dataflow and warnings main process -export([scc_spawn/2]). @@ -63,15 +62,13 @@ -export([receive_warnings/0]). %%% Exports for the typesig and dataflow analysis workers --export([sccs_to_pids/1]). +-export([request_activation/1, sccs_to_pids/1]). %%% Exports for the compilation main process --export([compiler_spawn/2, - receive_compilation_data/0]). +-export([compiler_spawn/2, receive_compilation_data/0]). %%% Exports for the compilation workers --export([compilation_done/3, - get_next_label/2]). +-export([get_next_label/2, compilation_done/3]). -export_type([coordinator/0, mode/0]). @@ -100,7 +97,9 @@ result :: [mfa_or_funlbl()] | dialyzer_analysis_callgraph:result() | [dial_warning()], - init_job_data :: servers() + init_job_data :: servers(), + tickets :: integer(), + queue :: queue() }). -include("dialyzer.hrl"). @@ -188,12 +187,35 @@ compiler_spawn(Filename, Coordinator) -> get_next_label(EstimatedSize, Coordinator) -> call({get_next_label, EstimatedSize}, Coordinator). +-spec request_activation(coordinator()) -> ok. + +request_activation(Coordinator) -> + cast({request_activation, self()}, Coordinator). + +-spec wait_activation() -> ok. + +wait_activation() -> + receive activate -> ok end. + +activate_pid(Pid) -> + Pid ! activate. + %%-------------------------------------------------------------------- -spec init({pid(), mode(), servers()}) -> {ok, #state{}}. init({Parent, Mode, InitJobData}) -> - InitState = #state{parent = Parent, mode = Mode, init_job_data = InitJobData}, + BaseTickets = erlang:system_info(logical_processors_available), + Tickets = + case Mode of + 'compile' -> 4*BaseTickets; + 'typesig' -> BaseTickets*BaseTickets; + 'dataflow' -> 4*BaseTickets; + 'warnings' -> 4*BaseTickets + end, + InitState = + #state{parent = Parent, mode = Mode, init_job_data = InitJobData, + tickets = Tickets, queue = queue:new()}, State = case Mode of X when X =:= 'typesig'; X =:= 'dataflow' -> @@ -222,8 +244,18 @@ handle_cast({done, Job, NewData}, spawn_count = SpawnCount, all_spawned = AllSpawned, result = OldResult, - init_job_data = Servers + init_job_data = Servers, + tickets = Tickets, + queue = Queue } = State) -> + {Waiting, NewQueue} = queue:out(Queue), + NewTickets = + case Waiting of + empty -> Tickets+1; + {value, Pid} -> + activate_pid(Pid), + Tickets + end, NewResult = case Mode of X when X =:= 'typesig'; X =:= 'dataflow' -> @@ -234,7 +266,8 @@ handle_cast({done, Job, NewData}, 'warnings' -> NewData ++ OldResult end, - UpdatedState = State#state{result = NewResult}, + UpdatedState = + State#state{result = NewResult, tickets = NewTickets, queue = NewQueue}, Action = case AllSpawned of false -> reduce; @@ -261,26 +294,58 @@ handle_cast(all_spawned, #state{spawn_count = SpawnCount} = State) -> NewState = State#state{all_spawned = true}, {noreply, NewState} end; +handle_cast({request_activation, Pid}, + #state{tickets = Tickets, queue = Queue} = State) -> + {NewTickets, NewQueue} = + case Tickets of + 0 -> {Tickets, queue:in(Pid, Queue)}; + N -> + activate_pid(Pid), + {N-1, Queue} + end, + {noreply, State#state{tickets = NewTickets, queue = NewQueue}}; handle_cast({scc_spawn, SCC}, #state{mode = Mode, init_job_data = Servers, - spawn_count = SpawnCount} = State) -> + spawn_count = SpawnCount, + tickets = Tickets, + queue = Queue} = State) -> Pid = dialyzer_worker:launch(Mode, SCC, Servers, self()), - case Mode of - X when X =:= 'typesig'; X =:= 'dataflow' -> - true = ets:insert(?MAP, {SCC, Pid}); - _ -> true - end, - {noreply, State#state{spawn_count = SpawnCount + 1}}; + {NewTickets, NewQueue} = + case Mode of + X when X =:= 'typesig'; X =:= 'dataflow' -> + true = ets:insert(?MAP, {SCC, Pid}), + {Tickets, Queue}; + warnings -> + case Tickets of + 0 -> {Tickets, queue:in(Pid, Queue)}; + N -> + activate_pid(Pid), + {N-1, Queue} + end + end, + {noreply, State#state{spawn_count = SpawnCount + 1, + tickets = NewTickets, + queue = NewQueue}}; handle_cast({compiler_spawn, Filename}, #state{mode = Mode, init_job_data = Servers, - spawn_count = SpawnCount + spawn_count = SpawnCount, + tickets = Tickets, + queue = Queue } = State) -> - dialyzer_worker:launch(Mode, Filename, Servers, self()), - {noreply, - State#state{spawn_count = SpawnCount + 1} - }. + Pid = dialyzer_worker:launch(Mode, Filename, Servers, self()), + NewSpawnCount = SpawnCount + 1, + {NewTickets, NewQueue} = + case Tickets of + 0 -> {Tickets, queue:in(Pid, Queue)}; + N -> + activate_pid(Pid), + {N-1, Queue} + end, + {noreply, State#state{spawn_count = NewSpawnCount, + tickets = NewTickets, + queue = NewQueue}}. -spec handle_info(term(), #state{}) -> {noreply, #state{}}. diff --git a/lib/dialyzer/src/dialyzer_worker.erl b/lib/dialyzer/src/dialyzer_worker.erl index 37ca6854b1..6392854101 100644 --- a/lib/dialyzer/src/dialyzer_worker.erl +++ b/lib/dialyzer/src/dialyzer_worker.erl @@ -84,6 +84,7 @@ loop(waiting, State) -> NewState = wait_for_success_typings(State), loop(updating, NewState); loop(running, #state{mode = 'compile'} = State) -> + dialyzer_coordinator:wait_activation(), ?debug("Compile: ~s\n",[State#state.job]), Result = case start_compilation(State) of @@ -95,16 +96,22 @@ loop(running, #state{mode = 'compile'} = State) -> end, report_to_coordinator(Result, State); loop(running, #state{mode = 'warnings'} = State) -> + dialyzer_coordinator:wait_activation(), ?debug("Warning: ~s\n",[State#state.job]), Result = collect_warnings(State), report_to_coordinator(Result, State); loop(running, #state{mode = Mode} = State) when Mode =:= 'typesig'; Mode =:= 'dataflow' -> + request_activation(State), + dialyzer_coordinator:wait_activation(), ?debug("Run: ~p\n",[State#state.job]), NotFixpoint = do_work(State), ok = broadcast_done(State), report_to_coordinator(NotFixpoint, State). +request_activation(#state{coordinator = Coordinator}) -> + dialyzer_coordinator:request_activation(Coordinator). + waits_more_success_typings(#state{depends_on = Depends}) -> case Depends of [] -> false; -- cgit v1.2.3 From e21f6ea9d28b0e8ed9609338499daaab306fa439 Mon Sep 17 00:00:00 2001 From: Stavros Aronis Date: Mon, 27 Feb 2012 21:41:01 +0100 Subject: Plain concatenation for typesig not-fixpoint list --- lib/dialyzer/src/dialyzer_coordinator.erl | 2 +- lib/dialyzer/src/dialyzer_succ_typings.erl | 7 +++---- 2 files changed, 4 insertions(+), 5 deletions(-) (limited to 'lib/dialyzer') diff --git a/lib/dialyzer/src/dialyzer_coordinator.erl b/lib/dialyzer/src/dialyzer_coordinator.erl index 91a0dd662d..24bc38616c 100644 --- a/lib/dialyzer/src/dialyzer_coordinator.erl +++ b/lib/dialyzer/src/dialyzer_coordinator.erl @@ -260,7 +260,7 @@ handle_cast({done, Job, NewData}, case Mode of X when X =:= 'typesig'; X =:= 'dataflow' -> FinalData = dialyzer_succ_typings:lookup_names(NewData, Servers), - ordsets:union(OldResult, FinalData); + FinalData ++ OldResult; 'compile' -> dialyzer_analysis_callgraph:add_to_result(Job, NewData, OldResult); 'warnings' -> diff --git a/lib/dialyzer/src/dialyzer_succ_typings.erl b/lib/dialyzer/src/dialyzer_succ_typings.erl index 2856a26169..ff4102f5a3 100644 --- a/lib/dialyzer/src/dialyzer_succ_typings.erl +++ b/lib/dialyzer/src/dialyzer_succ_typings.erl @@ -258,12 +258,11 @@ refine_one_module(M, {CodeServer, Callgraph, Plt}) -> NewFunTypes = dialyzer_dataflow:get_fun_types(ModCode, Plt, Callgraph, Records), case reached_fixpoint(FunTypes, NewFunTypes) of - true -> - ordsets:new(); + true -> []; {false, NotFixpoint} -> ?debug("Not fixpoint\n", []), Plt = insert_into_plt(dict:from_list(NotFixpoint), Callgraph, Plt), - ordsets:from_list([FunLbl || {FunLbl,_Type} <- NotFixpoint]) + [FunLbl || {FunLbl,_Type} <- NotFixpoint] end. reached_fixpoint(OldTypes, NewTypes) -> @@ -382,7 +381,7 @@ find_succ_types_for_scc(SCC, {Codeserver, Callgraph, Plt}) -> true -> []; false -> ?debug("Not fixpoint for: ~w\n", [AllFuns]), - ordsets:from_list([Fun || {Fun, _Arity} <- AllFuns]) + [Fun || {Fun, _Arity} <- AllFuns] end. get_fun_types_from_plt(FunList, Callgraph, Plt) -> -- cgit v1.2.3 From 6d0da21ad9f0bba67d5bd389ca76a67613884d5f Mon Sep 17 00:00:00 2001 From: Stavros Aronis Date: Mon, 27 Feb 2012 21:41:34 +0100 Subject: Typesig and dataflow analyses no longer use ticket regulation --- lib/dialyzer/src/dialyzer_coordinator.erl | 86 +++++++++++++++++-------------- lib/dialyzer/src/dialyzer_worker.erl | 5 -- 2 files changed, 46 insertions(+), 45 deletions(-) (limited to 'lib/dialyzer') diff --git a/lib/dialyzer/src/dialyzer_coordinator.erl b/lib/dialyzer/src/dialyzer_coordinator.erl index 24bc38616c..3663b3c737 100644 --- a/lib/dialyzer/src/dialyzer_coordinator.erl +++ b/lib/dialyzer/src/dialyzer_coordinator.erl @@ -209,9 +209,8 @@ init({Parent, Mode, InitJobData}) -> Tickets = case Mode of 'compile' -> 4*BaseTickets; - 'typesig' -> BaseTickets*BaseTickets; - 'dataflow' -> 4*BaseTickets; - 'warnings' -> 4*BaseTickets + 'warnings' -> 4*BaseTickets; + _ -> non_regulated end, InitState = #state{parent = Parent, mode = Mode, init_job_data = InitJobData, @@ -239,15 +238,19 @@ handle_call({get_next_label, EstimatedSize}, _From, -spec handle_cast(Msg::term(), #state{}) -> {noreply, #state{}} | {stop, normal, #state{}}. +handle_cast({done, _Job, NewData}, + #state{mode = Mode, result = OldResult, + init_job_data = Servers} = State) when + Mode =:= 'typesig'; Mode =:= 'dataflow' -> + FinalData = dialyzer_succ_typings:lookup_names(NewData, Servers), + UpdatedState = State#state{result = FinalData ++ OldResult}, + reduce_or_stop(UpdatedState); handle_cast({done, Job, NewData}, #state{mode = Mode, - spawn_count = SpawnCount, - all_spawned = AllSpawned, result = OldResult, - init_job_data = Servers, tickets = Tickets, - queue = Queue - } = State) -> + queue = Queue} = State) when + Mode =:= 'compile'; Mode =:= 'warnings' -> {Waiting, NewQueue} = queue:out(Queue), NewTickets = case Waiting of @@ -258,9 +261,6 @@ handle_cast({done, Job, NewData}, end, NewResult = case Mode of - X when X =:= 'typesig'; X =:= 'dataflow' -> - FinalData = dialyzer_succ_typings:lookup_names(NewData, Servers), - FinalData ++ OldResult; 'compile' -> dialyzer_analysis_callgraph:add_to_result(Job, NewData, OldResult); 'warnings' -> @@ -268,23 +268,7 @@ handle_cast({done, Job, NewData}, end, UpdatedState = State#state{result = NewResult, tickets = NewTickets, queue = NewQueue}, - Action = - case AllSpawned of - false -> reduce; - true -> - case SpawnCount of - 1 -> finish; - _ -> reduce - end - end, - case Action of - reduce -> - NewState = UpdatedState#state{spawn_count = SpawnCount - 1}, - {noreply, NewState}; - finish -> - send_done_to_parent(UpdatedState), - {stop, normal, State} - end; + reduce_or_stop(UpdatedState); handle_cast(all_spawned, #state{spawn_count = SpawnCount} = State) -> case SpawnCount of 0 -> @@ -306,23 +290,25 @@ handle_cast({request_activation, Pid}, {noreply, State#state{tickets = NewTickets, queue = NewQueue}}; handle_cast({scc_spawn, SCC}, #state{mode = Mode, + init_job_data = Servers, + spawn_count = SpawnCount} = State) when + Mode =:= 'typesig'; Mode =:= 'dataflow' -> + Pid = dialyzer_worker:launch(Mode, SCC, Servers, self()), + true = ets:insert(?MAP, {SCC, Pid}), + {noreply, State#state{spawn_count = SpawnCount + 1}}; +handle_cast({scc_spawn, SCC}, + #state{mode = 'warnings', init_job_data = Servers, spawn_count = SpawnCount, tickets = Tickets, queue = Queue} = State) -> - Pid = dialyzer_worker:launch(Mode, SCC, Servers, self()), + Pid = dialyzer_worker:launch('warnings', SCC, Servers, self()), {NewTickets, NewQueue} = - case Mode of - X when X =:= 'typesig'; X =:= 'dataflow' -> - true = ets:insert(?MAP, {SCC, Pid}), - {Tickets, Queue}; - warnings -> - case Tickets of - 0 -> {Tickets, queue:in(Pid, Queue)}; - N -> - activate_pid(Pid), - {N-1, Queue} - end + case Tickets of + 0 -> {Tickets, queue:in(Pid, Queue)}; + N -> + activate_pid(Pid), + {N-1, Queue} end, {noreply, State#state{spawn_count = SpawnCount + 1, tickets = NewTickets, @@ -373,3 +359,23 @@ cast(Message, Coordinator) -> call(Message, Coordinator) -> gen_server:call(Coordinator, Message, infinity). + +reduce_or_stop(#state{all_spawned = AllSpawned, + spawn_count = SpawnCount} = State) -> + Action = + case AllSpawned of + false -> reduce; + true -> + case SpawnCount of + 1 -> finish; + _ -> reduce + end + end, + case Action of + reduce -> + NewState = State#state{spawn_count = SpawnCount - 1}, + {noreply, NewState}; + finish -> + send_done_to_parent(State), + {stop, normal, State} + end. diff --git a/lib/dialyzer/src/dialyzer_worker.erl b/lib/dialyzer/src/dialyzer_worker.erl index 6392854101..4e967a18df 100644 --- a/lib/dialyzer/src/dialyzer_worker.erl +++ b/lib/dialyzer/src/dialyzer_worker.erl @@ -102,16 +102,11 @@ loop(running, #state{mode = 'warnings'} = State) -> report_to_coordinator(Result, State); loop(running, #state{mode = Mode} = State) when Mode =:= 'typesig'; Mode =:= 'dataflow' -> - request_activation(State), - dialyzer_coordinator:wait_activation(), ?debug("Run: ~p\n",[State#state.job]), NotFixpoint = do_work(State), ok = broadcast_done(State), report_to_coordinator(NotFixpoint, State). -request_activation(#state{coordinator = Coordinator}) -> - dialyzer_coordinator:request_activation(Coordinator). - waits_more_success_typings(#state{depends_on = Depends}) -> case Depends of [] -> false; -- cgit v1.2.3 From d0419ddb2e43b1ed957108712e9bf9505d7a0c01 Mon Sep 17 00:00:00 2001 From: Stavros Aronis Date: Tue, 28 Feb 2012 15:29:04 +0100 Subject: Fix race in coordinator --- lib/dialyzer/src/dialyzer_coordinator.erl | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) (limited to 'lib/dialyzer') diff --git a/lib/dialyzer/src/dialyzer_coordinator.erl b/lib/dialyzer/src/dialyzer_coordinator.erl index 3663b3c737..f4f1fda8fd 100644 --- a/lib/dialyzer/src/dialyzer_coordinator.erl +++ b/lib/dialyzer/src/dialyzer_coordinator.erl @@ -152,7 +152,9 @@ send_done_to_parent(#state{mode = Mode, next_label = NextLabel}) -> Msg = case Mode of - X when X =:= 'typesig'; X =:= 'dataflow' -> {not_fixpoint, Result}; + X when X =:= 'typesig'; X =:= 'dataflow' -> + ets:delete(?MAP), + {not_fixpoint, Result}; 'compile' -> {compilation_data, Result, NextLabel}; 'warnings' -> {warnings, Result} end, @@ -340,11 +342,7 @@ handle_info(_Info, State) -> -spec terminate(term(), #state{}) -> ok. -terminate(_Reason, #state{mode = Mode}) -> - case Mode of - X when X =:= 'typesig'; X =:= 'dataflow' -> ets:delete(?MAP); - _ -> true - end, +terminate(_Reason, _State) -> ok. -spec code_change(term(), #state{}, term()) -> {ok, #state{}}. -- cgit v1.2.3 From 913ee73601e3f7d0b27d833bd67cf6ee3868018a Mon Sep 17 00:00:00 2001 From: Stavros Aronis Date: Wed, 22 Feb 2012 15:31:16 +0100 Subject: All spawns are now spawn_links --- lib/dialyzer/src/dialyzer_callgraph.erl | 4 ++-- lib/dialyzer/src/dialyzer_coordinator.erl | 2 +- lib/dialyzer/src/dialyzer_timing.erl | 2 +- lib/dialyzer/src/dialyzer_worker.erl | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) (limited to 'lib/dialyzer') diff --git a/lib/dialyzer/src/dialyzer_callgraph.erl b/lib/dialyzer/src/dialyzer_callgraph.erl index 80f2c48676..0384160abc 100644 --- a/lib/dialyzer/src/dialyzer_callgraph.erl +++ b/lib/dialyzer/src/dialyzer_callgraph.erl @@ -724,7 +724,7 @@ put_behaviour_api_calls(Calls, new_race_data_server() -> - spawn(fun() -> race_data_server_loop(#race_data_state{}) end). + spawn_link(fun() -> race_data_server_loop(#race_data_state{}) end). race_data_server_loop(State) -> receive @@ -770,7 +770,7 @@ race_data_server_handle_call(Query, beh_api_calls = BehApiCalls} = State) -> case Query of - dup -> spawn(fun() -> race_data_server_loop(State) end); + dup -> spawn_link(fun() -> race_data_server_loop(State) end); get_race_code -> RaceCode; get_public_tables -> PublicTables; get_named_tables -> NamedTables; diff --git a/lib/dialyzer/src/dialyzer_coordinator.erl b/lib/dialyzer/src/dialyzer_coordinator.erl index f4f1fda8fd..c417b45717 100644 --- a/lib/dialyzer/src/dialyzer_coordinator.erl +++ b/lib/dialyzer/src/dialyzer_coordinator.erl @@ -109,7 +109,7 @@ -spec start(mode(), servers()) -> coordinator(). start(Mode, Servers) -> - {ok, Pid} = gen_server:start(?MODULE, {self(), Mode, Servers}, []), + {ok, Pid} = gen_server:start_link(?MODULE, {self(), Mode, Servers}, []), Pid. -spec scc_spawn(scc() | module(), coordinator()) -> ok. diff --git a/lib/dialyzer/src/dialyzer_timing.erl b/lib/dialyzer/src/dialyzer_timing.erl index 368c1715ce..dc4d522c2f 100644 --- a/lib/dialyzer/src/dialyzer_timing.erl +++ b/lib/dialyzer/src/dialyzer_timing.erl @@ -31,7 +31,7 @@ -spec init(boolean()) -> ok. init(Active) -> - Pid = spawn(fun() -> loop_init(Active) end), + Pid = spawn_link(fun() -> loop_init(Active) end), case whereis(?MODULE) of undefined -> ok; _ -> unregister(?MODULE) diff --git a/lib/dialyzer/src/dialyzer_worker.erl b/lib/dialyzer/src/dialyzer_worker.erl index 4e967a18df..0ef30cf940 100644 --- a/lib/dialyzer/src/dialyzer_worker.erl +++ b/lib/dialyzer/src/dialyzer_worker.erl @@ -63,7 +63,7 @@ launch(Mode, Job, Servers, Coordinator) -> X when X =:= 'typesig'; X =:= 'dataflow' -> initializing; X when X =:= 'compile'; X =:= 'warnings' -> running end, - spawn(fun() -> loop(InitState, State) end). + spawn_link(fun() -> loop(InitState, State) end). %%-------------------------------------------------------------------- -- cgit v1.2.3 From 1630eaa34f2cb8d6fceefbb752cfe94ac8e44b6a Mon Sep 17 00:00:00 2001 From: Stavros Aronis Date: Tue, 28 Feb 2012 22:49:04 +0100 Subject: Coordinator is no longer a separate process --- lib/dialyzer/src/dialyzer_analysis_callgraph.erl | 91 +++-- lib/dialyzer/src/dialyzer_coordinator.erl | 459 ++++++++--------------- lib/dialyzer/src/dialyzer_succ_typings.erl | 76 ++-- lib/dialyzer/src/dialyzer_worker.erl | 33 +- 4 files changed, 258 insertions(+), 401 deletions(-) (limited to 'lib/dialyzer') diff --git a/lib/dialyzer/src/dialyzer_analysis_callgraph.erl b/lib/dialyzer/src/dialyzer_analysis_callgraph.erl index 02108d6e38..e2e65d2e25 100644 --- a/lib/dialyzer/src/dialyzer_analysis_callgraph.erl +++ b/lib/dialyzer/src/dialyzer_analysis_callgraph.erl @@ -30,14 +30,14 @@ -export([start/3]). --export([compile_coordinator_init/0, - add_to_result/3, +-export([compile_init_result/0, + add_to_result/4, start_compilation/2, continue_compilation/2]). --export_type([compilation_data/0, - result/0, - servers/0]). +-export_type([compile_mid_data/0, + compile_result/0, + compile_init_data/0]). -include("dialyzer.hrl"). @@ -210,28 +210,37 @@ analyze_callgraph(Callgraph, State) -> %% Build the callgraph and fill the codeserver. %%-------------------------------------------------------------------- +-record(compile_init,{ + callgraph :: dialyzer_callgraph:callgraph(), + codeserver :: dialyzer_codeserver:codeserver(), + defines = [] :: [dial_define()], + include_dirs = [] :: [file:filename()], + start_from = byte_code :: start_from(), + use_contracts = true :: boolean() + }). + +make_compile_init(#analysis_state{codeserver = Codeserver, + defines = Defs, + include_dirs = Dirs, + use_contracts = UseContracts, + start_from = StartFrom}, Callgraph) -> + #compile_init{callgraph = Callgraph, + codeserver = Codeserver, + defines = [{d, Macro, Val} || {Macro, Val} <- Defs], + include_dirs = [{i, D} || D <- Dirs], + use_contracts = UseContracts, + start_from = StartFrom}. + compile_and_store(Files, #analysis_state{codeserver = CServer, - defines = Defs, - include_dirs = Dirs, - parent = Parent, - use_contracts = UseContracts, - start_from = StartFrom - } = State) -> + parent = Parent} = State) -> send_log(Parent, "Reading files and computing callgraph... "), {T1, _} = statistics(runtime), - Includes = [{i, D} || D <- Dirs], - Defines = [{d, Macro, Val} || {Macro, Val} <- Defs], Callgraph = dialyzer_callgraph:new(), - Servers = {Callgraph, CServer, StartFrom, Includes, Defines, UseContracts}, - Coordinator = dialyzer_coordinator:start(compile, Servers), - Spawner = fun(F) -> dialyzer_coordinator:compiler_spawn(F, Coordinator) end, - lists:foreach(Spawner, Files), - dialyzer_coordinator:all_spawned(Coordinator), - {{V, E, Failed, NoWarn, Modules}, NextLabel} = - ?timing("compile", _C1, dialyzer_coordinator:receive_compilation_data()), + CompileInit = make_compile_init(State, Callgraph), + {{Failed, NoWarn, Modules}, NextLabel} = + ?timing("compile", + dialyzer_coordinator:parallel_job(compile, Files, CompileInit)), CServer2 = dialyzer_codeserver:set_next_core_label(NextLabel, CServer), - Callgraph = - ?timing("digraph", _C2, dialyzer_callgraph:add_edges(E, V, Callgraph)), case Failed =:= [] of true -> NewFiles = lists:zip(lists:reverse(Modules), Files), @@ -255,33 +264,39 @@ compile_and_store(Files, #analysis_state{codeserver = CServer, send_log(Parent, Msg2), {Callgraph, sets:from_list(NoWarn), CServer2}. --type servers() :: term(). %%opaque --type result() :: term(). %%opaque --type file_result() :: term(). %%opaque --type compilation_data() :: term(). %%opaque +-type compile_init_data() :: #compile_init{}. +-type compile_result() :: {list(), list(), list()}. %%opaque +-type one_file_result() :: term(). %%opaque +-type compile_mid_data() :: term(). %%opaque --spec compile_coordinator_init() -> result(). +-spec compile_init_result() -> compile_result(). -compile_coordinator_init() -> {[], [], [], [], []}. +compile_init_result() -> {[], [], []}. --spec add_to_result(file:filename(), file_result(), result()) -> result(). +-spec add_to_result(file:filename(), one_file_result(), compile_result(), + compile_init_data()) -> compile_result(). -add_to_result(File, NewData, {V, E, Failed, NoWarn, Mods}) -> +add_to_result(File, NewData, {Failed, NoWarn, Mods}, InitData) -> case NewData of {error, Reason} -> {[{File, Reason}|Failed], NoWarn, Mods}; - {ok, NV, NE, NewNoWarn, Mod} -> - {NV ++ V, NE ++ E, Failed, NewNoWarn ++ NoWarn, [Mod|Mods]} + {ok, V, E, NewNoWarn, Mod} -> + Callgraph = InitData#compile_init.callgraph, + dialyzer_callgraph:add_edges(E, V, Callgraph), + {Failed, NewNoWarn ++ NoWarn, [Mod|Mods]} end. --spec start_compilation(file:filename(), servers()) -> - {error, term()} |{ok, integer(), compilation_data()}. +-spec start_compilation(file:filename(), compile_init_data()) -> + {error, term()} |{ok, integer(), compile_mid_data()}. -start_compilation(File, {Callgraph, Codeserver, StartFrom, - Includes, Defines, UseContracts}) -> +start_compilation(File, + #compile_init{callgraph = Callgraph, codeserver = Codeserver, + defines = Defines, include_dirs = IncludeD, + use_contracts = UseContracts, + start_from = StartFrom}) -> case StartFrom of src_code -> - compile_src(File, Includes, Defines, Callgraph, Codeserver, UseContracts); + compile_src(File, IncludeD, Defines, Callgraph, Codeserver, UseContracts); byte_code -> compile_byte(File, Callgraph, Codeserver, UseContracts) end. @@ -379,7 +394,7 @@ store_core(Mod, Core, NoWarn, Callgraph, CServer) -> CoreTree = cerl:from_records(Core), {ok, cerl_trees:size(CoreTree), {Mod, CoreTree, NoWarn, Callgraph, CServer}}. --spec continue_compilation(integer(), compilation_data()) -> file_result(). +-spec continue_compilation(integer(), compile_mid_data()) -> one_file_result(). continue_compilation(NextLabel, {Mod, CoreTree, NoWarn, Callgraph, CServer}) -> {LabeledTree, _NewNextLabel} = cerl_trees:label(CoreTree, NextLabel), diff --git a/lib/dialyzer/src/dialyzer_coordinator.erl b/lib/dialyzer/src/dialyzer_coordinator.erl index c417b45717..490cfb8d49 100644 --- a/lib/dialyzer/src/dialyzer_coordinator.erl +++ b/lib/dialyzer/src/dialyzer_coordinator.erl @@ -21,61 +21,23 @@ %%%------------------------------------------------------------------- %%% File : dialyzer_coordinator.erl %%% Authors : Stavros Aronis -%%% -%%% Description: -%%% -%%% The parallel version of Dialyzer's typesig analysis is spread over 4 modules -%%% with the intention to both minimize the changes on the original code and use -%%% a separate module for every kind of Erlang process that will be running. -%%% -%%% There are therefore 3 kinds of processes: -%%% -%%% - The original Dialyzer backend (in succ_typings module) -%%% - The worker process for the typesig analysis (in typesig and -%%% worker) -%%% - A coordinator of the worker processes (in coordinator) -%%% -%%% Operation guidelines: -%%% -%%% - The backend requests from the coordinator to spawn a worker for each SCC -%%% - The backend notifies the coordinator when all SCC have been spawned and -%%% waits for the server to report that the PLT has been updated -%%% - Each worker is responsible to notify all those who wait for it. -%%% %%%------------------------------------------------------------------- -module(dialyzer_coordinator). -%%% Exports for all possible uses of coordinator from main process --export([start/2, all_spawned/1]). +%%% Export for dialyzer main process +-export([parallel_job/3]). %%% Exports for all possible workers -export([wait_activation/0, job_done/3]). -%%% Export for the typesig, dataflow and warnings main process --export([scc_spawn/2]). - -%%% Export for the typesig and dataflow analysis main process --export([receive_not_fixpoint/0]). - -%%% Export for warning main process --export([receive_warnings/0]). - %%% Exports for the typesig and dataflow analysis workers --export([request_activation/1, sccs_to_pids/1]). - -%%% Exports for the compilation main process --export([compiler_spawn/2, receive_compilation_data/0]). +-export([sccs_to_pids/1]). %%% Exports for the compilation workers -export([get_next_label/2, compilation_done/3]). --export_type([coordinator/0, mode/0]). - --behaviour(gen_server). - --export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, - code_change/3]). +-export_type([coordinator/0, mode/0, init_data/0]). %%-------------------------------------------------------------------- @@ -83,41 +45,169 @@ -type coordinator() :: pid(). %%opaque --type scc() :: [mfa_or_funlbl()] | module(). +-type scc() :: [mfa_or_funlbl()]. -type mode() :: 'typesig' | 'dataflow' | 'compile' | 'warnings'. --type servers() :: dialyzer_succ_typings:servers() | - dialyzer_analysis_callgraph:servers() | - dialyzer_succ_typings:warning_servers(). - --record(state, {parent :: pid(), - mode :: mode(), - spawn_count = 0 :: integer(), - all_spawned = false :: boolean(), - next_label :: integer(), - result :: [mfa_or_funlbl()] | - dialyzer_analysis_callgraph:result() | - [dial_warning()], - init_job_data :: servers(), - tickets :: integer(), - queue :: queue() + +-type compile_jobs() :: [file:filename()]. +-type typesig_jobs() :: [scc()]. +-type dataflow_jobs() :: [module()]. +-type warnings_jobs() :: [module()]. + +-type compile_init_data() :: dialyzer_analysis_callgraph:compile_init_data(). +-type typesig_init_data() :: dialyzer_succ_typings:typesig_init_data(). +-type dataflow_init_data() :: dialyzer_succ_typings:dataflow_init_data(). +-type warnings_init_data() :: dialyzer_succ_typings:warnings_init_data(). + +-type compile_result() :: dialyzer_analysis_callgraph:compile_result(). +-type typesig_result() :: [mfa_or_funlbl()]. +-type dataflow_result() :: [mfa_or_funlbl()]. +-type warnings_result() :: [dial_warning()]. + +-type init_data() :: compile_init_data() | typesig_init_data() | + dataflow_init_data() | warnings_init_data(). + +-type result() :: compile_result() | typesig_result() | + dataflow_result() | warnings_result(). + +-record(state, {active = 0 :: integer(), + result :: result(), + next_label = 0 :: integer(), + tickets = 0 :: integer(), + queue = queue:new() :: queue(), + init_data :: init_data() }). -include("dialyzer.hrl"). %%-------------------------------------------------------------------- --spec start(mode(), servers()) -> coordinator(). +-spec parallel_job('compile', compile_jobs(), compile_init_data()) -> + {compile_result(), integer()}; + ('typesig', typesig_jobs(), typesig_init_data()) -> + typesig_result(); + ('dataflow', dataflow_jobs(), dataflow_init_data()) -> + dataflow_result(); + ('warnings', warnings_jobs(), warnings_init_data()) -> + warnings_result(). -start(Mode, Servers) -> - {ok, Pid} = gen_server:start_link(?MODULE, {self(), Mode, Servers}, []), - Pid. +parallel_job(Mode, Jobs, InitData) -> + State = spawn_jobs(Mode, Jobs, InitData), + collect_result(Mode, State). --spec scc_spawn(scc() | module(), coordinator()) -> ok. +spawn_jobs(Mode, Jobs, InitData) when + Mode =:= 'typesig'; Mode =:= 'dataflow' -> + Coordinator = self(), + ?MAP = ets:new(?MAP, [named_table, {read_concurrency, true}]), + Fold = + fun(Job, Count) -> + Pid = dialyzer_worker:launch(Mode, Job, InitData, Coordinator), + true = ets:insert(?MAP, {Job, Pid}), + Count + 1 + end, + JobCount = lists:foldl(Fold, 0, Jobs), + #state{active = JobCount, result = [], init_data = InitData}; +spawn_jobs(Mode, Jobs, InitData) when + Mode =:= 'compile'; Mode =:= 'warnings' -> + Coordinator = self(), + Fold = + fun(Job, {InTickets, InQueue, Count}) -> + Pid = dialyzer_worker:launch(Mode, Job, InitData, Coordinator), + NewCount = Count + 1, + case InTickets of + 0 -> {InTickets, queue:in(Pid, InQueue), NewCount}; + N -> activate_pid(Pid), {N-1, InQueue, NewCount} + end + end, + CPUs = erlang:system_info(logical_processors_available), + InitTickets = 4*CPUs, + {Tickets, Queue, JobCount} = + lists:foldl(Fold, {InitTickets, queue:new(), 0}, Jobs), + InitResult = + case Mode of + 'warnings' -> []; + 'compile' -> dialyzer_analysis_callgraph:compile_init_result() + end, + #state{active = JobCount, result = InitResult, next_label = 0, + tickets = Tickets, queue = Queue, init_data = InitData}. + +collect_result(Mode, State) -> + case Mode of + 'compile' -> compile_loop(State); + 'typesig' -> not_fixpoint_loop(State); + 'dataflow' -> not_fixpoint_loop(State); + 'warnings' -> warnings_loop(State) + end. -scc_spawn(SCC, Coordinator) -> - cast({scc_spawn, SCC}, Coordinator). +compile_loop(#state{active = Active, result = Result, + next_label = NextLabel, tickets = Tickets, + queue = Queue, init_data = InitData} = State) -> + receive + {next_label_request, Estimation, Pid} -> + Pid ! {next_label_reply, NextLabel}, + compile_loop(State#state{next_label = NextLabel + Estimation}); + {done, Job, Data} -> + NewResult = + dialyzer_analysis_callgraph:add_to_result(Job, Data, Result, InitData), + case Active of + 1 -> + {NewResult, NextLabel}; + _ -> + NewActive = Active - 1, + {NewQueue, NewTickets} = manage_waiting(Queue, Tickets), + NewState = + State#state{result = NewResult, active = NewActive, + queue = NewQueue, tickets = NewTickets}, + compile_loop(NewState) + end + end. --spec sccs_to_pids([scc()]) -> {[dialyzer_worker:worker()], [scc()]}. +not_fixpoint_loop(#state{active = Active, result = Result, + init_data = InitData} = State) -> + receive + {done, _Job, Data} -> + FinalData = dialyzer_succ_typings:lookup_names(Data, InitData), + NewResult = FinalData ++ Result, + case Active of + 1 -> + ets:delete(?MAP), + NewResult; + _ -> + NewActive = Active - 1, + NewState = State#state{active = NewActive, result = NewResult}, + not_fixpoint_loop(NewState) + end + end. + +warnings_loop(#state{active = Active, result = Result, tickets = Tickets, + queue = Queue} = State) -> + receive + {done, _Job, Data} -> + NewResult = Data ++ Result, + case Active of + 1 -> NewResult; + _ -> + NewActive = Active - 1, + {NewQueue, NewTickets} = manage_waiting(Queue, Tickets), + NewState = + State#state{result = NewResult, active = NewActive, + queue = NewQueue, tickets = NewTickets}, + warnings_loop(NewState) + end + end. + +manage_waiting(Queue, Tickets) -> + {Waiting, NewQueue} = queue:out(Queue), + NewTickets = + case Waiting of + empty -> Tickets + 1; + {value, Pid} -> + activate_pid(Pid), + Tickets + end, + {NewQueue, NewTickets}. + +-spec sccs_to_pids([scc() | module()]) -> + {[dialyzer_worker:worker()], [scc() | module()]}. sccs_to_pids(SCCs) -> lists:foldl(fun pid_partition/2, {[], []}, SCCs). @@ -129,70 +219,27 @@ pid_partition(SCC, {Pids, Unknown}) -> _:_ -> {Pids, [SCC|Unknown]} end. --spec job_done(scc() | file:filename(), term(), coordinator()) -> ok. +-spec job_done(scc() | module() | file:filename(), term(), coordinator()) -> ok. job_done(Job, Result, Coordinator) -> - cast({done, Job, Result}, Coordinator). + Coordinator ! {done, Job, Result}, + ok. -spec compilation_done(file:filename(), - dialyzer_analysis_callgraph:compilation_data(), + dialyzer_analysis_callgraph:compile_result(), coordinator()) -> ok. compilation_done(Filename, CompilationData, Coordinator) -> - cast({done, Filename, CompilationData}, Coordinator). - --spec all_spawned(coordinator()) -> ok. - -all_spawned(Coordinator) -> - cast(all_spawned, Coordinator). - -send_done_to_parent(#state{mode = Mode, - parent = Parent, - result = Result, - next_label = NextLabel}) -> - Msg = - case Mode of - X when X =:= 'typesig'; X =:= 'dataflow' -> - ets:delete(?MAP), - {not_fixpoint, Result}; - 'compile' -> {compilation_data, Result, NextLabel}; - 'warnings' -> {warnings, Result} - end, - Parent ! Msg, + Coordinator ! {done, Filename, CompilationData}, ok. --spec receive_not_fixpoint() -> [mfa_or_funlbl()]. - -receive_not_fixpoint() -> - receive {not_fixpoint, NotFixpoint} -> NotFixpoint end. - --spec receive_compilation_data() -> - {dialyzer_analysis_callgraph:result(), integer()}. - -receive_compilation_data() -> - receive {compilation_data, CompilationData, NextLabel} -> - {CompilationData, NextLabel} - end. - --spec receive_warnings() -> [dial_warning()]. - -receive_warnings() -> - receive {warnings, Warnings} -> Warnings end. - --spec compiler_spawn(file:filename(), coordinator()) -> ok. - -compiler_spawn(Filename, Coordinator) -> - cast({compiler_spawn, Filename}, Coordinator). - -spec get_next_label(integer(), coordinator()) -> integer(). get_next_label(EstimatedSize, Coordinator) -> - call({get_next_label, EstimatedSize}, Coordinator). - --spec request_activation(coordinator()) -> ok. - -request_activation(Coordinator) -> - cast({request_activation, self()}, Coordinator). + Coordinator ! {next_label_request, EstimatedSize, self()}, + receive + {next_label_reply, NextLabel} -> NextLabel + end. -spec wait_activation() -> ok. @@ -201,179 +248,3 @@ wait_activation() -> activate_pid(Pid) -> Pid ! activate. - -%%-------------------------------------------------------------------- - --spec init({pid(), mode(), servers()}) -> {ok, #state{}}. - -init({Parent, Mode, InitJobData}) -> - BaseTickets = erlang:system_info(logical_processors_available), - Tickets = - case Mode of - 'compile' -> 4*BaseTickets; - 'warnings' -> 4*BaseTickets; - _ -> non_regulated - end, - InitState = - #state{parent = Parent, mode = Mode, init_job_data = InitJobData, - tickets = Tickets, queue = queue:new()}, - State = - case Mode of - X when X =:= 'typesig'; X =:= 'dataflow' -> - ?MAP = ets:new(?MAP, [named_table, {read_concurrency, true}]), - InitState#state{result = []}; - 'warnings' -> - InitState#state{result = []}; - 'compile' -> - InitResult = dialyzer_analysis_callgraph:compile_coordinator_init(), - InitState#state{result = InitResult, next_label = 0} - end, - {ok, State}. - --spec handle_call(Query::term(), From::term(), #state{}) -> - {reply, Reply::term(), #state{}}. - -handle_call({get_next_label, EstimatedSize}, _From, - #state{next_label = NextLabel} = State) -> - {reply, NextLabel, State#state{next_label = NextLabel + EstimatedSize}}. - --spec handle_cast(Msg::term(), #state{}) -> - {noreply, #state{}} | {stop, normal, #state{}}. - -handle_cast({done, _Job, NewData}, - #state{mode = Mode, result = OldResult, - init_job_data = Servers} = State) when - Mode =:= 'typesig'; Mode =:= 'dataflow' -> - FinalData = dialyzer_succ_typings:lookup_names(NewData, Servers), - UpdatedState = State#state{result = FinalData ++ OldResult}, - reduce_or_stop(UpdatedState); -handle_cast({done, Job, NewData}, - #state{mode = Mode, - result = OldResult, - tickets = Tickets, - queue = Queue} = State) when - Mode =:= 'compile'; Mode =:= 'warnings' -> - {Waiting, NewQueue} = queue:out(Queue), - NewTickets = - case Waiting of - empty -> Tickets+1; - {value, Pid} -> - activate_pid(Pid), - Tickets - end, - NewResult = - case Mode of - 'compile' -> - dialyzer_analysis_callgraph:add_to_result(Job, NewData, OldResult); - 'warnings' -> - NewData ++ OldResult - end, - UpdatedState = - State#state{result = NewResult, tickets = NewTickets, queue = NewQueue}, - reduce_or_stop(UpdatedState); -handle_cast(all_spawned, #state{spawn_count = SpawnCount} = State) -> - case SpawnCount of - 0 -> - send_done_to_parent(State), - {stop, normal, State}; - _ -> - NewState = State#state{all_spawned = true}, - {noreply, NewState} - end; -handle_cast({request_activation, Pid}, - #state{tickets = Tickets, queue = Queue} = State) -> - {NewTickets, NewQueue} = - case Tickets of - 0 -> {Tickets, queue:in(Pid, Queue)}; - N -> - activate_pid(Pid), - {N-1, Queue} - end, - {noreply, State#state{tickets = NewTickets, queue = NewQueue}}; -handle_cast({scc_spawn, SCC}, - #state{mode = Mode, - init_job_data = Servers, - spawn_count = SpawnCount} = State) when - Mode =:= 'typesig'; Mode =:= 'dataflow' -> - Pid = dialyzer_worker:launch(Mode, SCC, Servers, self()), - true = ets:insert(?MAP, {SCC, Pid}), - {noreply, State#state{spawn_count = SpawnCount + 1}}; -handle_cast({scc_spawn, SCC}, - #state{mode = 'warnings', - init_job_data = Servers, - spawn_count = SpawnCount, - tickets = Tickets, - queue = Queue} = State) -> - Pid = dialyzer_worker:launch('warnings', SCC, Servers, self()), - {NewTickets, NewQueue} = - case Tickets of - 0 -> {Tickets, queue:in(Pid, Queue)}; - N -> - activate_pid(Pid), - {N-1, Queue} - end, - {noreply, State#state{spawn_count = SpawnCount + 1, - tickets = NewTickets, - queue = NewQueue}}; -handle_cast({compiler_spawn, Filename}, - #state{mode = Mode, - init_job_data = Servers, - spawn_count = SpawnCount, - tickets = Tickets, - queue = Queue - } = State) -> - Pid = dialyzer_worker:launch(Mode, Filename, Servers, self()), - NewSpawnCount = SpawnCount + 1, - {NewTickets, NewQueue} = - case Tickets of - 0 -> {Tickets, queue:in(Pid, Queue)}; - N -> - activate_pid(Pid), - {N-1, Queue} - end, - {noreply, State#state{spawn_count = NewSpawnCount, - tickets = NewTickets, - queue = NewQueue}}. - --spec handle_info(term(), #state{}) -> {noreply, #state{}}. - -handle_info(_Info, State) -> - {noreply, State}. - --spec terminate(term(), #state{}) -> ok. - -terminate(_Reason, _State) -> - ok. - --spec code_change(term(), #state{}, term()) -> {ok, #state{}}. - -code_change(_OldVsn, State, _Extra) -> - {ok, State}. - -%%-------------------------------------------------------------------- - -cast(Message, Coordinator) -> - gen_server:cast(Coordinator, Message). - -call(Message, Coordinator) -> - gen_server:call(Coordinator, Message, infinity). - -reduce_or_stop(#state{all_spawned = AllSpawned, - spawn_count = SpawnCount} = State) -> - Action = - case AllSpawned of - false -> reduce; - true -> - case SpawnCount of - 1 -> finish; - _ -> reduce - end - end, - case Action of - reduce -> - NewState = State#state{spawn_count = SpawnCount - 1}, - {noreply, NewState}; - finish -> - send_done_to_parent(State), - {stop, normal, State} - end. diff --git a/lib/dialyzer/src/dialyzer_succ_typings.erl b/lib/dialyzer/src/dialyzer_succ_typings.erl index ff4102f5a3..b980e43ecc 100644 --- a/lib/dialyzer/src/dialyzer_succ_typings.erl +++ b/lib/dialyzer/src/dialyzer_succ_typings.erl @@ -41,7 +41,7 @@ lookup_names/2 ]). --export_type([servers/0, warning_servers/0]). +-export_type([typesig_init_data/0, dataflow_init_data/0, warnings_init_data/0]). %%-define(DEBUG, true). @@ -61,7 +61,12 @@ %% State record -- local to this module -type parent() :: 'none' | pid(). --type servers() :: term(). %%opaque +-type typesig_init_data() :: term(). +-type dataflow_init_data() :: term(). +-type warnings_init_data() :: term(). + +-type fixpoint_init_data() :: typesig_init_data() | dataflow_init_data(). + -type scc() :: [mfa_or_funlbl()] | [module()]. @@ -146,12 +151,8 @@ get_warnings(Callgraph, Plt, DocPlt, Codeserver, NoWarnUnused, Parent) -> get_warnings_from_modules(Mods, State, DocPlt) -> #st{callgraph = Callgraph, codeserver = Codeserver, no_warn_unused = NoWarnUnused, plt = Plt} = State, - Servers = {Callgraph, Codeserver, NoWarnUnused, Plt, DocPlt}, - Coordinator = dialyzer_coordinator:start('warnings', Servers), - Spawner = fun(M) -> dialyzer_coordinator:scc_spawn(M, Coordinator) end, - lists:foreach(Spawner, Mods), - dialyzer_coordinator:all_spawned(Coordinator), - dialyzer_coordinator:receive_warnings(). + Init = {Callgraph, Codeserver, NoWarnUnused, Plt, DocPlt}, + dialyzer_coordinator:parallel_job(warnings, Mods, Init). -type warning_servers() :: term(). @@ -210,45 +211,36 @@ postprocess_dataflow_warns([{?WARN_CONTRACT_RANGE, {CallF, CallL}, Msg}|Rest], postprocess_dataflow_warns(Rest, Codeserver, WAcc, [W|Acc]) end. -refine_succ_typings(ModulePostorder, #st{codeserver = Codeserver, - callgraph = Callgraph, - plt = Plt} = State) -> +refine_succ_typings(Modules, #st{codeserver = Codeserver, + callgraph = Callgraph, + plt = Plt} = State) -> ?debug("Module postorder: ~p\n", [ModulePostorder]), - Servers = {Codeserver, Callgraph, Plt}, - Coordinator = dialyzer_coordinator:start(dataflow, Servers), - ?timing("refine",refine_succ_typings(ModulePostorder, State, Coordinator)). - -refine_succ_typings([M|Rest], State, Coordinator) -> - Msg = io_lib:format("Dataflow of module: ~w\n", [M]), - send_log(State#st.parent, Msg), - ?debug("~s\n", [Msg]), - dialyzer_coordinator:scc_spawn(M, Coordinator), - refine_succ_typings(Rest, State, Coordinator); -refine_succ_typings([], State, Coordinator) -> - dialyzer_coordinator:all_spawned(Coordinator), - NotFixpoint = dialyzer_coordinator:receive_not_fixpoint(), + Init = {Codeserver, Callgraph, Plt}, + NotFixpoint = + ?timing("refine", + dialyzer_coordinator:parallel_job(dataflow, Modules, Init)), ?debug("==================== Dataflow done ====================\n\n", []), case NotFixpoint =:= [] of true -> {fixpoint, State}; false -> {not_fixpoint, NotFixpoint, State} end. --spec find_depends_on(scc() | module(), servers()) -> [scc()]. +-spec find_depends_on(scc() | module(), fixpoint_init_data()) -> [scc()]. find_depends_on(SCC, {_Codeserver, Callgraph, _Plt}) -> dialyzer_callgraph:get_depends_on(SCC, Callgraph). --spec find_required_by(scc() | module(), servers()) -> [scc()]. +-spec find_required_by(scc() | module(), fixpoint_init_data()) -> [scc()]. find_required_by(SCC, {_Codeserver, Callgraph, _Plt}) -> dialyzer_callgraph:get_required_by(SCC, Callgraph). --spec lookup_names([label()], servers()) -> [mfa_or_funlbl()]. +-spec lookup_names([label()], fixpoint_init_data()) -> [mfa_or_funlbl()]. lookup_names(Labels, {_Codeserver, Callgraph, _Plt}) -> [lookup_name(F, Callgraph) || F <- Labels]. --spec refine_one_module(module(), servers()) -> [label()]. % ordset +-spec refine_one_module(module(), dataflow_init_data()) -> [label()]. % ordset refine_one_module(M, {CodeServer, Callgraph, Plt}) -> ModCode = dialyzer_codeserver:lookup_mod_code(M, CodeServer), @@ -322,28 +314,17 @@ compare_types_1([], [], _Strict, NotFixpoint) -> find_succ_typings(SCCs, #st{codeserver = Codeserver, callgraph = Callgraph, plt = Plt} = State) -> - Servers = {Codeserver, dialyzer_callgraph:mini_callgraph(Callgraph), Plt}, - Coordinator = dialyzer_coordinator:start(typesig, Servers), - ?timing("spawn", _C1, find_succ_typings(SCCs, State, Coordinator)), - dialyzer_coordinator:all_spawned(Coordinator), + Init = {Codeserver, dialyzer_callgraph:mini_callgraph(Callgraph), Plt}, NotFixpoint = - ?timing("typesig", _C2, dialyzer_coordinator:receive_not_fixpoint()), + ?timing("typesig", + dialyzer_coordinator:parallel_job(typesig, SCCs, Init)), ?debug("==================== Typesig done ====================\n\n", []), case NotFixpoint =:= [] of true -> {fixpoint, State}; false -> {not_fixpoint, NotFixpoint, State} end. -find_succ_typings([SCC|Rest], #st{parent = Parent} = State, Coordinator) -> - Msg = io_lib:format("Typesig analysis for SCC: ~w\n", [format_scc(SCC)]), - ?debug("~s", [Msg]), - send_log(Parent, Msg), - dialyzer_coordinator:scc_spawn(SCC, Coordinator), - find_succ_typings(Rest, State, Coordinator); -find_succ_typings([], _State, _Coordinator) -> - ok. - --spec find_succ_types_for_scc(scc(), servers()) -> [mfa_or_funlbl()]. +-spec find_succ_types_for_scc(scc(), typesig_init_data()) -> [mfa_or_funlbl()]. find_succ_types_for_scc(SCC, {Codeserver, Callgraph, Plt}) -> SCC_Info = [{MFA, @@ -459,12 +440,3 @@ lookup_name(F, CG) -> error -> F; {ok, Name} -> Name end. - -send_log(none, _Msg) -> - ok; -send_log(Parent, Msg) -> - Parent ! {self(), log, lists:flatten(Msg)}, - ok. - -format_scc(SCC) -> - [MFA || {_M, _F, _A} = MFA <- SCC]. diff --git a/lib/dialyzer/src/dialyzer_worker.erl b/lib/dialyzer/src/dialyzer_worker.erl index 0ef30cf940..cc4032d154 100644 --- a/lib/dialyzer/src/dialyzer_worker.erl +++ b/lib/dialyzer/src/dialyzer_worker.erl @@ -28,14 +28,13 @@ -type mode() :: dialyzer_coordinator:mode(). -type coordinator() :: dialyzer_coordinator:coordinator(). --type servers() :: dialyzer_succ_typings:servers() | - dialyzer_analysis_callgraph:servers(). +-type init_data() :: dialyzer_coordinator:init_data(). -record(state, { mode :: mode(), job :: mfa_or_funlbl() | file:filename(), coordinator :: coordinator(), - servers :: servers(), + init_data :: init_data(), depends_on = [] :: list() }). @@ -51,12 +50,12 @@ %%-------------------------------------------------------------------- --spec launch(mode(), [mfa_or_funlbl()], servers(), coordinator()) -> worker(). +-spec launch(mode(), [mfa_or_funlbl()], init_data(), coordinator()) -> worker(). -launch(Mode, Job, Servers, Coordinator) -> +launch(Mode, Job, InitData, Coordinator) -> State = #state{mode = Mode, job = Job, - servers = Servers, + init_data = InitData, coordinator = Coordinator}, InitState = case Mode of @@ -75,8 +74,8 @@ loop(updating, State) -> false -> running end, loop(NextStatus, State); -loop(initializing, #state{job = SCC, servers = Servers} = State) -> - DependsOn = dialyzer_succ_typings:find_depends_on(SCC, Servers), +loop(initializing, #state{job = SCC, init_data = InitData} = State) -> + DependsOn = dialyzer_succ_typings:find_depends_on(SCC, InitData), ?debug("Deps ~p: ~p\n",[State#state.job, DependsOn]), loop(updating, State#state{depends_on = DependsOn}); loop(waiting, State) -> @@ -113,8 +112,8 @@ waits_more_success_typings(#state{depends_on = Depends}) -> _ -> true end. -broadcast_done(#state{job = SCC, servers = Servers}) -> - RequiredBy = dialyzer_succ_typings:find_required_by(SCC, Servers), +broadcast_done(#state{job = SCC, init_data = InitData}) -> + RequiredBy = dialyzer_succ_typings:find_required_by(SCC, InitData), {Callers, Unknown} = dialyzer_coordinator:sccs_to_pids(RequiredBy), send_done(Callers, SCC), continue_broadcast_done(Unknown, SCC). @@ -144,18 +143,18 @@ wait_for_success_typings(#state{depends_on = DependsOn} = State) -> State end. -do_work(#state{mode = Mode, job = Job, servers = Servers}) -> +do_work(#state{mode = Mode, job = Job, init_data = InitData}) -> case Mode of - typesig -> dialyzer_succ_typings:find_succ_types_for_scc(Job, Servers); - dataflow -> dialyzer_succ_typings:refine_one_module(Job, Servers) + typesig -> dialyzer_succ_typings:find_succ_types_for_scc(Job, InitData); + dataflow -> dialyzer_succ_typings:refine_one_module(Job, InitData) end. report_to_coordinator(Result, #state{job = Job, coordinator = Coordinator}) -> ?debug("Done: ~p\n",[Job]), dialyzer_coordinator:job_done(Job, Result, Coordinator). -start_compilation(#state{job = Job, servers = Servers}) -> - dialyzer_analysis_callgraph:start_compilation(Job, Servers). +start_compilation(#state{job = Job, init_data = InitData}) -> + dialyzer_analysis_callgraph:start_compilation(Job, InitData). ask_coordinator_for_label(EstimatedSize, #state{coordinator = Coordinator}) -> dialyzer_coordinator:get_next_label(EstimatedSize, Coordinator). @@ -163,5 +162,5 @@ ask_coordinator_for_label(EstimatedSize, #state{coordinator = Coordinator}) -> continue_compilation(Label, Data) -> dialyzer_analysis_callgraph:continue_compilation(Label, Data). -collect_warnings(#state{job = Job, servers = Servers}) -> - dialyzer_succ_typings:collect_warnings(Job, Servers). +collect_warnings(#state{job = Job, init_data = InitData}) -> + dialyzer_succ_typings:collect_warnings(Job, InitData). -- cgit v1.2.3 From 9b0cf90622c3f84f2e3f463c2685d0257d249846 Mon Sep 17 00:00:00 2001 From: Stavros Aronis Date: Fri, 3 Feb 2012 19:23:44 +0100 Subject: Solve big SCC constraints in parallel --- lib/dialyzer/src/dialyzer_typesig.erl | 214 +++++++++++++++++++++++++++------- 1 file changed, 173 insertions(+), 41 deletions(-) (limited to 'lib/dialyzer') diff --git a/lib/dialyzer/src/dialyzer_typesig.erl b/lib/dialyzer/src/dialyzer_typesig.erl index d0d27740f3..3f73afb971 100644 --- a/lib/dialyzer/src/dialyzer_typesig.erl +++ b/lib/dialyzer/src/dialyzer_typesig.erl @@ -91,23 +91,25 @@ -type typesig_scc() :: [{mfa(), {cerl:c_var(), cerl:c_fun()}, dict()}]. -type typesig_funmap() :: [{type_var(), type_var()}]. %% Orddict --record(state, {callgraph :: dialyzer_callgraph:callgraph(), - cs = [] :: [constr()], - cmap = dict:new() :: dict(), - fun_map = [] :: typesig_funmap(), - fun_arities = dict:new() :: dict(), - in_match = false :: boolean(), - in_guard = false :: boolean(), - module :: module(), - name_map = dict:new() :: dict(), - next_label :: label(), - self_recs :: [label()], - plt :: dialyzer_plt:plt(), - prop_types = dict:new() :: dict(), - records = dict:new() :: dict(), - opaques = [] :: [erl_types:erl_type()], - scc = [] :: [type_var()], - mfas = [] :: [dialyzer_callgraph:mfa_or_funlbl()] +-type dict_or_ets() :: {'d', dict()} | {'e', ets:tid()}. + +-record(state, {callgraph :: dialyzer_callgraph:callgraph(), + cs = [] :: [constr()], + cmap = {'d', dict:new()} :: dict_or_ets(), + fun_map = [] :: typesig_funmap(), + fun_arities = dict:new() :: dict(), + in_match = false :: boolean(), + in_guard = false :: boolean(), + module :: module(), + name_map = dict:new() :: dict(), + next_label = 0 :: label(), + self_rec :: erl_types:erl_type(), + plt :: dialyzer_plt:plt(), + prop_types = {'d', dict:new()} :: dict_or_ets(), + records = dict:new() :: dict(), + opaques = [] :: [erl_types:erl_type()], + scc = [] :: [type_var()], + mfas :: [tuple()] }). %%----------------------------------------------------------------------------- @@ -1665,7 +1667,12 @@ solve([Fun], State) -> solve([_|_] = SCC, State) -> ?debug("============ Analyzing SCC: ~w ===========\n", [[debug_lookup_name(F) || F <- SCC]]), - solve_scc(SCC, dict:new(), State, false). + {Parallel, NewState} = + case parallel_split(SCC) of + false -> {false, State}; + SplitSCC -> {SplitSCC, minimize_state(State)} + end, + solve_scc(SCC, Parallel, dict:new(), NewState, false). solve_fun(Fun, FunMap, State) -> Cs = state__get_cs(Fun, State), @@ -1680,8 +1687,7 @@ solve_fun(Fun, FunMap, State) -> end, enter_type(Fun, NewType, NewFunMap1). -solve_scc(SCC, Map, State, TryingUnit) -> - State1 = state__mark_as_non_self_rec(SCC, State), +solve_scc(SCC, Parallel, Map, State, TryingUnit) -> Vars0 = [{Fun, state__get_rec_var(Fun, State)} || Fun <- SCC], Vars = [Var || {_, {ok, Var}} <- Vars0], Funs = [Fun || {Fun, {ok, _}} <- Vars0], @@ -1692,9 +1698,12 @@ solve_scc(SCC, Map, State, TryingUnit) -> end, Map, SCC), Map1 = enter_type_lists(Vars, RecTypes, CleanMap), ?debug("Checking SCC: ~w\n", [[debug_lookup_name(F) || F <- SCC]]), - SolveFun = fun(X, Y) -> scc_fold_fun(X, Y, State1) end, - Map2 = lists:foldl(SolveFun, Map1, SCC), FunSet = ordsets:from_list([t_var_name(F) || F <- SCC]), + Map2 = + case Parallel of + false -> solve_whole_scc(SCC, Map1, State); + SplitSCC -> solve_whole_scc_parallel(SplitSCC, Map1, State) + end, case maps_are_equal(Map2, Map, FunSet) of true -> ?debug("SCC ~w reached fixpoint\n", [SCC]), @@ -1708,15 +1717,127 @@ solve_scc(SCC, Map, State, TryingUnit) -> true -> t_fun(t_fun_args(T), t_unit()) end || T <- NewTypes], Map3 = enter_type_lists(Funs, UnitTypes, Map2), - solve_scc(SCC, Map3, State, true); + solve_scc(SCC, Parallel, Map3, State, true); false -> + case Parallel of + false -> true; + _ -> dispose_state(State) + end, Map2 end; false -> ?debug("SCC ~w did not reach fixpoint\n", [SCC]), - solve_scc(SCC, Map2, State, TryingUnit) + solve_scc(SCC, Parallel, Map2, State, TryingUnit) + end. + +solve_whole_scc(SCC, Map, State) -> + SolveFun = fun(X, Y) -> scc_fold_fun(X, Y, State) end, + lists:foldl(SolveFun, Map, SCC). + +%%------------------------------------------------------------------------------ + +-define(worth_it, 42). + +parallel_split(SCC) -> + Length = length(SCC), + case Length > 2*?worth_it of + false -> false; + true -> + case min(erlang:system_info(logical_processors_available), 8) of + 1 -> false; + CPUs -> + FullShare = Length div CPUs + 1, + Unit = max(FullShare, ?worth_it), + split(SCC, Unit, []) + end end. +minimize_state(#state{ + cmap = {d, CMap}, + fun_map = FunMap, + fun_arities = FunArities, + self_rec = SelfRec, + prop_types = {d, PropTypes}, + opaques = Opaques + }) -> + ETSCMap = ets:new(cmap,[{read_concurrency, true}]), + ETSPropTypes = ets:new(prop_types,[{read_concurrency, true}]), + true = ets:insert(ETSCMap, dict:to_list(CMap)), + true = ets:insert(ETSPropTypes, dict:to_list(PropTypes)), + #state + {cmap = {e, ETSCMap}, + fun_map = FunMap, + fun_arities = FunArities, + self_rec = SelfRec, + prop_types = {e, ETSPropTypes}, + opaques = Opaques + }. + +dispose_state(#state{cmap = {e, ETSCMap}, + prop_types = {e, ETSPropTypes}}) -> + true = ets:delete(ETSCMap), + true = ets:delete(ETSPropTypes). + +solve_whole_scc_parallel(SplitSCC, Map, State) -> + Workers = spawn_workers(SplitSCC, Map, State), + wait_results(Workers, Map, fold_res_fun(State)). + +spawn_workers(SplitSCC, Map, State) -> + Spawner = solve_scc_spawner(self(), Map, State), + lists:foreach(Spawner, SplitSCC), + length(SplitSCC). + +wait_results(0, Map, _FoldResFun) -> + Map; +wait_results(Pending, Map, FoldResFun) -> + Res = receive_scc_result(), + NewMap = lists:foldl(FoldResFun, Map, Res), + wait_results(Pending-1, NewMap, FoldResFun). + +solve_scc_spawner(Parent, Map, State) -> + fun(SCCPart) -> + spawn_link(fun() -> solve_scc_worker(Parent, SCCPart, Map, State) end) + end. + +split([], _Unit, Acc) -> + Acc; +split(List, Unit, Acc) -> + {Taken, Rest} = + try + lists:split(Unit, List) + catch + _:_ -> {List, []} + end, + split(Rest, Unit, [Taken|Acc]). + +solve_scc_worker(Parent, SCCPart, Map, State) -> + SolveFun = fun(X, Y) -> scc_fold_fun(X, Y, State) end, + FinalMap = lists:foldl(SolveFun, Map, SCCPart), + Res = + [{F, t_limit(unsafe_lookup_type(F, FinalMap), ?TYPE_LIMIT)} || + F <- SCCPart], + send_scc_result(Parent, Res). + +fold_res_fun(State) -> + fun({F, Type}, Map) -> + case state__get_rec_var(F, State) of + {ok, R} -> + enter_type(R, Type, enter_type(F, Type, Map)); + error -> + enter_type(F, Type, Map) + end + end. + +receive_scc_result() -> + receive + {scc_fun, Res} -> Res + end. + +send_scc_result(Parent, Res) -> + Parent ! {scc_fun, Res}. + +%%------------------------------------------------------------------------------ + scc_fold_fun(F, FunMap, State) -> Deps = get_deps(state__get_cs(F, State)), Cs = mk_constraint_ref(F, Deps), @@ -2090,11 +2211,19 @@ new_state(SCC0, NextLabel, CallGraph, Plt, PropTypes) -> NameMap = dict:from_list(List), MFAs = [MFA || {MFA, _Var} <- List], SCC = [mk_var(Fun) || {_MFA, {_Var, Fun}, _Rec} <- SCC0], - SelfRecs = [F || F <- SCC, - dialyzer_callgraph:is_self_rec(t_var_name(F), CallGraph)], + SelfRec = + case SCC of + [OneF] -> + Label = t_var_name(OneF), + case dialyzer_callgraph:is_self_rec(Label, CallGraph) of + true -> OneF; + false -> false + end; + _Many -> false + end, #state{callgraph = CallGraph, name_map = NameMap, next_label = NextLabel, - prop_types = PropTypes, plt = Plt, scc = ordsets:from_list(SCC), - mfas = MFAs, self_recs = ordsets:from_list(SelfRecs)}. + prop_types = {d, PropTypes}, plt = Plt, scc = ordsets:from_list(SCC), + mfas = MFAs, self_rec = SelfRec}. state__set_rec_dict(State, RecDict) -> State#state{records = RecDict}. @@ -2193,14 +2322,21 @@ state__plt(#state{plt = PLT}) -> state__new_constraint_context(State) -> State#state{cs = []}. -state__prop_domain(FunLabel, #state{prop_types = PropTypes}) -> +state__prop_domain(FunLabel, #state{prop_types = {e, ETSPropTypes}}) -> + try ets:lookup_element(ETSPropTypes, FunLabel, 2) of + {_Range_Fun, Dom} -> {ok, Dom}; + FunType -> {ok, t_fun_args(FunType)} + catch + _:_ -> error + end; +state__prop_domain(FunLabel, #state{prop_types = {d, PropTypes}}) -> case dict:find(FunLabel, PropTypes) of error -> error; {ok, {_Range_Fun, Dom}} -> {ok, Dom}; {ok, FunType} -> {ok, t_fun_args(FunType)} end. -state__add_prop_constrs(Tree, #state{prop_types = PropTypes} = State) -> +state__add_prop_constrs(Tree, #state{prop_types = {d, PropTypes}} = State) -> Label = cerl_trees:get_label(Tree), case dict:find(Label, PropTypes) of error -> State; @@ -2263,21 +2399,17 @@ state__mk_vars(N, #state{next_label = NL} = State) -> Vars = [t_var(X) || X <- lists:seq(NL, NewLabel-1)], {State#state{next_label = NewLabel}, Vars}. -state__store_constrs(Id, Cs, #state{cmap = Dict} = State) -> +state__store_constrs(Id, Cs, #state{cmap = {d, Dict}} = State) -> NewDict = dict:store(Id, Cs, Dict), - State#state{cmap = NewDict}. + State#state{cmap = {d, NewDict}}. -state__get_cs(Var, #state{cmap = Dict}) -> +state__get_cs(Var, #state{cmap = {e, ETSDict}}) -> + ets:lookup_element(ETSDict, Var, 2); +state__get_cs(Var, #state{cmap = {d, Dict}}) -> dict:fetch(Var, Dict). -%% The functions here will not be treated as self recursive. -%% These functions will need to be handled as such manually. -state__mark_as_non_self_rec(SCC, #state{self_recs = SelfRecs} = State) -> - %% TODO: Check if the result is always empty and just set it to [] if so. - State#state{self_recs = ordsets:subtract(SelfRecs, ordsets:from_list(SCC))}. - -state__is_self_rec(Fun, #state{self_recs = SelfRecs}) -> - ordsets:is_element(Fun, SelfRecs). +state__is_self_rec(Fun, #state{self_rec = SelfRec}) -> + Fun =:= SelfRec. state__store_funs(Vars0, Funs0, #state{fun_map = Map} = State) -> debug_make_name_map(Vars0, Funs0), -- cgit v1.2.3 From 9f30b73af775daeca99e8094b08ca4e5d9b6cd82 Mon Sep 17 00:00:00 2001 From: Stavros Aronis Date: Tue, 28 Feb 2012 11:04:12 +0100 Subject: More efficient calculation of module deps and postorder --- lib/dialyzer/src/dialyzer_callgraph.erl | 45 +++++++++++++++++---------------- 1 file changed, 23 insertions(+), 22 deletions(-) (limited to 'lib/dialyzer') diff --git a/lib/dialyzer/src/dialyzer_callgraph.erl b/lib/dialyzer/src/dialyzer_callgraph.erl index 0384160abc..cc26d9fdd5 100644 --- a/lib/dialyzer/src/dialyzer_callgraph.erl +++ b/lib/dialyzer/src/dialyzer_callgraph.erl @@ -279,26 +279,36 @@ modules(#callgraph{digraph = DG}) -> -spec module_postorder(callgraph()) -> {[module()], {'d', digraph()}}. module_postorder(#callgraph{digraph = DG}) -> - Edges = digraph_edges(DG), - Nodes = ordsets:from_list([M || {M,_F,_A} <- digraph_vertices(DG)]), + Edges = lists:foldl(fun edge_fold/2, sets:new(), digraph_edges(DG)), + Nodes = sets:from_list([M || {M,_F,_A} <- digraph_vertices(DG)]), MDG = digraph:new([acyclic]), - MDG1 = digraph_confirm_vertices(Nodes, MDG), - MDG2 = create_module_digraph(Edges, MDG1), - PostOrder = digraph_utils:topsort(MDG2), - {PostOrder, {'d', MDG2}}. + MDG = digraph_confirm_vertices(sets:to_list(Nodes), MDG), + Foreach = fun({M1,M2}) -> digraph:add_edge(MDG, M1, M2) end, + lists:foreach(Foreach, sets:to_list(Edges)), + PostOrder = digraph_utils:topsort(MDG), + {PostOrder, {'d', MDG}}. + +edge_fold({{M1,_,_},{M2,_,_}}, Set) -> + case M1 =/= M2 of + true -> sets:add_element({M1,M2},Set); + false -> Set + end; +edge_fold(_, Set) -> Set. + %% The module deps of a module are modules that depend on the module -spec module_deps(callgraph()) -> dict(). module_deps(#callgraph{digraph = DG}) -> - Edges = digraph_edges(DG), - Nodes = ordsets:from_list([M || {M,_F,_A} <- digraph_vertices(DG)]), + Edges = lists:foldl(fun edge_fold/2, sets:new(), digraph_edges(DG)), + Nodes = sets:from_list([M || {M,_F,_A} <- digraph_vertices(DG)]), MDG = digraph:new(), - MDG1 = digraph_confirm_vertices(Nodes, MDG), - MDG2 = create_module_digraph(Edges, MDG1), - Deps = [{N, ordsets:from_list(digraph:in_neighbours(MDG2, N))} - || N <- Nodes], - digraph_delete(MDG2), + MDG = digraph_confirm_vertices(sets:to_list(Nodes), MDG), + Foreach = fun({M1,M2}) -> digraph:add_edge(MDG, M1, M2) end, + lists:foreach(Foreach, sets:to_list(Edges)), + Deps = [{N, ordsets:from_list(digraph:in_neighbours(MDG, N))} + || N <- sets:to_list(Nodes)], + digraph_delete(MDG), dict:from_list(Deps). -spec strip_module_deps(dict(), set()) -> dict(). @@ -310,15 +320,6 @@ strip_module_deps(ModDeps, StripSet) -> FilterFun2 = fun(_Key, ValSet) -> ValSet =/= [] end, dict:filter(FilterFun2, ModDeps1). -create_module_digraph([{{M, _, _}, {M, _, _}}|Left], MDG) -> - create_module_digraph(Left, MDG); -create_module_digraph([{{M1, _, _}, {M2, _, _}}|Left], MDG) -> - create_module_digraph(Left, digraph_add_edge(M1, M2, MDG)); -create_module_digraph([{_, _}|Left], MDG) -> - create_module_digraph(Left, MDG); -create_module_digraph([], MDG) -> - MDG. - -spec finalize(callgraph()) -> {[scc()], callgraph()}. finalize(#callgraph{digraph = DG} = CG) -> -- cgit v1.2.3 From ec142ae5c0696bb7893a67ff6356c7da3369a7fc Mon Sep 17 00:00:00 2001 From: Kostis Sagonas Date: Sun, 4 Mar 2012 16:16:52 +0100 Subject: Code simplifications (tidier) --- lib/dialyzer/src/dialyzer_callgraph.erl | 5 +---- lib/dialyzer/src/dialyzer_worker.erl | 5 +---- 2 files changed, 2 insertions(+), 8 deletions(-) (limited to 'lib/dialyzer') diff --git a/lib/dialyzer/src/dialyzer_callgraph.erl b/lib/dialyzer/src/dialyzer_callgraph.erl index cc26d9fdd5..854d67e835 100644 --- a/lib/dialyzer/src/dialyzer_callgraph.erl +++ b/lib/dialyzer/src/dialyzer_callgraph.erl @@ -354,10 +354,7 @@ ets_lookup_dict(Key, Table) -> end. ets_lookup_set(Key, Table) -> - case ets:lookup(Table, Key) of - [] -> false; - _ -> true - end. + ets:lookup(Table, Key) =/= []. %%---------------------------------------------------------------------- %% Core code diff --git a/lib/dialyzer/src/dialyzer_worker.erl b/lib/dialyzer/src/dialyzer_worker.erl index cc4032d154..b58e662f06 100644 --- a/lib/dialyzer/src/dialyzer_worker.erl +++ b/lib/dialyzer/src/dialyzer_worker.erl @@ -107,10 +107,7 @@ loop(running, #state{mode = Mode} = State) when report_to_coordinator(NotFixpoint, State). waits_more_success_typings(#state{depends_on = Depends}) -> - case Depends of - [] -> false; - _ -> true - end. + Depends =/= []. broadcast_done(#state{job = SCC, init_data = InitData}) -> RequiredBy = dialyzer_succ_typings:find_required_by(SCC, InitData), -- cgit v1.2.3 From 49ccc5df4f2f64ff19dd5751523c8ba45adaf658 Mon Sep 17 00:00:00 2001 From: Stavros Aronis Date: Mon, 5 Mar 2012 10:38:01 +0100 Subject: Better reflect side-effect based code in dialyzer_callgraph --- lib/dialyzer/src/dialyzer_behaviours.erl | 4 ++-- lib/dialyzer/src/dialyzer_callgraph.erl | 31 ++++++++++++++++--------------- 2 files changed, 18 insertions(+), 17 deletions(-) (limited to 'lib/dialyzer') diff --git a/lib/dialyzer/src/dialyzer_behaviours.erl b/lib/dialyzer/src/dialyzer_behaviours.erl index d40caae8b4..b84071b95c 100644 --- a/lib/dialyzer/src/dialyzer_behaviours.erl +++ b/lib/dialyzer/src/dialyzer_behaviours.erl @@ -280,8 +280,8 @@ translate_callgraph([{Behaviour,_}|Behaviours], Module, Callgraph) -> DirectCalls = [{From, {Module, Fun, Arity}} || {From, To} <- UsedCalls,{API, {Fun, Arity, _Ord}} <- Calls, To =:= API], - NewCallgraph = dialyzer_callgraph:add_edges(DirectCalls, Callgraph), - translate_callgraph(Behaviours, Module, NewCallgraph); + dialyzer_callgraph:add_edges(DirectCalls, Callgraph), + translate_callgraph(Behaviours, Module, Callgraph); translate_callgraph([], _Module, Callgraph) -> Callgraph. diff --git a/lib/dialyzer/src/dialyzer_callgraph.erl b/lib/dialyzer/src/dialyzer_callgraph.erl index 854d67e835..3330ec252d 100644 --- a/lib/dialyzer/src/dialyzer_callgraph.erl +++ b/lib/dialyzer/src/dialyzer_callgraph.erl @@ -206,17 +206,17 @@ is_escaping(Label, #callgraph{esc = Esc}) when is_integer(Label) -> ets_lookup_set(Label, Esc). -type callgraph_edge() :: {mfa_or_funlbl(),mfa_or_funlbl()}. --spec add_edges([callgraph_edge()], callgraph()) -> callgraph(). +-spec add_edges([callgraph_edge()], callgraph()) -> ok. -add_edges([], CG) -> - CG; -add_edges(Edges, #callgraph{digraph = Digraph} = CG) -> - CG#callgraph{digraph = digraph_add_edges(Edges, Digraph)}. +add_edges([], _CG) -> + ok; +add_edges(Edges, #callgraph{digraph = Digraph}) -> + digraph_add_edges(Edges, Digraph). --spec add_edges([callgraph_edge()], [mfa_or_funlbl()], callgraph()) -> callgraph(). +-spec add_edges([callgraph_edge()], [mfa_or_funlbl()], callgraph()) -> ok. add_edges(Edges, MFAs, #callgraph{digraph = DG} = CG) -> - DG = digraph_confirm_vertices(MFAs, DG), + digraph_confirm_vertices(MFAs, DG), add_edges(Edges, CG). -spec remove_external(callgraph()) -> {callgraph(), [tuple()]}. @@ -282,7 +282,7 @@ module_postorder(#callgraph{digraph = DG}) -> Edges = lists:foldl(fun edge_fold/2, sets:new(), digraph_edges(DG)), Nodes = sets:from_list([M || {M,_F,_A} <- digraph_vertices(DG)]), MDG = digraph:new([acyclic]), - MDG = digraph_confirm_vertices(sets:to_list(Nodes), MDG), + digraph_confirm_vertices(sets:to_list(Nodes), MDG), Foreach = fun({M1,M2}) -> digraph:add_edge(MDG, M1, M2) end, lists:foreach(Foreach, sets:to_list(Edges)), PostOrder = digraph_utils:topsort(MDG), @@ -303,7 +303,7 @@ module_deps(#callgraph{digraph = DG}) -> Edges = lists:foldl(fun edge_fold/2, sets:new(), digraph_edges(DG)), Nodes = sets:from_list([M || {M,_F,_A} <- digraph_vertices(DG)]), MDG = digraph:new(), - MDG = digraph_confirm_vertices(sets:to_list(Nodes), MDG), + digraph_confirm_vertices(sets:to_list(Nodes), MDG), Foreach = fun({M1,M2}) -> digraph:add_edge(MDG, M1, M2) end, lists:foreach(Foreach, sets:to_list(Edges)), Deps = [{N, ordsets:from_list(digraph:in_neighbours(MDG, N))} @@ -511,9 +511,10 @@ get_label(T) -> %%---------------------------------------------------------------------- digraph_add_edges([{From, To}|Left], DG) -> - digraph_add_edges(Left, digraph_add_edge(From, To, DG)); -digraph_add_edges([], DG) -> - DG. + digraph_add_edge(From, To, DG), + digraph_add_edges(Left, DG); +digraph_add_edges([], _DG) -> + ok. digraph_add_edge(From, To, DG) -> case digraph:vertex(DG, From) of @@ -525,13 +526,13 @@ digraph_add_edge(From, To, DG) -> {To, _} -> ok end, digraph:add_edge(DG, {From, To}, From, To, []), - DG. + ok. digraph_confirm_vertices([MFA|Left], DG) -> digraph:add_vertex(DG, MFA, confirmed), digraph_confirm_vertices(Left, DG); -digraph_confirm_vertices([], DG) -> - DG. +digraph_confirm_vertices([], _DG) -> + ok. digraph_remove_external(DG) -> Vertices = digraph:vertices(DG), -- cgit v1.2.3 From 4a1fa04e5f57ad56e35aae8e9ff278bf1133889a Mon Sep 17 00:00:00 2001 From: Stavros Aronis Date: Mon, 5 Mar 2012 11:39:28 +0100 Subject: Change --time to --statistics and include more info --- lib/dialyzer/src/dialyzer_cl_parse.erl | 9 +++++---- lib/dialyzer/src/dialyzer_coordinator.erl | 7 +++++++ lib/dialyzer/src/dialyzer_timing.erl | 29 ++++++++++++++++++++++------- 3 files changed, 34 insertions(+), 11 deletions(-) (limited to 'lib/dialyzer') diff --git a/lib/dialyzer/src/dialyzer_cl_parse.erl b/lib/dialyzer/src/dialyzer_cl_parse.erl index 5baba7d11b..05e50c3d6b 100644 --- a/lib/dialyzer/src/dialyzer_cl_parse.erl +++ b/lib/dialyzer/src/dialyzer_cl_parse.erl @@ -164,7 +164,7 @@ cl(["--src"|T]) -> cl(["--no_spec"|T]) -> put(dialyzer_options_use_contracts, false), cl(T); -cl(["--time"|T]) -> +cl(["--statistics"|T]) -> put(dialyzer_timing, true), cl(T); cl(["-v"|_]) -> @@ -356,7 +356,7 @@ help_message() -> [--apps applications] [-o outfile] [--build_plt] [--add_to_plt] [--remove_from_plt] [--check_plt] [--no_check_plt] [--plt_info] [--get_warnings] - [--no_native] [--fullpath] [--time] + [--no_native] [--fullpath] [--statistics] Options: files_or_dirs (for backwards compatibility also as: -c files_or_dirs) Use Dialyzer from the command line to detect defects in the @@ -423,8 +423,9 @@ Options: Make Dialyzer a bit more quiet. --verbose Make Dialyzer a bit more verbose. - --time - Print time information + --statistics + Prints information about the progress of execution (analysis phases, + time spent in each and size of the relative input). --build_plt The analysis starts from an empty plt and creates a new one from the files specified with -c and -r. Only works for beam files. diff --git a/lib/dialyzer/src/dialyzer_coordinator.erl b/lib/dialyzer/src/dialyzer_coordinator.erl index 490cfb8d49..63b2d8c3f2 100644 --- a/lib/dialyzer/src/dialyzer_coordinator.erl +++ b/lib/dialyzer/src/dialyzer_coordinator.erl @@ -105,6 +105,12 @@ spawn_jobs(Mode, Jobs, InitData) when Count + 1 end, JobCount = lists:foldl(Fold, 0, Jobs), + Unit = + case Mode of + 'typesig' -> "SCCs"; + 'dataflow' -> "modules" + end, + dialyzer_timing:send_size_info(JobCount, Unit), #state{active = JobCount, result = [], init_data = InitData}; spawn_jobs(Mode, Jobs, InitData) when Mode =:= 'compile'; Mode =:= 'warnings' -> @@ -122,6 +128,7 @@ spawn_jobs(Mode, Jobs, InitData) when InitTickets = 4*CPUs, {Tickets, Queue, JobCount} = lists:foldl(Fold, {InitTickets, queue:new(), 0}, Jobs), + dialyzer_timing:send_size_info(JobCount, "modules"), InitResult = case Mode of 'warnings' -> []; diff --git a/lib/dialyzer/src/dialyzer_timing.erl b/lib/dialyzer/src/dialyzer_timing.erl index dc4d522c2f..c2a7b3ecf8 100644 --- a/lib/dialyzer/src/dialyzer_timing.erl +++ b/lib/dialyzer/src/dialyzer_timing.erl @@ -26,7 +26,7 @@ -module(dialyzer_timing). --export([init/1, start_stamp/1, end_stamp/0, stop/0]). +-export([init/1, start_stamp/1, send_size_info/2, end_stamp/0, stop/0]). -spec init(boolean()) -> ok. @@ -43,7 +43,7 @@ loop_init(Active) -> case Active of true -> io:format("\n"), - loop(now()); + loop(now(), 0, ""); false -> dummy_loop() end. @@ -53,16 +53,25 @@ dummy_loop() -> _ -> dummy_loop() end. -loop(LastNow) -> +loop(LastNow, Size, Unit) -> receive {stamp, Msg, Now} -> io:format(" ~-10s (+~4.2fs):", [Msg, diff(Now, LastNow)]), - loop(Now); + loop(Now, 0, ""); {stamp, Now} -> - io:format("~7.2fs\n", [diff(Now, LastNow)]), - loop(Now); + SizeStr = + case Size of + 0 -> ""; + _ -> + Data = io_lib:format("~p ~s",[Size, Unit]), + io_lib:format(" (~12s)",[Data]) + end, + io:format("~7.2fs~s\n", [diff(Now, LastNow), SizeStr]), + loop(Now, 0, ""); + {size, NewSize, NewUnit} -> + loop(LastNow, NewSize, NewUnit); {Pid, stop, Now} -> - io:format(" ~-10s (+~4.2fs)\n", ["",diff(Now, LastNow)]), + io:format(" ~-9s (+~5.2fs)\n", ["",diff(Now, LastNow)]), Pid ! ok end. @@ -78,6 +87,12 @@ end_stamp() -> ?MODULE ! {stamp, now()}, ok. +-spec send_size_info(integer(), string()) -> ok. + +send_size_info(Size, Unit) -> + ?MODULE ! {size, Size, Unit}, + ok. + -spec stop() -> ok. stop() -> -- cgit v1.2.3 From 4920bb64ad39fde53b02e73c22e7e771b016283f Mon Sep 17 00:00:00 2001 From: Stavros Aronis Date: Mon, 5 Mar 2012 12:56:37 +0100 Subject: Remove unused function --- lib/dialyzer/src/dialyzer_coordinator.erl | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) (limited to 'lib/dialyzer') diff --git a/lib/dialyzer/src/dialyzer_coordinator.erl b/lib/dialyzer/src/dialyzer_coordinator.erl index 63b2d8c3f2..1ae30d9862 100644 --- a/lib/dialyzer/src/dialyzer_coordinator.erl +++ b/lib/dialyzer/src/dialyzer_coordinator.erl @@ -35,7 +35,7 @@ -export([sccs_to_pids/1]). %%% Exports for the compilation workers --export([get_next_label/2, compilation_done/3]). +-export([get_next_label/2]). -export_type([coordinator/0, mode/0, init_data/0]). @@ -232,14 +232,6 @@ job_done(Job, Result, Coordinator) -> Coordinator ! {done, Job, Result}, ok. --spec compilation_done(file:filename(), - dialyzer_analysis_callgraph:compile_result(), - coordinator()) -> ok. - -compilation_done(Filename, CompilationData, Coordinator) -> - Coordinator ! {done, Filename, CompilationData}, - ok. - -spec get_next_label(integer(), coordinator()) -> integer(). get_next_label(EstimatedSize, Coordinator) -> -- cgit v1.2.3 From 19f4b4090fd38085d60596b4685c9323501d473e Mon Sep 17 00:00:00 2001 From: Stavros Aronis Date: Mon, 5 Mar 2012 13:00:36 +0100 Subject: Inline a function in dialyzer_worker --- lib/dialyzer/src/dialyzer_worker.erl | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) (limited to 'lib/dialyzer') diff --git a/lib/dialyzer/src/dialyzer_worker.erl b/lib/dialyzer/src/dialyzer_worker.erl index b58e662f06..453bb76dfc 100644 --- a/lib/dialyzer/src/dialyzer_worker.erl +++ b/lib/dialyzer/src/dialyzer_worker.erl @@ -89,7 +89,7 @@ loop(running, #state{mode = 'compile'} = State) -> case start_compilation(State) of {ok, EstimatedSize, Data} -> Label = ask_coordinator_for_label(EstimatedSize, State), - continue_compilation(Label, Data); + dialyzer_analysis_callgraph:continue_compilation(Label, Data); {error, _Reason} = Error -> Error end, @@ -156,8 +156,5 @@ start_compilation(#state{job = Job, init_data = InitData}) -> ask_coordinator_for_label(EstimatedSize, #state{coordinator = Coordinator}) -> dialyzer_coordinator:get_next_label(EstimatedSize, Coordinator). -continue_compilation(Label, Data) -> - dialyzer_analysis_callgraph:continue_compilation(Label, Data). - collect_warnings(#state{job = Job, init_data = InitData}) -> dialyzer_succ_typings:collect_warnings(Job, InitData). -- cgit v1.2.3 From 76b7c72882ee521a7c5a39d33ecf1009a72ee4e3 Mon Sep 17 00:00:00 2001 From: Stavros Aronis Date: Mon, 5 Mar 2012 13:01:11 +0100 Subject: Fix types and specs --- lib/dialyzer/src/dialyzer_analysis_callgraph.erl | 20 +++++++++++++------- lib/dialyzer/src/dialyzer_callgraph.erl | 2 +- lib/dialyzer/src/dialyzer_coordinator.erl | 6 +++++- 3 files changed, 19 insertions(+), 9 deletions(-) (limited to 'lib/dialyzer') diff --git a/lib/dialyzer/src/dialyzer_analysis_callgraph.erl b/lib/dialyzer/src/dialyzer_analysis_callgraph.erl index e2e65d2e25..67c5b3241d 100644 --- a/lib/dialyzer/src/dialyzer_analysis_callgraph.erl +++ b/lib/dialyzer/src/dialyzer_analysis_callgraph.erl @@ -35,9 +35,9 @@ start_compilation/2, continue_compilation/2]). --export_type([compile_mid_data/0, - compile_result/0, - compile_init_data/0]). +-export_type([compile_init_data/0, + one_file_result/0, + compile_result/0]). -include("dialyzer.hrl"). @@ -265,9 +265,15 @@ compile_and_store(Files, #analysis_state{codeserver = CServer, {Callgraph, sets:from_list(NoWarn), CServer2}. -type compile_init_data() :: #compile_init{}. --type compile_result() :: {list(), list(), list()}. %%opaque --type one_file_result() :: term(). %%opaque --type compile_mid_data() :: term(). %%opaque +-type error_reason() :: string(). +-type compile_result() :: {[{file:filename(), error_reason()}], [mfa()], + [module()]}. %%opaque +-type one_file_result() :: {error, error_reason()} | + {ok, [dialyzer_callgraph:callgraph_edge()], + [mfa_or_funlbl()], [mfa()], module()}. %%opaque +-type compile_mid_data() :: {module(), cerl:cerl(), [mfa()], + dialyzer_callgraph:callgraph(), + dialyzer_codeserver:codeserver()}. -spec compile_init_result() -> compile_result(). @@ -287,7 +293,7 @@ add_to_result(File, NewData, {Failed, NoWarn, Mods}, InitData) -> end. -spec start_compilation(file:filename(), compile_init_data()) -> - {error, term()} |{ok, integer(), compile_mid_data()}. + {error, error_reason()} |{ok, integer(), compile_mid_data()}. start_compilation(File, #compile_init{callgraph = Callgraph, codeserver = Codeserver, diff --git a/lib/dialyzer/src/dialyzer_callgraph.erl b/lib/dialyzer/src/dialyzer_callgraph.erl index 3330ec252d..d6307ec658 100644 --- a/lib/dialyzer/src/dialyzer_callgraph.erl +++ b/lib/dialyzer/src/dialyzer_callgraph.erl @@ -64,7 +64,7 @@ put_named_tables/2, put_public_tables/2, put_behaviour_api_calls/2, get_behaviour_api_calls/1, dispose_race_server/1, duplicate/1]). --export_type([callgraph/0, mfa_or_funlbl/0]). +-export_type([callgraph/0, mfa_or_funlbl/0, callgraph_edge/0]). -include("dialyzer.hrl"). diff --git a/lib/dialyzer/src/dialyzer_coordinator.erl b/lib/dialyzer/src/dialyzer_coordinator.erl index 1ae30d9862..8ebbb11137 100644 --- a/lib/dialyzer/src/dialyzer_coordinator.erl +++ b/lib/dialyzer/src/dialyzer_coordinator.erl @@ -69,6 +69,10 @@ -type result() :: compile_result() | typesig_result() | dataflow_result() | warnings_result(). +-type job() :: scc() | module() | file:filename(). +-type job_result() :: dialyzer_analysis_callgraph:one_file_result() | + typesig_result() | dataflow_result() | warnings_result(). + -record(state, {active = 0 :: integer(), result :: result(), next_label = 0 :: integer(), @@ -226,7 +230,7 @@ pid_partition(SCC, {Pids, Unknown}) -> _:_ -> {Pids, [SCC|Unknown]} end. --spec job_done(scc() | module() | file:filename(), term(), coordinator()) -> ok. +-spec job_done(job(), job_result(), coordinator()) -> ok. job_done(Job, Result, Coordinator) -> Coordinator ! {done, Job, Result}, -- cgit v1.2.3 From d24d65f1a76871d3e9c8f5c19f0b4ace6de393aa Mon Sep 17 00:00:00 2001 From: Stavros Aronis Date: Wed, 21 Mar 2012 16:34:58 +0100 Subject: Remove needless conversion The final version of the callgraph needs not be "reduced" for more efficient copying. --- lib/dialyzer/src/dialyzer_callgraph.erl | 20 -------------------- lib/dialyzer/src/dialyzer_succ_typings.erl | 2 +- 2 files changed, 1 insertion(+), 21 deletions(-) (limited to 'lib/dialyzer') diff --git a/lib/dialyzer/src/dialyzer_callgraph.erl b/lib/dialyzer/src/dialyzer_callgraph.erl index d6307ec658..327092e23c 100644 --- a/lib/dialyzer/src/dialyzer_callgraph.erl +++ b/lib/dialyzer/src/dialyzer_callgraph.erl @@ -44,7 +44,6 @@ %% module_postorder/1, module_postorder_from_funs/2, new/0, - mini_callgraph/1, get_depends_on/2, get_required_by/2, in_neighbours/2, @@ -129,25 +128,6 @@ new() -> self_rec = ETSSelfRec, calls = ETSCalls}. --spec mini_callgraph(callgraph()) -> callgraph(). - -mini_callgraph(#callgraph{digraph = Digraph, - active_digraph = ActiveDigraph, - esc = Esc, - name_map = NameMap, - rev_name_map = RevNameMap, - rec_var_map = RecVarMap, - self_rec = SelfRecs, - calls = Calls}) -> - #callgraph{digraph = Digraph, - active_digraph = ActiveDigraph, - esc = Esc, - name_map = NameMap, - rev_name_map = RevNameMap, - rec_var_map = RecVarMap, - self_rec = SelfRecs, - calls = Calls}. - -spec delete(callgraph()) -> 'true'. delete(#callgraph{digraph = Digraph}) -> diff --git a/lib/dialyzer/src/dialyzer_succ_typings.erl b/lib/dialyzer/src/dialyzer_succ_typings.erl index b980e43ecc..9f61f9ff6f 100644 --- a/lib/dialyzer/src/dialyzer_succ_typings.erl +++ b/lib/dialyzer/src/dialyzer_succ_typings.erl @@ -314,7 +314,7 @@ compare_types_1([], [], _Strict, NotFixpoint) -> find_succ_typings(SCCs, #st{codeserver = Codeserver, callgraph = Callgraph, plt = Plt} = State) -> - Init = {Codeserver, dialyzer_callgraph:mini_callgraph(Callgraph), Plt}, + Init = {Codeserver, Callgraph, Plt}, NotFixpoint = ?timing("typesig", dialyzer_coordinator:parallel_job(typesig, SCCs, Init)), -- cgit v1.2.3 From 24c2c72cab2c282eb17c8ac97c832106ecaf4ed8 Mon Sep 17 00:00:00 2001 From: Stavros Aronis Date: Wed, 21 Mar 2012 17:58:09 +0100 Subject: Better names for callgaph ETS tables --- lib/dialyzer/src/dialyzer_callgraph.erl | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) (limited to 'lib/dialyzer') diff --git a/lib/dialyzer/src/dialyzer_callgraph.erl b/lib/dialyzer/src/dialyzer_callgraph.erl index 327092e23c..4ff9f5920b 100644 --- a/lib/dialyzer/src/dialyzer_callgraph.erl +++ b/lib/dialyzer/src/dialyzer_callgraph.erl @@ -785,9 +785,9 @@ to_ps(#callgraph{} = CG, File, Args) -> condensation(G) -> SCs = digraph_utils:strong_components(G), - V2I = ets:new(condensation, []), - I2C = ets:new(condensation, []), - I2I = ets:new(condensation, [bag]), + V2I = ets:new(condensation_v2i, []), + I2C = ets:new(condensation_i2c, []), + I2I = ets:new(condensation_i2i, [bag]), CFun = fun(SC, N) -> lists:foreach(fun(V) -> true = ets:insert(V2I, {V,N}) end, SC), @@ -813,7 +813,8 @@ condensation(G) -> end, {OutDict, InDict} = ets:foldl(Fun3, {dict:new(), dict:new()}, I2I), [OutETS, InETS] = - [ets:new(Name,[{read_concurrency, true}]) || Name <- [out, in]], + [ets:new(Name,[{read_concurrency, true}]) || + Name <- [callgraph_deps_out, callgraph_deps_in]], ets:insert(OutETS, dict:to_list(OutDict)), ets:insert(InETS, dict:to_list(InDict)), ets:delete(V2I), -- cgit v1.2.3 From 4e1ed3a5666c13d442759e710d9d08280362c0bb Mon Sep 17 00:00:00 2001 From: Stavros Aronis Date: Thu, 22 Mar 2012 10:21:18 +0100 Subject: Relocate start and stop of timing server --- lib/dialyzer/src/dialyzer.hrl | 1 + lib/dialyzer/src/dialyzer_analysis_callgraph.erl | 4 +++- lib/dialyzer/src/dialyzer_cl.erl | 5 ++--- lib/dialyzer/src/dialyzer_timing.erl | 6 ++---- 4 files changed, 8 insertions(+), 8 deletions(-) (limited to 'lib/dialyzer') diff --git a/lib/dialyzer/src/dialyzer.hrl b/lib/dialyzer/src/dialyzer.hrl index f282bbf73c..ebfc6b9731 100644 --- a/lib/dialyzer/src/dialyzer.hrl +++ b/lib/dialyzer/src/dialyzer.hrl @@ -127,6 +127,7 @@ use_contracts = true :: boolean(), race_detection = false :: boolean(), behaviours_chk = false :: boolean(), + timing = false :: boolean(), callgraph_file = "" :: file:filename()}). -record(options, {files = [] :: [file:filename()], diff --git a/lib/dialyzer/src/dialyzer_analysis_callgraph.erl b/lib/dialyzer/src/dialyzer_analysis_callgraph.erl index 67c5b3241d..641b7eb22a 100644 --- a/lib/dialyzer/src/dialyzer_analysis_callgraph.erl +++ b/lib/dialyzer/src/dialyzer_analysis_callgraph.erl @@ -64,12 +64,14 @@ -spec start(pid(), [dial_warn_tag()], #analysis{}) -> 'ok'. start(Parent, LegalWarnings, Analysis) -> + dialyzer_timing:init(Analysis#analysis.timing), RacesOn = ordsets:is_element(?WARN_RACE_CONDITION, LegalWarnings), Analysis0 = Analysis#analysis{race_detection = RacesOn}, Analysis1 = expand_files(Analysis0), Analysis2 = run_analysis(Analysis1), State = #server_state{parent = Parent, legal_warnings = LegalWarnings}, - loop(State, Analysis2, none). + loop(State, Analysis2, none), + dialyzer_timing:stop(). run_analysis(Analysis) -> Self = self(), diff --git a/lib/dialyzer/src/dialyzer_cl.erl b/lib/dialyzer/src/dialyzer_cl.erl index 482f428e02..5d253e77fa 100644 --- a/lib/dialyzer/src/dialyzer_cl.erl +++ b/lib/dialyzer/src/dialyzer_cl.erl @@ -394,16 +394,15 @@ do_analysis(Files, Options, Plt, PltInfo) -> defines = Options#options.defines, include_dirs = Options#options.include_dirs, files = Files, - start_from = Options#options.from, + start_from = Options#options.from, + timing = Options#options.timing, plt = Plt, use_contracts = Options#options.use_contracts, callgraph_file = Options#options.callgraph_file}, State3 = start_analysis(State2, InitAnalysis), {T1, _} = statistics(wall_clock), - ok = dialyzer_timing:init(Options#options.timing), Return = cl_loop(State3), {T2, _} = statistics(wall_clock), - ok = dialyzer_timing:stop(), report_elapsed_time(T1, T2, Options), Return. diff --git a/lib/dialyzer/src/dialyzer_timing.erl b/lib/dialyzer/src/dialyzer_timing.erl index c2a7b3ecf8..d5e6d1476d 100644 --- a/lib/dialyzer/src/dialyzer_timing.erl +++ b/lib/dialyzer/src/dialyzer_timing.erl @@ -32,10 +32,6 @@ init(Active) -> Pid = spawn_link(fun() -> loop_init(Active) end), - case whereis(?MODULE) of - undefined -> ok; - _ -> unregister(?MODULE) - end, register(?MODULE, Pid), ok. @@ -72,6 +68,8 @@ loop(LastNow, Size, Unit) -> loop(LastNow, NewSize, NewUnit); {Pid, stop, Now} -> io:format(" ~-9s (+~5.2fs)\n", ["",diff(Now, LastNow)]), + Pid ! ok; + {Pid, stop} -> Pid ! ok end. -- cgit v1.2.3 From 720b65deff021ddb17aaa125046f97ff13ade883 Mon Sep 17 00:00:00 2001 From: Stavros Aronis Date: Tue, 20 Mar 2012 12:07:42 +0100 Subject: Regulate all kinds of running workers up to the number of schedulers --- lib/dialyzer/src/dialyzer_callgraph.erl | 3 +- lib/dialyzer/src/dialyzer_coordinator.erl | 208 ++++++++++++++---------------- lib/dialyzer/src/dialyzer_typesig.erl | 2 +- lib/dialyzer/src/dialyzer_utils.erl | 12 +- lib/dialyzer/src/dialyzer_worker.erl | 37 +++++- 5 files changed, 145 insertions(+), 117 deletions(-) (limited to 'lib/dialyzer') diff --git a/lib/dialyzer/src/dialyzer_callgraph.erl b/lib/dialyzer/src/dialyzer_callgraph.erl index 4ff9f5920b..64e0ee88af 100644 --- a/lib/dialyzer/src/dialyzer_callgraph.erl +++ b/lib/dialyzer/src/dialyzer_callgraph.erl @@ -265,8 +265,7 @@ module_postorder(#callgraph{digraph = DG}) -> digraph_confirm_vertices(sets:to_list(Nodes), MDG), Foreach = fun({M1,M2}) -> digraph:add_edge(MDG, M1, M2) end, lists:foreach(Foreach, sets:to_list(Edges)), - PostOrder = digraph_utils:topsort(MDG), - {PostOrder, {'d', MDG}}. + {digraph_utils:topsort(MDG), {'d', MDG}}. edge_fold({{M1,_,_},{M2,_,_}}, Set) -> case M1 =/= M2 of diff --git a/lib/dialyzer/src/dialyzer_coordinator.erl b/lib/dialyzer/src/dialyzer_coordinator.erl index 8ebbb11137..e81da5c456 100644 --- a/lib/dialyzer/src/dialyzer_coordinator.erl +++ b/lib/dialyzer/src/dialyzer_coordinator.erl @@ -32,18 +32,18 @@ -export([wait_activation/0, job_done/3]). %%% Exports for the typesig and dataflow analysis workers --export([sccs_to_pids/1]). +-export([sccs_to_pids/1, request_activation/1]). %%% Exports for the compilation workers -export([get_next_label/2]). --export_type([coordinator/0, mode/0, init_data/0]). +-export_type([coordinator/0, mode/0, init_data/0, result/0]). %%-------------------------------------------------------------------- -define(MAP, dialyzer_coordinator_map). --type coordinator() :: pid(). %%opaque +-type coordinator() :: {pid(), pid()}. %%opaque -type scc() :: [mfa_or_funlbl()]. -type mode() :: 'typesig' | 'dataflow' | 'compile' | 'warnings'. @@ -73,12 +73,12 @@ -type job_result() :: dialyzer_analysis_callgraph:one_file_result() | typesig_result() | dataflow_result() | warnings_result(). --record(state, {active = 0 :: integer(), - result :: result(), - next_label = 0 :: integer(), - tickets = 0 :: integer(), - queue = queue:new() :: queue(), - init_data :: init_data() +-record(state, {mode :: mode(), + active = 0 :: integer(), + result :: result(), + next_label = 0 :: integer(), + init_data :: init_data(), + regulator :: pid() }). -include("dialyzer.hrl"). @@ -96,127 +96,79 @@ parallel_job(Mode, Jobs, InitData) -> State = spawn_jobs(Mode, Jobs, InitData), - collect_result(Mode, State). - -spawn_jobs(Mode, Jobs, InitData) when - Mode =:= 'typesig'; Mode =:= 'dataflow' -> - Coordinator = self(), - ?MAP = ets:new(?MAP, [named_table, {read_concurrency, true}]), + collect_result(State). + +spawn_jobs(Mode, Jobs, InitData) -> + Collector = self(), + Regulator = spawn_regulator(), + Coordinator = {Collector, Regulator}, + TypesigOrDataflow = (Mode =:= 'typesig') orelse (Mode =:= 'dataflow'), + case TypesigOrDataflow of + true -> + ?MAP = ets:new(?MAP, [named_table, {read_concurrency, true}]); + false -> ok + end, Fold = fun(Job, Count) -> Pid = dialyzer_worker:launch(Mode, Job, InitData, Coordinator), - true = ets:insert(?MAP, {Job, Pid}), + case TypesigOrDataflow of + true -> true = ets:insert(?MAP, {Job, Pid}); + false -> request_activation(Regulator, Pid) + end, Count + 1 end, JobCount = lists:foldl(Fold, 0, Jobs), Unit = case Mode of 'typesig' -> "SCCs"; - 'dataflow' -> "modules" + _ -> "modules" end, dialyzer_timing:send_size_info(JobCount, Unit), - #state{active = JobCount, result = [], init_data = InitData}; -spawn_jobs(Mode, Jobs, InitData) when - Mode =:= 'compile'; Mode =:= 'warnings' -> - Coordinator = self(), - Fold = - fun(Job, {InTickets, InQueue, Count}) -> - Pid = dialyzer_worker:launch(Mode, Job, InitData, Coordinator), - NewCount = Count + 1, - case InTickets of - 0 -> {InTickets, queue:in(Pid, InQueue), NewCount}; - N -> activate_pid(Pid), {N-1, InQueue, NewCount} - end - end, - CPUs = erlang:system_info(logical_processors_available), - InitTickets = 4*CPUs, - {Tickets, Queue, JobCount} = - lists:foldl(Fold, {InitTickets, queue:new(), 0}, Jobs), - dialyzer_timing:send_size_info(JobCount, "modules"), InitResult = case Mode of - 'warnings' -> []; - 'compile' -> dialyzer_analysis_callgraph:compile_init_result() + 'compile' -> dialyzer_analysis_callgraph:compile_init_result(); + _ -> [] end, - #state{active = JobCount, result = InitResult, next_label = 0, - tickets = Tickets, queue = Queue, init_data = InitData}. - -collect_result(Mode, State) -> - case Mode of - 'compile' -> compile_loop(State); - 'typesig' -> not_fixpoint_loop(State); - 'dataflow' -> not_fixpoint_loop(State); - 'warnings' -> warnings_loop(State) - end. + #state{mode = Mode, active = JobCount, result = InitResult, next_label = 0, + init_data = InitData, regulator = Regulator}. -compile_loop(#state{active = Active, result = Result, - next_label = NextLabel, tickets = Tickets, - queue = Queue, init_data = InitData} = State) -> +collect_result(#state{mode = Mode, active = Active, result = Result, + next_label = NextLabel, init_data = InitData, + regulator = Regulator} = State) -> receive {next_label_request, Estimation, Pid} -> Pid ! {next_label_reply, NextLabel}, - compile_loop(State#state{next_label = NextLabel + Estimation}); + collect_result(State#state{next_label = NextLabel + Estimation}); {done, Job, Data} -> - NewResult = - dialyzer_analysis_callgraph:add_to_result(Job, Data, Result, InitData), + NewResult = update_result(Mode, InitData, Job, Data, Result), case Active of 1 -> - {NewResult, NextLabel}; - _ -> - NewActive = Active - 1, - {NewQueue, NewTickets} = manage_waiting(Queue, Tickets), - NewState = - State#state{result = NewResult, active = NewActive, - queue = NewQueue, tickets = NewTickets}, - compile_loop(NewState) + kill_regulator(Regulator), + case Mode of + 'compile' -> + {NewResult, NextLabel}; + X when X =:= 'typesig'; X =:= 'dataflow' -> + ets:delete(?MAP), + NewResult; + 'warnings' -> + NewResult + end; + N -> + collect_result(State#state{result = NewResult, active = N - 1}) end end. -not_fixpoint_loop(#state{active = Active, result = Result, - init_data = InitData} = State) -> - receive - {done, _Job, Data} -> - FinalData = dialyzer_succ_typings:lookup_names(Data, InitData), - NewResult = FinalData ++ Result, - case Active of - 1 -> - ets:delete(?MAP), - NewResult; - _ -> - NewActive = Active - 1, - NewState = State#state{active = NewActive, result = NewResult}, - not_fixpoint_loop(NewState) - end - end. - -warnings_loop(#state{active = Active, result = Result, tickets = Tickets, - queue = Queue} = State) -> - receive - {done, _Job, Data} -> - NewResult = Data ++ Result, - case Active of - 1 -> NewResult; - _ -> - NewActive = Active - 1, - {NewQueue, NewTickets} = manage_waiting(Queue, Tickets), - NewState = - State#state{result = NewResult, active = NewActive, - queue = NewQueue, tickets = NewTickets}, - warnings_loop(NewState) - end +update_result(Mode, InitData, Job, Data, Result) -> + case Mode of + 'compile' -> + dialyzer_analysis_callgraph:add_to_result(Job, Data, Result, + InitData); + X when X =:= 'typesig'; X =:= 'dataflow' -> + dialyzer_succ_typings:lookup_names(Data, InitData) ++ Result; + 'warnings' -> + Data ++ Result end. -manage_waiting(Queue, Tickets) -> - {Waiting, NewQueue} = queue:out(Queue), - NewTickets = - case Waiting of - empty -> Tickets + 1; - {value, Pid} -> - activate_pid(Pid), - Tickets - end, - {NewQueue, NewTickets}. - -spec sccs_to_pids([scc() | module()]) -> {[dialyzer_worker:worker()], [scc() | module()]}. @@ -232,14 +184,15 @@ pid_partition(SCC, {Pids, Unknown}) -> -spec job_done(job(), job_result(), coordinator()) -> ok. -job_done(Job, Result, Coordinator) -> - Coordinator ! {done, Job, Result}, +job_done(Job, Result, {Collector, Regulator}) -> + Regulator ! done, + Collector ! {done, Job, Result}, ok. -spec get_next_label(integer(), coordinator()) -> integer(). -get_next_label(EstimatedSize, Coordinator) -> - Coordinator ! {next_label_request, EstimatedSize, self()}, +get_next_label(EstimatedSize, {Collector, _Regulator}) -> + Collector ! {next_label_request, EstimatedSize, self()}, receive {next_label_reply, NextLabel} -> NextLabel end. @@ -251,3 +204,42 @@ wait_activation() -> activate_pid(Pid) -> Pid ! activate. + +-spec request_activation(coordinator()) -> ok. + +request_activation({_Collector, Regulator}) -> + Regulator ! {req, self()}, + wait_activation(). + +request_activation(Regulator, Pid) -> + Regulator ! {req, Pid}. + +spawn_regulator() -> + InitTickets = dialyzer_utils:parallelism(), + spawn_link(fun() -> regulator_loop(InitTickets, queue:new()) end). + +regulator_loop(Tickets, Queue) -> + receive + {req, Pid} -> + case Tickets of + 0 -> + regulator_loop(0, queue:in(Pid, Queue)); + N -> + activate_pid(Pid), + regulator_loop(N-1, Queue) + end; + done -> + {Waiting, NewQueue} = queue:out(Queue), + NewTickets = + case Waiting of + empty -> Tickets + 1; + {value, Pid} -> + activate_pid(Pid), + Tickets + end, + regulator_loop(NewTickets, NewQueue); + stop -> ok + end. + +kill_regulator(Regulator) -> + Regulator ! stop. diff --git a/lib/dialyzer/src/dialyzer_typesig.erl b/lib/dialyzer/src/dialyzer_typesig.erl index 3f73afb971..6ee1795fc5 100644 --- a/lib/dialyzer/src/dialyzer_typesig.erl +++ b/lib/dialyzer/src/dialyzer_typesig.erl @@ -1743,7 +1743,7 @@ parallel_split(SCC) -> case Length > 2*?worth_it of false -> false; true -> - case min(erlang:system_info(logical_processors_available), 8) of + case min(dialyzer_utils:parallelism(), 8) of 1 -> false; CPUs -> FullShare = Length div CPUs + 1, diff --git a/lib/dialyzer/src/dialyzer_utils.erl b/lib/dialyzer/src/dialyzer_utils.erl index 2a248fb028..149e777e1f 100644 --- a/lib/dialyzer/src/dialyzer_utils.erl +++ b/lib/dialyzer/src/dialyzer_utils.erl @@ -43,7 +43,8 @@ pp_hook/0, process_record_remote_types/1, sets_filter/2, - src_compiler_opts/0 + src_compiler_opts/0, + parallelism/0 ]). -include("dialyzer.hrl"). @@ -536,3 +537,12 @@ pp_unit(Unit, Ctxt, Cont) -> pp_atom(Atom) -> String = atom_to_list(cerl:atom_val(Atom)), prettypr:text(String). + +%%------------------------------------------------------------------------------ + +-spec parallelism() -> integer(). + +parallelism() -> + CPUs = erlang:system_info(logical_processors_available), + Schedulers = erlang:system_info(schedulers), + min(CPUs, Schedulers). diff --git a/lib/dialyzer/src/dialyzer_worker.erl b/lib/dialyzer/src/dialyzer_worker.erl index 453bb76dfc..cccf1d144b 100644 --- a/lib/dialyzer/src/dialyzer_worker.erl +++ b/lib/dialyzer/src/dialyzer_worker.erl @@ -20,15 +20,16 @@ -module(dialyzer_worker). --export([launch/4]). +-export([launch/4, sequential/4]). -export_type([worker/0]). -type worker() :: pid(). %%opaque --type mode() :: dialyzer_coordinator:mode(). +-type mode() :: dialyzer_coordinator:mode(). -type coordinator() :: dialyzer_coordinator:coordinator(). --type init_data() :: dialyzer_coordinator:init_data(). +-type init_data() :: dialyzer_coordinator:init_data(). +-type result() :: dialyzer_coordinator:result(). -record(state, { mode :: mode(), @@ -55,7 +56,7 @@ launch(Mode, Job, InitData, Coordinator) -> State = #state{mode = Mode, job = Job, - init_data = InitData, + init_data = InitData, coordinator = Coordinator}, InitState = case Mode of @@ -89,7 +90,7 @@ loop(running, #state{mode = 'compile'} = State) -> case start_compilation(State) of {ok, EstimatedSize, Data} -> Label = ask_coordinator_for_label(EstimatedSize, State), - dialyzer_analysis_callgraph:continue_compilation(Label, Data); + continue_compilation(Label, Data); {error, _Reason} = Error -> Error end, @@ -101,6 +102,7 @@ loop(running, #state{mode = 'warnings'} = State) -> report_to_coordinator(Result, State); loop(running, #state{mode = Mode} = State) when Mode =:= 'typesig'; Mode =:= 'dataflow' -> + request_activation(State), ?debug("Run: ~p\n",[State#state.job]), NotFixpoint = do_work(State), ok = broadcast_done(State), @@ -140,6 +142,9 @@ wait_for_success_typings(#state{depends_on = DependsOn} = State) -> State end. +request_activation(#state{coordinator = Coordinator}) -> + dialyzer_coordinator:request_activation(Coordinator). + do_work(#state{mode = Mode, job = Job, init_data = InitData}) -> case Mode of typesig -> dialyzer_succ_typings:find_succ_types_for_scc(Job, InitData); @@ -156,5 +161,27 @@ start_compilation(#state{job = Job, init_data = InitData}) -> ask_coordinator_for_label(EstimatedSize, #state{coordinator = Coordinator}) -> dialyzer_coordinator:get_next_label(EstimatedSize, Coordinator). +continue_compilation(Label, Data) -> + dialyzer_analysis_callgraph:continue_compilation(Label, Data). + collect_warnings(#state{job = Job, init_data = InitData}) -> dialyzer_succ_typings:collect_warnings(Job, InitData). + +%%------------------------------------------------------------------------------ + +-type extra() :: label() | 'unused'. + +-spec sequential(mode(), [mfa_or_funlbl()], init_data(), extra()) -> result(). + +sequential('compile', Job, InitData, Extra) -> + case dialyzer_analysis_callgraph:start_compilation(Job, InitData) of + {ok, EstimatedSize, Data} -> + {EstimatedSize, continue_compilation(Extra, Data)}; + {error, _Reason} = Error -> {0, Error} + end; +sequential('typesig', Job, InitData, _Extra) -> + dialyzer_succ_typings:find_succ_types_for_scc(Job, InitData); +sequential('dataflow', Job, InitData, _Extra) -> + dialyzer_succ_typings:refine_one_module(Job, InitData); +sequential('warnings', Job, InitData, _Extra) -> + dialyzer_succ_typings:collect_warnings(Job, InitData). -- cgit v1.2.3 From 5c52ff6b5f10c7bd9ce06cdf607e88035c16e079 Mon Sep 17 00:00:00 2001 From: Stavros Aronis Date: Thu, 29 Mar 2012 14:44:08 +0200 Subject: Anonymous time server --- lib/dialyzer/src/dialyzer.hrl | 9 +-- lib/dialyzer/src/dialyzer_analysis_callgraph.erl | 42 ++++++++------ lib/dialyzer/src/dialyzer_coordinator.erl | 23 ++++---- lib/dialyzer/src/dialyzer_succ_typings.erl | 71 +++++++++++++----------- lib/dialyzer/src/dialyzer_timing.erl | 51 ++++++++--------- 5 files changed, 105 insertions(+), 91 deletions(-) (limited to 'lib/dialyzer') diff --git a/lib/dialyzer/src/dialyzer.hrl b/lib/dialyzer/src/dialyzer.hrl index ebfc6b9731..70ee408f12 100644 --- a/lib/dialyzer/src/dialyzer.hrl +++ b/lib/dialyzer/src/dialyzer.hrl @@ -128,6 +128,7 @@ race_detection = false :: boolean(), behaviours_chk = false :: boolean(), timing = false :: boolean(), + timing_server :: dialyzer_timing:timing_server(), callgraph_file = "" :: file:filename()}). -record(options, {files = [] :: [file:filename()], @@ -156,11 +157,11 @@ %%-------------------------------------------------------------------- --define(timing(Msg,Var,Expr), +-define(timing(Server, Msg, Var, Expr), begin - dialyzer_timing:start_stamp(Msg), + dialyzer_timing:start_stamp(Server, Msg), Var = Expr, - dialyzer_timing:end_stamp(), + dialyzer_timing:end_stamp(Server), Var end). --define(timing(Msg,Expr),?timing(Msg,_T,Expr)). +-define(timing(Server, Msg, Expr),?timing(Server, Msg, _T, Expr)). diff --git a/lib/dialyzer/src/dialyzer_analysis_callgraph.erl b/lib/dialyzer/src/dialyzer_analysis_callgraph.erl index 641b7eb22a..3bbde12481 100644 --- a/lib/dialyzer/src/dialyzer_analysis_callgraph.erl +++ b/lib/dialyzer/src/dialyzer_analysis_callgraph.erl @@ -52,7 +52,8 @@ parent :: pid(), plt :: dialyzer_plt:plt(), start_from = byte_code :: start_from(), - use_contracts = true :: boolean() + use_contracts = true :: boolean(), + timing_server :: dialyzer_timing:timing_server() }). -record(server_state, {parent :: pid(), legal_warnings :: [dial_warn_tag()]}). @@ -64,14 +65,15 @@ -spec start(pid(), [dial_warn_tag()], #analysis{}) -> 'ok'. start(Parent, LegalWarnings, Analysis) -> - dialyzer_timing:init(Analysis#analysis.timing), + TimingServer = dialyzer_timing:init(Analysis#analysis.timing), RacesOn = ordsets:is_element(?WARN_RACE_CONDITION, LegalWarnings), - Analysis0 = Analysis#analysis{race_detection = RacesOn}, + Analysis0 = + Analysis#analysis{race_detection = RacesOn, timing_server = TimingServer}, Analysis1 = expand_files(Analysis0), Analysis2 = run_analysis(Analysis1), State = #server_state{parent = Parent, legal_warnings = LegalWarnings}, loop(State, Analysis2, none), - dialyzer_timing:stop(). + dialyzer_timing:stop(TimingServer). run_analysis(Analysis) -> Self = self(), @@ -133,7 +135,8 @@ analysis_start(Parent, Analysis) -> plt = Plt, parent = Parent, start_from = Analysis#analysis.start_from, - use_contracts = Analysis#analysis.use_contracts + use_contracts = Analysis#analysis.use_contracts, + timing_server = Analysis#analysis.timing_server }, Files = ordsets:from_list(Analysis#analysis.files), {Callgraph, NoWarn, TmpCServer0} = compile_and_store(Files, State), @@ -157,7 +160,7 @@ analysis_start(Parent, Analysis) -> dialyzer_codeserver:insert_temp_exported_types(MergedExpTypes, TmpCServer1), TmpCServer3 = dialyzer_utils:process_record_remote_types(TmpCServer2), - ?timing("remote", + ?timing(State#analysis_state.timing_server, "remote", dialyzer_contracts:process_contract_remote_types(TmpCServer3)) catch throw:{error, _ErrorMsg} = Error -> exit(Error) @@ -186,22 +189,23 @@ analysis_start(Parent, Analysis) -> send_codeserver_plt(Parent, CServer, State3#analysis_state.plt), send_analysis_done(Parent, Plt2, State3#analysis_state.doc_plt). -analyze_callgraph(Callgraph, State) -> - Codeserver = State#analysis_state.codeserver, - Parent = State#analysis_state.parent, - DocPlt = State#analysis_state.doc_plt, +analyze_callgraph(Callgraph, #analysis_state{codeserver = Codeserver, + doc_plt = DocPlt, + timing_server = TimingServer, + parent = Parent} = State) -> Plt = dialyzer_plt:insert_callbacks(State#analysis_state.plt, Codeserver), {NewPlt, NewDocPlt} = case State#analysis_state.analysis_type of plt_build -> - {dialyzer_succ_typings:analyze_callgraph(Callgraph, Plt, - Codeserver, Parent), - DocPlt}; + NewPlt0 = + dialyzer_succ_typings:analyze_callgraph(Callgraph, Plt, Codeserver, + TimingServer, Parent), + {NewPlt0, DocPlt}; succ_typings -> NoWarn = State#analysis_state.no_warn_unused, {Warnings, NewPlt0, NewDocPlt0} = - dialyzer_succ_typings:get_warnings(Callgraph, Plt, DocPlt, - Codeserver, NoWarn, Parent), + dialyzer_succ_typings:get_warnings(Callgraph, Plt, DocPlt, Codeserver, + NoWarn, TimingServer, Parent), send_warnings(State#analysis_state.parent, Warnings), {NewPlt0, NewDocPlt0} end, @@ -234,14 +238,16 @@ make_compile_init(#analysis_state{codeserver = Codeserver, start_from = StartFrom}. compile_and_store(Files, #analysis_state{codeserver = CServer, + timing_server = Timing, parent = Parent} = State) -> send_log(Parent, "Reading files and computing callgraph... "), {T1, _} = statistics(runtime), Callgraph = dialyzer_callgraph:new(), CompileInit = make_compile_init(State, Callgraph), {{Failed, NoWarn, Modules}, NextLabel} = - ?timing("compile", - dialyzer_coordinator:parallel_job(compile, Files, CompileInit)), + ?timing(Timing, "compile", _C1, + dialyzer_coordinator:parallel_job(compile, Files, + CompileInit, Timing)), CServer2 = dialyzer_codeserver:set_next_core_label(NextLabel, CServer), case Failed =:= [] of true -> @@ -259,7 +265,7 @@ compile_and_store(Files, #analysis_state{codeserver = CServer, Msg1 = io_lib:format("done in ~.2f secs\nRemoving edges... ", [(T2-T1)/1000]), send_log(Parent, Msg1), Callgraph = - ?timing("clean", _C3, + ?timing(Timing, "clean", _C2, cleanup_callgraph(State, CServer2, Callgraph, Modules)), {T3, _} = statistics(runtime), Msg2 = io_lib:format("done in ~.2f secs\n", [(T3-T2)/1000]), diff --git a/lib/dialyzer/src/dialyzer_coordinator.erl b/lib/dialyzer/src/dialyzer_coordinator.erl index e81da5c456..b91fc95959 100644 --- a/lib/dialyzer/src/dialyzer_coordinator.erl +++ b/lib/dialyzer/src/dialyzer_coordinator.erl @@ -26,7 +26,7 @@ -module(dialyzer_coordinator). %%% Export for dialyzer main process --export([parallel_job/3]). +-export([parallel_job/4]). %%% Exports for all possible workers -export([wait_activation/0, job_done/3]). @@ -44,6 +44,7 @@ -define(MAP, dialyzer_coordinator_map). -type coordinator() :: {pid(), pid()}. %%opaque +-type timing() :: dialyzer_timing:timing_server(). -type scc() :: [mfa_or_funlbl()]. -type mode() :: 'typesig' | 'dataflow' | 'compile' | 'warnings'. @@ -85,20 +86,20 @@ %%-------------------------------------------------------------------- --spec parallel_job('compile', compile_jobs(), compile_init_data()) -> +-spec parallel_job('compile', compile_jobs(), compile_init_data(), timing()) -> {compile_result(), integer()}; - ('typesig', typesig_jobs(), typesig_init_data()) -> + ('typesig', typesig_jobs(), typesig_init_data(), timing()) -> typesig_result(); - ('dataflow', dataflow_jobs(), dataflow_init_data()) -> - dataflow_result(); - ('warnings', warnings_jobs(), warnings_init_data()) -> - warnings_result(). + ('dataflow', dataflow_jobs(), dataflow_init_data(), + timing()) -> dataflow_result(); + ('warnings', warnings_jobs(), warnings_init_data(), + timing()) -> warnings_result(). -parallel_job(Mode, Jobs, InitData) -> - State = spawn_jobs(Mode, Jobs, InitData), +parallel_job(Mode, Jobs, InitData, Timing) -> + State = spawn_jobs(Mode, Jobs, InitData, Timing), collect_result(State). -spawn_jobs(Mode, Jobs, InitData) -> +spawn_jobs(Mode, Jobs, InitData, Timing) -> Collector = self(), Regulator = spawn_regulator(), Coordinator = {Collector, Regulator}, @@ -123,7 +124,7 @@ spawn_jobs(Mode, Jobs, InitData) -> 'typesig' -> "SCCs"; _ -> "modules" end, - dialyzer_timing:send_size_info(JobCount, Unit), + dialyzer_timing:send_size_info(Timing, JobCount, Unit), InitResult = case Mode of 'compile' -> dialyzer_analysis_callgraph:compile_init_result(); diff --git a/lib/dialyzer/src/dialyzer_succ_typings.erl b/lib/dialyzer/src/dialyzer_succ_typings.erl index 9f61f9ff6f..9ca5a66dab 100644 --- a/lib/dialyzer/src/dialyzer_succ_typings.erl +++ b/lib/dialyzer/src/dialyzer_succ_typings.erl @@ -28,8 +28,8 @@ -module(dialyzer_succ_typings). -export([analyze_callgraph/3, - analyze_callgraph/4, - get_warnings/6 + analyze_callgraph/5, + get_warnings/7 ]). -export([ @@ -74,6 +74,7 @@ codeserver :: dialyzer_codeserver:codeserver(), no_warn_unused :: set(), parent = none :: parent(), + timing_server :: dialyzer_timing:timing_server(), plt :: dialyzer_plt:plt()}). %%-------------------------------------------------------------------- @@ -83,33 +84,38 @@ dialyzer_plt:plt(). analyze_callgraph(Callgraph, Plt, Codeserver) -> - analyze_callgraph(Callgraph, Plt, Codeserver, none). + analyze_callgraph(Callgraph, Plt, Codeserver, none, none). -spec analyze_callgraph(dialyzer_callgraph:callgraph(), dialyzer_plt:plt(), - dialyzer_codeserver:codeserver(), parent()) -> + dialyzer_codeserver:codeserver(), + dialyzer_timing:timing_server(), parent()) -> dialyzer_plt:plt(). -analyze_callgraph(Callgraph, Plt, Codeserver, Parent) -> +analyze_callgraph(Callgraph, Plt, Codeserver, TimingServer, Parent) -> NewState = - init_state_and_get_success_typings(Callgraph, Plt, Codeserver, Parent), + init_state_and_get_success_typings(Callgraph, Plt, Codeserver, + TimingServer, Parent), dialyzer_plt:restore_full_plt(NewState#st.plt, Plt). %%-------------------------------------------------------------------- -init_state_and_get_success_typings(Callgraph, Plt, Codeserver, Parent) -> +init_state_and_get_success_typings(Callgraph, Plt, Codeserver, + TimingServer, Parent) -> {SCCs, Callgraph1} = - ?timing("order", dialyzer_callgraph:finalize(Callgraph)), + ?timing(TimingServer, "order", dialyzer_callgraph:finalize(Callgraph)), State = #st{callgraph = Callgraph1, plt = dialyzer_plt:get_mini_plt(Plt), - codeserver = Codeserver, parent = Parent}, + codeserver = Codeserver, parent = Parent, + timing_server = TimingServer}, get_refined_success_typings(SCCs, State). -get_refined_success_typings(SCCs, #st{callgraph = Callgraph} = State) -> +get_refined_success_typings(SCCs, #st{callgraph = Callgraph, + timing_server = TimingServer} = State) -> case find_succ_typings(SCCs, State) of {fixpoint, State1} -> State1; {not_fixpoint, NotFixpoint1, State1} -> {ModulePostorder, ModCallgraph} = ?timing( - "order", _C1, + TimingServer, "order", _C1, dialyzer_callgraph:module_postorder_from_funs(NotFixpoint1, Callgraph)), ModState = State1#st{callgraph = ModCallgraph}, @@ -119,7 +125,7 @@ get_refined_success_typings(SCCs, #st{callgraph = Callgraph} = State) -> {not_fixpoint, NotFixpoint2, State2} -> %% Need to reset the callgraph. {NewSCCs, Callgraph2} = - ?timing("order", _C2, + ?timing(TimingServer, "order", _C2, dialyzer_callgraph:reset_from_funs(NotFixpoint2, ModCallgraph)), NewState = State2#st{callgraph = Callgraph2}, @@ -130,12 +136,14 @@ get_refined_success_typings(SCCs, #st{callgraph = Callgraph} = State) -> -type doc_plt() :: 'undefined' | dialyzer_plt:plt(). -spec get_warnings(dialyzer_callgraph:callgraph(), dialyzer_plt:plt(), doc_plt(), dialyzer_codeserver:codeserver(), set(), - pid()) -> + dialyzer_timing:timing_server(), pid()) -> {[dial_warning()], dialyzer_plt:plt(), doc_plt()}. -get_warnings(Callgraph, Plt, DocPlt, Codeserver, NoWarnUnused, Parent) -> +get_warnings(Callgraph, Plt, DocPlt, Codeserver, + NoWarnUnused, TimingServer, Parent) -> InitState = - init_state_and_get_success_typings(Callgraph, Plt, Codeserver, Parent), + init_state_and_get_success_typings(Callgraph, Plt, Codeserver, + TimingServer, Parent), NewState = InitState#st{no_warn_unused = NoWarnUnused}, Mods = dialyzer_callgraph:modules(NewState#st.callgraph), MiniPlt = NewState#st.plt, @@ -143,22 +151,22 @@ get_warnings(Callgraph, Plt, DocPlt, Codeserver, NoWarnUnused, Parent) -> dialyzer_contracts:get_invalid_contract_warnings(Mods, Codeserver, MiniPlt), MiniDocPlt = dialyzer_plt:get_mini_plt(DocPlt), ModWarns = - ?timing("warning", get_warnings_from_modules(Mods, NewState, MiniDocPlt)), + ?timing(TimingServer, "warning", + get_warnings_from_modules(Mods, NewState, MiniDocPlt)), {postprocess_warnings(CWarns ++ ModWarns, Codeserver), dialyzer_plt:restore_full_plt(MiniPlt, Plt), dialyzer_plt:restore_full_plt(MiniDocPlt, DocPlt)}. get_warnings_from_modules(Mods, State, DocPlt) -> #st{callgraph = Callgraph, codeserver = Codeserver, - no_warn_unused = NoWarnUnused, plt = Plt} = State, - Init = {Callgraph, Codeserver, NoWarnUnused, Plt, DocPlt}, - dialyzer_coordinator:parallel_job(warnings, Mods, Init). + no_warn_unused = NoWarnUnused, plt = Plt, + timing_server = TimingServer} = State, + Init = {Codeserver, Callgraph, NoWarnUnused, Plt, DocPlt}, + dialyzer_coordinator:parallel_job(warnings, Mods, Init, TimingServer). --type warning_servers() :: term(). +-spec collect_warnings(module(), warnings_init_data()) -> [dial_warning()]. --spec collect_warnings(module(), warning_servers()) -> [dial_warning()]. - -collect_warnings(M, {Callgraph, Codeserver, NoWarnUnused, Plt, DocPlt}) -> +collect_warnings(M, {Codeserver, Callgraph, NoWarnUnused, Plt, DocPlt}) -> ModCode = dialyzer_codeserver:lookup_mod_code(M, Codeserver), Records = dialyzer_codeserver:lookup_mod_records(M, Codeserver), Contracts = dialyzer_codeserver:lookup_mod_contracts(M, Codeserver), @@ -212,13 +220,14 @@ postprocess_dataflow_warns([{?WARN_CONTRACT_RANGE, {CallF, CallL}, Msg}|Rest], end. refine_succ_typings(Modules, #st{codeserver = Codeserver, - callgraph = Callgraph, - plt = Plt} = State) -> - ?debug("Module postorder: ~p\n", [ModulePostorder]), + callgraph = Callgraph, + plt = Plt, + timing_server = Timing} = State) -> + ?debug("Module postorder: ~p\n", [Modules]), Init = {Codeserver, Callgraph, Plt}, NotFixpoint = - ?timing("refine", - dialyzer_coordinator:parallel_job(dataflow, Modules, Init)), + ?timing(Timing, "refine", + dialyzer_coordinator:parallel_job(dataflow, Modules, Init, Timing)), ?debug("==================== Dataflow done ====================\n\n", []), case NotFixpoint =:= [] of true -> {fixpoint, State}; @@ -313,11 +322,11 @@ compare_types_1([], [], _Strict, NotFixpoint) -> end. find_succ_typings(SCCs, #st{codeserver = Codeserver, callgraph = Callgraph, - plt = Plt} = State) -> + plt = Plt, timing_server = Timing} = State) -> Init = {Codeserver, Callgraph, Plt}, NotFixpoint = - ?timing("typesig", - dialyzer_coordinator:parallel_job(typesig, SCCs, Init)), + ?timing(Timing, "typesig", + dialyzer_coordinator:parallel_job(typesig, SCCs, Init, Timing)), ?debug("==================== Typesig done ====================\n\n", []), case NotFixpoint =:= [] of true -> {fixpoint, State}; diff --git a/lib/dialyzer/src/dialyzer_timing.erl b/lib/dialyzer/src/dialyzer_timing.erl index d5e6d1476d..15d8795a37 100644 --- a/lib/dialyzer/src/dialyzer_timing.erl +++ b/lib/dialyzer/src/dialyzer_timing.erl @@ -26,27 +26,20 @@ -module(dialyzer_timing). --export([init/1, start_stamp/1, send_size_info/2, end_stamp/0, stop/0]). +-export([init/1, start_stamp/2, send_size_info/3, end_stamp/1, stop/1]). --spec init(boolean()) -> ok. +-export_type([timing_server/0]). -init(Active) -> - Pid = spawn_link(fun() -> loop_init(Active) end), - register(?MODULE, Pid), - ok. +-type timing_server() :: pid() | 'none'. -loop_init(Active) -> +-spec init(boolean()) -> timing_server(). + +init(Active) -> case Active of true -> io:format("\n"), - loop(now(), 0, ""); - false -> dummy_loop() - end. - -dummy_loop() -> - receive - {Pid, stop, _Now} -> Pid ! ok; - _ -> dummy_loop() + spawn_link(fun() -> loop(now(), 0, "") end); + false -> none end. loop(LastNow, Size, Unit) -> @@ -73,28 +66,32 @@ loop(LastNow, Size, Unit) -> Pid ! ok end. --spec start_stamp(string()) -> ok. +-spec start_stamp(timing_server(), string()) -> ok. -start_stamp(Msg) -> - ?MODULE ! {stamp, Msg, now()}, +start_stamp(none, _) -> ok; +start_stamp(Pid, Msg) -> + Pid ! {stamp, Msg, now()}, ok. --spec end_stamp() -> ok. +-spec end_stamp(timing_server()) -> ok. -end_stamp() -> - ?MODULE ! {stamp, now()}, +end_stamp(none) -> ok; +end_stamp(Pid) -> + Pid ! {stamp, now()}, ok. --spec send_size_info(integer(), string()) -> ok. +-spec send_size_info(timing_server(), integer(), string()) -> ok. -send_size_info(Size, Unit) -> - ?MODULE ! {size, Size, Unit}, +send_size_info(none, _, _) -> ok; +send_size_info(Pid, Size, Unit) -> + Pid ! {size, Size, Unit}, ok. --spec stop() -> ok. +-spec stop(timing_server()) -> ok. -stop() -> - ?MODULE ! {self(), stop, now()}, +stop(none) -> ok; +stop(Pid) -> + Pid ! {self(), stop, now()}, receive ok -> ok end. diff(T2, T1) -> -- cgit v1.2.3 From 12b8ce08ece794e677fdd148723fbe0a707bef6f Mon Sep 17 00:00:00 2001 From: Stavros Aronis Date: Thu, 29 Mar 2012 15:06:47 +0200 Subject: Anonymous SCCtoPID ETS table --- lib/dialyzer/src/dialyzer_coordinator.erl | 58 +++++++++++++++++-------------- lib/dialyzer/src/dialyzer_worker.erl | 16 +++++---- 2 files changed, 40 insertions(+), 34 deletions(-) (limited to 'lib/dialyzer') diff --git a/lib/dialyzer/src/dialyzer_coordinator.erl b/lib/dialyzer/src/dialyzer_coordinator.erl index b91fc95959..5719132215 100644 --- a/lib/dialyzer/src/dialyzer_coordinator.erl +++ b/lib/dialyzer/src/dialyzer_coordinator.erl @@ -32,7 +32,7 @@ -export([wait_activation/0, job_done/3]). %%% Exports for the typesig and dataflow analysis workers --export([sccs_to_pids/1, request_activation/1]). +-export([sccs_to_pids/2, request_activation/1]). %%% Exports for the compilation workers -export([get_next_label/2]). @@ -41,9 +41,11 @@ %%-------------------------------------------------------------------- --define(MAP, dialyzer_coordinator_map). +-type collector() :: pid(). +-type regulator() :: pid(). +-type scc_to_pid() :: ets:tid() | 'unused'. --type coordinator() :: {pid(), pid()}. %%opaque +-type coordinator() :: {collector(), regulator(), scc_to_pid()}. %%opaque -type timing() :: dialyzer_timing:timing_server(). -type scc() :: [mfa_or_funlbl()]. @@ -79,7 +81,8 @@ result :: result(), next_label = 0 :: integer(), init_data :: init_data(), - regulator :: pid() + regulator :: regulator(), + scc_to_pid :: scc_to_pid() }). -include("dialyzer.hrl"). @@ -102,18 +105,18 @@ parallel_job(Mode, Jobs, InitData, Timing) -> spawn_jobs(Mode, Jobs, InitData, Timing) -> Collector = self(), Regulator = spawn_regulator(), - Coordinator = {Collector, Regulator}, TypesigOrDataflow = (Mode =:= 'typesig') orelse (Mode =:= 'dataflow'), - case TypesigOrDataflow of - true -> - ?MAP = ets:new(?MAP, [named_table, {read_concurrency, true}]); - false -> ok - end, + SCCtoPID = + case TypesigOrDataflow of + true -> ets:new(scc_to_pid, [{read_concurrency, true}]); + false -> unused + end, + Coordinator = {Collector, Regulator, SCCtoPID}, Fold = fun(Job, Count) -> Pid = dialyzer_worker:launch(Mode, Job, InitData, Coordinator), case TypesigOrDataflow of - true -> true = ets:insert(?MAP, {Job, Pid}); + true -> true = ets:insert(SCCtoPID, {Job, Pid}); false -> request_activation(Regulator, Pid) end, Count + 1 @@ -131,11 +134,11 @@ spawn_jobs(Mode, Jobs, InitData, Timing) -> _ -> [] end, #state{mode = Mode, active = JobCount, result = InitResult, next_label = 0, - init_data = InitData, regulator = Regulator}. + init_data = InitData, regulator = Regulator, scc_to_pid = SCCtoPID}. collect_result(#state{mode = Mode, active = Active, result = Result, next_label = NextLabel, init_data = InitData, - regulator = Regulator} = State) -> + regulator = Regulator, scc_to_pid = SCCtoPID} = State) -> receive {next_label_request, Estimation, Pid} -> Pid ! {next_label_reply, NextLabel}, @@ -149,7 +152,7 @@ collect_result(#state{mode = Mode, active = Active, result = Result, 'compile' -> {NewResult, NextLabel}; X when X =:= 'typesig'; X =:= 'dataflow' -> - ets:delete(?MAP), + ets:delete(SCCtoPID), NewResult; 'warnings' -> NewResult @@ -170,29 +173,30 @@ update_result(Mode, InitData, Job, Data, Result) -> Data ++ Result end. --spec sccs_to_pids([scc() | module()]) -> +-spec sccs_to_pids([scc() | module()], coordinator()) -> {[dialyzer_worker:worker()], [scc() | module()]}. -sccs_to_pids(SCCs) -> - lists:foldl(fun pid_partition/2, {[], []}, SCCs). - -pid_partition(SCC, {Pids, Unknown}) -> - try ets:lookup_element(?MAP, SCC, 2) of - Result -> {[Result|Pids], Unknown} - catch - _:_ -> {Pids, [SCC|Unknown]} - end. +sccs_to_pids(SCCs, {_Collector, _Regulator, SCCtoPID}) -> + Fold = + fun(SCC, {Pids, Unknown}) -> + try ets:lookup_element(SCCtoPID, SCC, 2) of + Result -> {[Result|Pids], Unknown} + catch + _:_ -> {Pids, [SCC|Unknown]} + end + end, + lists:foldl(Fold, {[], []}, SCCs). -spec job_done(job(), job_result(), coordinator()) -> ok. -job_done(Job, Result, {Collector, Regulator}) -> +job_done(Job, Result, {Collector, Regulator, _SCCtoPID}) -> Regulator ! done, Collector ! {done, Job, Result}, ok. -spec get_next_label(integer(), coordinator()) -> integer(). -get_next_label(EstimatedSize, {Collector, _Regulator}) -> +get_next_label(EstimatedSize, {Collector, _Regulator, _SCCtoPID}) -> Collector ! {next_label_request, EstimatedSize, self()}, receive {next_label_reply, NextLabel} -> NextLabel @@ -208,7 +212,7 @@ activate_pid(Pid) -> -spec request_activation(coordinator()) -> ok. -request_activation({_Collector, Regulator}) -> +request_activation({_Collector, Regulator, _SCCtoPID}) -> Regulator ! {req, self()}, wait_activation(). diff --git a/lib/dialyzer/src/dialyzer_worker.erl b/lib/dialyzer/src/dialyzer_worker.erl index cccf1d144b..50b2e31ed8 100644 --- a/lib/dialyzer/src/dialyzer_worker.erl +++ b/lib/dialyzer/src/dialyzer_worker.erl @@ -111,25 +111,27 @@ loop(running, #state{mode = Mode} = State) when waits_more_success_typings(#state{depends_on = Depends}) -> Depends =/= []. -broadcast_done(#state{job = SCC, init_data = InitData}) -> +broadcast_done(#state{job = SCC, init_data = InitData, + coordinator = Coordinator}) -> RequiredBy = dialyzer_succ_typings:find_required_by(SCC, InitData), - {Callers, Unknown} = dialyzer_coordinator:sccs_to_pids(RequiredBy), + {Callers, Unknown} = + dialyzer_coordinator:sccs_to_pids(RequiredBy, Coordinator), send_done(Callers, SCC), - continue_broadcast_done(Unknown, SCC). + continue_broadcast_done(Unknown, SCC, Coordinator). send_done(Callers, SCC) -> ?debug("Sending ~p: ~p\n",[SCC, Callers]), SendSTFun = fun(PID) -> PID ! {done, SCC} end, lists:foreach(SendSTFun, Callers). -continue_broadcast_done([], _SCC) -> ok; -continue_broadcast_done(Rest, SCC) -> +continue_broadcast_done([], _SCC, _Coordinator) -> ok; +continue_broadcast_done(Rest, SCC, Coordinator) -> %% This time limit should be greater than the time required %% by the coordinator to spawn all processes. timer:sleep(500), - {Callers, Unknown} = dialyzer_coordinator:sccs_to_pids(Rest), + {Callers, Unknown} = dialyzer_coordinator:sccs_to_pids(Rest, Coordinator), send_done(Callers, SCC), - continue_broadcast_done(Unknown, SCC). + continue_broadcast_done(Unknown, SCC, Coordinator). wait_for_success_typings(#state{depends_on = DependsOn} = State) -> receive -- cgit v1.2.3 From 49c657461866f0fe87de2ee7578b46b1b926db10 Mon Sep 17 00:00:00 2001 From: Stavros Aronis Date: Wed, 28 Mar 2012 16:11:36 +0200 Subject: Logfile-like statistics (enabled with --resources) --- lib/dialyzer/src/dialyzer.hrl | 4 ++-- lib/dialyzer/src/dialyzer_cl_parse.erl | 4 ++++ lib/dialyzer/src/dialyzer_timing.erl | 37 +++++++++++++++++++++++++++++++++- 3 files changed, 42 insertions(+), 3 deletions(-) (limited to 'lib/dialyzer') diff --git a/lib/dialyzer/src/dialyzer.hrl b/lib/dialyzer/src/dialyzer.hrl index 70ee408f12..1b999a7b99 100644 --- a/lib/dialyzer/src/dialyzer.hrl +++ b/lib/dialyzer/src/dialyzer.hrl @@ -127,14 +127,14 @@ use_contracts = true :: boolean(), race_detection = false :: boolean(), behaviours_chk = false :: boolean(), - timing = false :: boolean(), + timing = false :: boolean() | 'debug', timing_server :: dialyzer_timing:timing_server(), callgraph_file = "" :: file:filename()}). -record(options, {files = [] :: [file:filename()], files_rec = [] :: [file:filename()], analysis_type = succ_typings :: anal_type1(), - timing = false :: boolean(), + timing = false :: boolean() | 'debug', defines = [] :: [dial_define()], from = byte_code :: start_from(), get_warnings = maybe :: boolean() | 'maybe', diff --git a/lib/dialyzer/src/dialyzer_cl_parse.erl b/lib/dialyzer/src/dialyzer_cl_parse.erl index 05e50c3d6b..205b97ccf9 100644 --- a/lib/dialyzer/src/dialyzer_cl_parse.erl +++ b/lib/dialyzer/src/dialyzer_cl_parse.erl @@ -167,6 +167,10 @@ cl(["--no_spec"|T]) -> cl(["--statistics"|T]) -> put(dialyzer_timing, true), cl(T); +cl(["--resources"|T]) -> + put(dialyzer_options_report_mode, quiet), + put(dialyzer_timing, debug), + cl(T); cl(["-v"|_]) -> io:format("Dialyzer version "++?VSN++"\n"), erlang:halt(?RET_NOTHING_SUSPICIOUS); diff --git a/lib/dialyzer/src/dialyzer_timing.erl b/lib/dialyzer/src/dialyzer_timing.erl index 15d8795a37..b1a4bdc07c 100644 --- a/lib/dialyzer/src/dialyzer_timing.erl +++ b/lib/dialyzer/src/dialyzer_timing.erl @@ -32,13 +32,16 @@ -type timing_server() :: pid() | 'none'. --spec init(boolean()) -> timing_server(). +-spec init(boolean() | 'debug') -> timing_server(). init(Active) -> case Active of true -> io:format("\n"), spawn_link(fun() -> loop(now(), 0, "") end); + debug -> + io:format("\n"), + spawn_link(fun() -> debug_loop("") end); false -> none end. @@ -66,6 +69,38 @@ loop(LastNow, Size, Unit) -> Pid ! ok end. +debug_loop(Phase) -> + receive + Message -> + {Runtime,_} = statistics(wall_clock), + Procs = erlang:system_info(process_count), + ProcMem = erlang:memory(total), + Status = io_lib:format("~12w ~6w ~20w", [Runtime, Procs, ProcMem]), + case Message of + {stamp, Msg, _Now} -> + io:format("~s ~s_start\n", [Status, Msg]), + debug_loop(Msg); + {stamp, _Now} -> + io:format("~s ~s_stop\n", [Status, Phase]), + debug_loop(""); + {Pid, stop, _Now} -> + Pid ! ok; + {Pid, stop} -> + Pid ! ok; + _ -> + debug_loop(Phase) + end + after + 50 -> + {Runtime,_} = statistics(wall_clock), + Procs = erlang:system_info(process_count), + ProcMem = erlang:memory(total), + Status = io_lib:format("~12w ~6w ~20w", [Runtime, Procs, ProcMem]), + io:format("~s\n", [Status]), + debug_loop(Phase) + end. + + -spec start_stamp(timing_server(), string()) -> ok. start_stamp(none, _) -> ok; -- cgit v1.2.3