dialyzer_analysis_callgraph.erl
来自「OTP是开放电信平台的简称」· ERL 代码 · 共 666 行 · 第 1/2 页
ERL
666 行
%% -*- erlang-indent-level: 2 -*-%%--------------------------------------------------------------------%% ``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 via the world wide web 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.%% %% Copyright 2006, 2007 Tobias Lindahl and Kostis Sagonas%% %% $Id$%%%%%-------------------------------------------------------------------%%% File : dialyzer_analysis_callgraph.erl%%% Author : Tobias Lindahl <tobiasl@it.uu.se>%%% Description : %%%%%% Created : 5 Apr 2005 by Tobias Lindahl <tobiasl@it.uu.se>%%%--------------------------------------------------------------------module(dialyzer_analysis_callgraph).-export([start/3]).-include("dialyzer.hrl").-record(analysis_state, { codeserver, analysis_type, defines, doc_plt, include_dirs, no_warn_unused, options, parent, plt, start_from, supress_inline }).-record(state, {parent, legal_warnings}).-define(HIPE_COMPILE_OPTS, [dialyzer|?HIPE_DEF_OPTS]).%%____________________________________________________________%%%% Main%%start(Parent, LegalWarnings, Analysis) -> NewAnalysis1 = expand_files(Analysis), init_plt(NewAnalysis1), NewAnalysis2 = run_analysis(NewAnalysis1), State = #state{parent=Parent, legal_warnings=LegalWarnings}, loop(State, NewAnalysis2, none).run_analysis(Analysis) -> Self = self(), Fun = fun() -> analysis_start(Self, Analysis) end, Analysis#analysis{analysis_pid=spawn_link(Fun)}.loop(State, Analysis = #analysis{}, ExtCalls) -> AnalPid = Analysis#analysis.analysis_pid, Parent = State#state.parent, receive {AnalPid, log, LogMsg} -> send_log(Parent, LogMsg), loop(State, Analysis, ExtCalls); {AnalPid, warnings, Warnings} -> case filter_warnings(State#state.legal_warnings, Warnings) of [] -> ok; SendWarnings -> send_warnings(Parent, SendWarnings) end, loop(State, Analysis, ExtCalls); {AnalPid, error, Msg} -> send_error(Parent, Msg), loop(State, Analysis, ExtCalls); {AnalPid, done} -> case ExtCalls =:= none of true -> send_analysis_done(Parent); false -> send_ext_calls(Parent, ExtCalls), send_analysis_done(Parent) end; {AnalPid, ext_calls, NewExtCalls} -> loop(State, Analysis, NewExtCalls); {Parent, stop} -> exit(AnalPid, kill), ok end.%%____________________________________________________________%%%% The Analysis%%analysis_start(Parent, Analysis) -> %%XXX: Until we move the icode analysis out of HiPE put(hipe_target_arch, x86), CServer = dialyzer_codeserver:new(), Plt = Analysis#analysis.user_plt, State = #analysis_state{codeserver=CServer, analysis_type=Analysis#analysis.type, defines=Analysis#analysis.defines, doc_plt=Analysis#analysis.doc_plt, include_dirs=Analysis#analysis.include_dirs, plt=Plt, parent=Parent, start_from=Analysis#analysis.start_from, supress_inline=Analysis#analysis.supress_inline }, Files = ordsets:from_list(Analysis#analysis.files), {Callgraph, NoWarn, NewCServer} = compile_and_store(Files, State), State1 = State#analysis_state{codeserver=NewCServer}, State2 = State1#analysis_state{no_warn_unused=NoWarn}, %% Remove all old versions of the files being analyzed dialyzer_plt:delete_list(Plt, dialyzer_callgraph:all_nodes(Callgraph)), analyze_callgraph(Callgraph, State2), dialyzer_callgraph:delete(Callgraph), Exports = dialyzer_codeserver:all_exports(NewCServer), dialyzer_plt:strip_non_member_mfas(Plt, Exports), dialyzer_codeserver:delete(NewCServer), send_analysis_done(Parent). analyze_callgraph(Callgraph, State) -> Plt = State#analysis_state.plt, Codeserver = State#analysis_state.codeserver, case State#analysis_state.analysis_type of plt_build -> Callgraph1 = dialyzer_callgraph:finalize(Callgraph), dialyzer_succ_typings:analyze_callgraph(Callgraph1, Plt, Codeserver); old_style -> case erlang:system_info(schedulers) of 1 -> Callgraph1 = dialyzer_callgraph:finalize(Callgraph), analyze_callgraph_single_threaded(Callgraph1, State); N when is_integer(N), N > 1 -> analyze_callgraph_in_parallel(Callgraph, State, N) end; Type when Type =:= dataflow; Type =:= succ_typings -> NoWarn = State#analysis_state.no_warn_unused, DocPlt = State#analysis_state.doc_plt, Callgraph1 = dialyzer_callgraph:finalize(Callgraph), Warnings = dialyzer_succ_typings:get_warnings(Callgraph1, Plt, DocPlt, Codeserver, NoWarn, Type), send_warnings(State#analysis_state.parent, Warnings), ok end.analyze_callgraph_in_parallel(Callgraph, State, N) -> CallgraphList1 = dialyzer_callgraph:split_into_components(Callgraph), CallgraphList2 = [dialyzer_callgraph:finalize(CG) || CG <- CallgraphList1], parallel_analysis_loop(CallgraphList2, State, 0, N).parallel_analysis_loop([], #analysis_state{parent=Parent}, 0, _MaxProc) -> send_analysis_done(Parent), ok;parallel_analysis_loop([CG|CGs], State, Running, MaxProc) when Running < MaxProc -> Pid = self(), spawn_link(fun()-> %%XXX: Until we move the icode analysis out of HiPE put(hipe_target_arch, x86), %%io:format("Starting with nodes: ~p\n", %% [dialyzer_callgraph:all_nodes(CG)]), %% Hijack our parents messages. State1 = State#analysis_state{parent=Pid}, analyze_callgraph_single_threaded(CG, State1), Pid ! done end), parallel_analysis_loop(CGs, State, Running+1, MaxProc);parallel_analysis_loop(CGs, State, Running, MaxProc) -> Parent = State#analysis_state.parent, receive {_Pid, log, LogMsg} -> Parent ! {self(), log, LogMsg}, parallel_analysis_loop(CGs, State, Running, MaxProc); {_Pid, warnings, Warnings} -> Parent ! {self(), warnings, Warnings}, parallel_analysis_loop(CGs, State, Running, MaxProc); {_Pid, error, Msg} -> Parent ! {self(), error, Msg}, parallel_analysis_loop(CGs, State, Running, MaxProc); done -> parallel_analysis_loop(CGs, State, Running - 1, MaxProc) end.analyze_callgraph_single_threaded(Callgraph, State) -> case dialyzer_callgraph:take_scc(Callgraph) of {ok, SCC, NewCallgraph} -> analyze_scc_warnings(SCC, Callgraph, State), analyze_callgraph_single_threaded(NewCallgraph, State); none -> ok end.analyze_scc_warnings([Fun], Callgraph, State=#analysis_state{parent=Parent}) -> Msg = io_lib:format("Analyzing Fun: ~p\n", [Fun]), send_log(Parent, Msg), case dialyzer_callgraph:is_self_rec(Fun, Callgraph) of true -> analyze_scc_icode([Fun], State); false -> {_, Warnings} = analyze_fun_icode(Fun, State), send_warnings(Parent, Warnings) end;analyze_scc_warnings(SCC, _Callgraph, State = #analysis_state{parent=Parent}) -> %%io:format("Analyzing scc: ~p\n", [SCC]), Msg = io_lib:format("Analyzing SCC: ~p\n", [SCC]), send_log(Parent, Msg), analyze_scc_icode(SCC, State).analyze_scc_icode(SCC, State = #analysis_state{parent=Parent}) -> Res = [analyze_fun_icode(MFA, State) || MFA <- SCC], case lists:any(fun({X, _}) -> X =:= not_fixpoint end, Res) of true -> analyze_scc_icode(SCC, State); false -> send_log(Parent, "Reached fixpoint for SCC\n"), Warnings = lists:foldl(fun({_, W}, Acc) -> W ++ Acc end, [], Res), send_warnings(Parent, Warnings) end.analyze_fun_icode(MFA, #analysis_state{codeserver=CServer, doc_plt=DocPlt, no_warn_unused=NoWarn, parent=Parent, plt=Plt}) -> %%io:format("Analyzing icode for: ~p\n", [MFA]), case dialyzer_codeserver:lookup(MFA, icode, CServer) of {ok, CFG} -> Msg1 = io_lib:format(" Analyzing icode for: ~p ...", [MFA]), send_log(Parent, Msg1), {T1, _} = statistics(runtime), Res = dialyzer_icode:run_analysis(CFG, MFA, Plt, NoWarn, true), {T2, _} = statistics(runtime), Msg2 = io_lib:format("done in ~.2f secs\n", [(T2-T1)/1000]), send_log(Parent, Msg2), case Res of {not_fixpoint, UpdateInfo, Warnings} -> if DocPlt =:= undefined -> ok; true -> dialyzer_plt:insert(DocPlt, [UpdateInfo]) end, dialyzer_plt:insert(Plt, [UpdateInfo]), {not_fixpoint, Warnings}; {fixpoint, UpdateInfo, Warnings} -> if DocPlt =:= undefined -> ok; true -> dialyzer_plt:insert(DocPlt, [UpdateInfo]) end, dialyzer_plt:insert(Plt, [UpdateInfo]), {fixpoint, Warnings} end; error -> %% Since HiPE removes module_info it is ok to not find the code %% for it. The only time this happens is when we start from %% byte_code and there is no abstract code. case MFA of {_, module_info, 0} -> ok; {_, module_info, 1} -> ok; _ -> Msg = io_lib:format(" Could not find icode for ~w\n", [MFA]), send_error(Parent, Msg) end, {fixpoint, []} end.%%____________________________________________________________%%%% Build the callgraph and fill the codeserver.%%compile_and_store(Files, State = #analysis_state{}) -> send_log(State#analysis_state.parent, "Reading files and computing callgraph... "), {T1, _} = statistics(runtime), Includes = [{i, X} || X <- State#analysis_state.include_dirs], Defines = [{d, Macro, Val} || {Macro, Val} <- State#analysis_state.defines], Callgraph = dialyzer_callgraph:new(), AnalType = State#analysis_state.analysis_type, Plt = State#analysis_state.plt, case State#analysis_state.start_from of src_code -> Fun = fun(File, {TmpCG, TmpCServer, TmpFailed, TmpNoWarn}) -> case compile_src(File, Includes, Defines, TmpCG, TmpCServer, AnalType, Plt) of {error, Reason} -> {TmpCG, TmpCServer, [{File, Reason}|TmpFailed], TmpNoWarn}; {ok, NewCG, NoWarn, NewCServer} -> {NewCG, NewCServer, TmpFailed, NoWarn++TmpNoWarn} end end; byte_code -> Fun = fun(File, {TmpCG, TmpCServer, TmpFailed, TmpNoWarn}) -> case compile_byte(File, TmpCG, TmpCServer, AnalType, Plt) of {error, Reason} -> {TmpCG, TmpCServer, [{File, Reason}|TmpFailed], TmpNoWarn}; {ok, NewCG, NoWarn, NewCServer} -> {NewCG, NewCServer, TmpFailed, NoWarn++TmpNoWarn} end end end, CServer = State#analysis_state.codeserver, {NewCallgraph1, NewCServer, Failed, NoWarn} = lists:foldl(Fun, {Callgraph, CServer, [], []}, Files), {T2, _} = statistics(runtime), Msg1 = io_lib:format("done in ~.2f secs\n" "Removing edges... ", [(T2-T1)/1000]), send_log(State#analysis_state.parent, Msg1), %%io:format("All exports: ~p\n", [dialyzer_codeserver:all_exports(NewCServer)]), NewCallgraph2 = cleanup_callgraph(State, NewCServer, NewCallgraph1, Files), send_scan_fail(State#analysis_state.parent, Failed), {T3, _} = statistics(runtime), Msg2 = io_lib:format("done in ~.2f secs\n", [(T3-T2)/1000]), send_log(State#analysis_state.parent, Msg2), {NewCallgraph2, sets:from_list(NoWarn), NewCServer}.cleanup_callgraph(#analysis_state{plt=InitPlt, parent=Parent, start_from=StartFrom, analysis_type=AnalType, codeserver=CodeServer}, CServer, Callgraph, Files) -> {Callgraph1, ExtCalls} = dialyzer_callgraph:remove_external(Callgraph), ExtCalls1 = lists:filter(fun({_From, To}) ->
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?