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 + -
显示快捷键?