From 5d9e51a4271833855519df37df8f964216a0e594 Mon Sep 17 00:00:00 2001 From: Hans Bolinder Date: Wed, 28 Dec 2016 19:22:42 +0100 Subject: dialyzer: Use less memory when translating contracts --- lib/dialyzer/src/dialyzer_codeserver.erl | 83 +++++++++++++++++--------------- lib/dialyzer/src/dialyzer_contracts.erl | 28 +++++------ 2 files changed, 58 insertions(+), 53 deletions(-) (limited to 'lib') diff --git a/lib/dialyzer/src/dialyzer_codeserver.erl b/lib/dialyzer/src/dialyzer_codeserver.erl index c05a7a075b..ba278b627a 100644 --- a/lib/dialyzer/src/dialyzer_codeserver.erl +++ b/lib/dialyzer/src/dialyzer_codeserver.erl @@ -29,7 +29,8 @@ -module(dialyzer_codeserver). -export([delete/1, - finalize_contracts/3, + store_temp_contracts/4, + finalize_contracts/1, finalize_exported_types/2, finalize_records/2, get_contracts/1, @@ -38,7 +39,9 @@ get_exports/1, get_records/1, get_next_core_label/1, - get_temp_contracts/1, + get_temp_contracts/2, + contracts_modules/1, + store_contracts/4, get_temp_exported_types/1, get_temp_records/1, insert/3, @@ -56,8 +59,7 @@ new/0, set_next_core_label/2, set_temp_records/2, - store_temp_records/3, - store_temp_contracts/4]). + store_temp_records/3]). -export_type([codeserver/0, fun_meta_info/0, contracts/0]). @@ -85,8 +87,8 @@ code :: dict_ets(), exported_types :: set_ets() | 'undefined', % set(mfa()) records :: map_ets() | 'undefined', - contracts :: map_ets() | 'undefined', - callbacks :: map_ets() | 'undefined', + contracts :: map_ets(), + callbacks :: map_ets(), fun_meta_info :: dict_ets(), % {mfa(), meta_info()} exports :: 'clean' | set_ets(), % set(mfa()) temp_exported_types :: 'clean' | set_ets(), % set(mfa()) @@ -141,6 +143,8 @@ new() -> CodeOptions = [compressed, public, {read_concurrency, true}], Code = ets:new(dialyzer_codeserver_code, CodeOptions), TempOptions = [public, {write_concurrency, true}], + Contracts = ets_read_concurrent_table(dialyzer_codeserver_contracts), + Callbacks = ets_read_concurrent_table(dialyzer_codeserver_callbacks), [Exports, FunMetaInfo, TempExportedTypes, TempRecords, TempContracts, TempCallbacks] = [ets:new(Name, TempOptions) || @@ -152,6 +156,8 @@ new() -> #codeserver{code = Code, exports = Exports, fun_meta_info = FunMetaInfo, + contracts = Contracts, + callbacks = Callbacks, temp_exported_types = TempExportedTypes, temp_records = TempRecords, temp_contracts = TempContracts, @@ -341,44 +347,43 @@ store_temp_contracts(Mod, SpecMap, CallbackMap, #codeserver{temp_contracts = Cn, temp_callbacks = Cb} = CS) when is_atom(Mod) -> - CS1 = - case maps:size(SpecMap) =:= 0 of - true -> CS; - false -> - CS#codeserver{temp_contracts = ets_map_store(Mod, SpecMap, Cn)} - end, - case maps:size(CallbackMap) =:= 0 of - true -> CS1; - false -> - CS1#codeserver{temp_callbacks = ets_map_store(Mod, CallbackMap, Cb)} - end. + CS1 = CS#codeserver{temp_contracts = ets_map_store(Mod, SpecMap, Cn)}, + CS1#codeserver{temp_callbacks = ets_map_store(Mod, CallbackMap, Cb)}. --spec get_temp_contracts(codeserver()) -> {mod_contracts(), mod_contracts()}. +-spec contracts_modules(codeserver()) -> [module()]. -get_temp_contracts(#codeserver{temp_contracts = TempContDict, - temp_callbacks = TempCallDict}) -> - {ets_dict_to_dict(TempContDict), ets_dict_to_dict(TempCallDict)}. +contracts_modules(#codeserver{temp_contracts = TempContTable}) -> + ets:select(TempContTable, [{{'$1', '$2'}, [], ['$1']}]). --spec finalize_contracts(mod_contracts(), mod_contracts(), codeserver()) -> - codeserver(). +-spec store_contracts(module(), contracts(), contracts(), codeserver()) -> + codeserver(). -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, Map, Table) -> - Keys = maps:keys(Map), - true = ets:insert(Table, maps:to_list(Map)), - true = ets:insert(Table, {Mod, Keys}), - Table. +store_contracts(Mod, SpecMap, CallbackMap, CS) -> + #codeserver{contracts = SpecDict, callbacks = CallbackDict} = CS, + Keys = maps:keys(SpecMap), + true = ets:insert(SpecDict, maps:to_list(SpecMap)), + true = ets:insert(SpecDict, {Mod, Keys}), + true = ets:insert(CallbackDict, maps:to_list(CallbackMap)), + CS. -decompose_cb_dict(_Mod, Map, Table) -> - true = ets:insert(Table, maps:to_list(Map)), - Table. +-spec get_temp_contracts(module(), codeserver()) -> + {contracts(), contracts()}. + +get_temp_contracts(Mod, #codeserver{temp_contracts = TempContDict, + temp_callbacks = TempCallDict}) -> + [{Mod, Contracts}] = ets:lookup(TempContDict, Mod), + true = ets:delete(TempContDict, Mod), + [{Mod, Callbacks}] = ets:lookup(TempCallDict, Mod), + true = ets:delete(TempCallDict, Mod), + {Contracts, Callbacks}. + +-spec finalize_contracts(codeserver()) -> codeserver(). + +finalize_contracts(#codeserver{temp_contracts = TempContDict, + temp_callbacks = TempCallDict} = CS) -> + true = ets:delete(TempContDict), + true = ets:delete(TempCallDict), + CS#codeserver{temp_contracts = clean, temp_callbacks = clean}. 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_contracts.erl b/lib/dialyzer/src/dialyzer_contracts.erl index d3ed4435ad..c0a4c6892c 100644 --- a/lib/dialyzer/src/dialyzer_contracts.erl +++ b/lib/dialyzer/src/dialyzer_contracts.erl @@ -150,8 +150,7 @@ sequence([H|T], Delimiter) -> H ++ Delimiter ++ sequence(T, Delimiter). dialyzer_codeserver:codeserver(). process_contract_remote_types(CodeServer) -> - {TmpContractDict, TmpCallbackDict} = - dialyzer_codeserver:get_temp_contracts(CodeServer), + Mods = dialyzer_codeserver:contracts_modules(CodeServer), ExpTypes = dialyzer_codeserver:get_exported_types(CodeServer), RecordDict = dialyzer_codeserver:get_records(CodeServer), ContractFun = @@ -165,21 +164,22 @@ process_contract_remote_types(CodeServer) -> {{MFA, {File, Contract, Xtra}}, C2} end, ModuleFun = - fun({ModuleName, ContractMap}) -> + fun(ModuleName) -> Cache = erl_types:cache__new(), - {NewContractList, _NewCache} = + {ContractMap, CallbackMap} = + dialyzer_codeserver:get_temp_contracts(ModuleName, CodeServer), + {NewContractList, Cache1} = lists:mapfoldl(ContractFun, Cache, maps:to_list(ContractMap)), - {ModuleName, maps:from_list(NewContractList)} + {NewCallbackList, _NewCache} = + lists:mapfoldl(ContractFun, Cache1, maps:to_list(CallbackMap)), + dialyzer_codeserver:store_contracts(ModuleName, + maps:from_list(NewContractList), + maps:from_list(NewCallbackList), + CodeServer) end, - erlang:garbage_collect(), - NewContractList = lists:map(ModuleFun, dict:to_list(TmpContractDict)), - NewCallbackList = lists:map(ModuleFun, dict:to_list(TmpCallbackDict)), - NewContractDict = dict:from_list(NewContractList), - NewCallbackDict = dict:from_list(NewCallbackList), - %% Make sure temporary data and the (huge) cache are garbage collected: - erlang:garbage_collect(), - dialyzer_codeserver:finalize_contracts(NewContractDict, NewCallbackDict, - CodeServer). + lists:foreach(ModuleFun, Mods), + %% erlang:garbage_collect(), + dialyzer_codeserver:finalize_contracts(CodeServer). -type opaques_fun() :: fun((module()) -> [erl_types:erl_type()]). -- cgit v1.2.3