dialyzer_analysis_callgraph.erl

来自「OTP是开放电信平台的简称」· ERL 代码 · 共 666 行 · 第 1/2 页

ERL
666
字号
			       not dialyzer_plt:contains_mfa(InitPlt, To)			   end, ExtCalls),  {BadCalls1, RealExtCalls} =    if ExtCalls1 =:= [] -> {[], []};       true -> 	Modules = 	  case StartFrom of	    byte_code -> 	      [list_to_atom(filename:basename(F, ".beam")) || F <- Files];	    src_code -> 	      [list_to_atom(filename:basename(F, ".erl")) || F <- Files]	  end,	ModuleSet = sets:from_list(Modules),	lists:partition(fun({_From, {M, _F, _A}}) -> 			    sets:is_element(M, ModuleSet)			end, ExtCalls1)    end,  NonLocalCalls = dialyzer_callgraph:non_local_calls(Callgraph1),  BadCalls2 = lists:filter(fun({_From, To}) ->			       not dialyzer_codeserver:is_exported(To, CServer)			   end, NonLocalCalls),  case BadCalls1 ++ BadCalls2 of    [] -> ok;    BadCalls -> send_bad_calls(Parent, BadCalls, AnalType, CodeServer)  end,  if RealExtCalls =:= [] -> ok;     true ->      send_ext_calls(Parent, lists:usort([To || {_From, To} <- RealExtCalls]))  end,  Callgraph1.compile_src(File, Includes, Defines, Callgraph, CServer, AnalType, Plt) ->  DefaultIncludes = default_includes(filename:dirname(File)),  DefCompOpts = ?SRC_COMPILE_OPTS ++ Includes++Defines++DefaultIncludes,  CompOpts = if AnalType =:= old_style -> DefCompOpts;		true -> [no_copt|DefCompOpts]	     end,  Mod = list_to_atom(filename:basename(File, ".erl")),  case dialyzer_utils:get_abstract_code_from_src(File, CompOpts) of    {error, Msg} -> {error, Msg};    AbstrCode ->      case dialyzer_utils:get_core_from_abstract_code(AbstrCode, CompOpts) of	error -> {error, "  Could not find abstract code for: "++File};	Core ->	  NoWarn = abs_get_nowarn(AbstrCode, Mod),	  case dialyzer_utils:get_record_info(AbstrCode) of	    {error, _} = Error -> Error;	    {ok, RecInfo} ->	      CServer2 = 		dialyzer_codeserver:store_records(Mod, RecInfo, CServer),	      compile_core(Mod, Core, NoWarn, Callgraph, 			   CServer2, AnalType, Plt)	  end      end  end.compile_byte(File, Callgraph, CServer, AnalType, Plt) ->  %% We must always set the code path, because the HiPE compiler  %% does a M:module_info() call!  OldPath = code:get_path(),  Mod = list_to_atom(filename:basename(File, ".beam")),  Dir = filename:dirname(File),  case code:add_patha(Dir) of    true ->                  Opts = if AnalType =:= old_style -> [];		true -> [no_copt]	     end,      Res = 	case dialyzer_utils:get_abstract_code_from_beam(File) of	  error ->	  	    case AnalType of	      dataflow -> 		{error, "  Could not get abstract code for "++File++		 "\n  Use option --old_style or recompile with"		 " +debug_info (preferred)."};	      plt_build ->		{error, "  Could not get abstract code for "++File};	      old_style ->		case (catch hipe:c(Mod, ?HIPE_COMPILE_OPTS)) of		  {'EXIT', Why} -> {error, io_lib:format("~p", [Why])};		  {error, Why} -> {error, io_lib:format("~p", [Why])};		  {ok, Icode} ->		    CS1 = dialyzer_codeserver:insert(Icode, icode, CServer),		    Exp = beam_get_exports(File),		    CS2 = dialyzer_codeserver:insert_exports(Exp, CS1),		    NewCG = dialyzer_callgraph:scan_icode(Icode, Callgraph),		    NoWarn = beam_get_nowarn(File),		    {ok, NewCG, NoWarn, CS2}		end	    end;	  AbstrCode ->	    NoWarn = abs_get_nowarn(AbstrCode, Mod),	    case dialyzer_utils:get_core_from_abstract_code(AbstrCode, Opts) of	      error -> {error, "  Could not get core for "++File};	      Core ->		case dialyzer_utils:get_record_info(AbstrCode) of		  {error, _} = Error -> Error;		  {ok, RecInfo} ->		    CServer1 = 		      dialyzer_codeserver:store_records(Mod, RecInfo, CServer),		    compile_core(Mod, Core, NoWarn, Callgraph, 				 CServer1, AnalType, Plt)		end	    end	end,      true = code:set_path(OldPath),      Res;    false ->      {error, "  Could not add path: "++Dir}  end.compile_core(Mod, Core, NoWarn, Callgraph, CServer, AnalType, Plt) ->  Exp = core_get_exports(Core),  CServer1 = dialyzer_codeserver:insert_exports(Exp, CServer),  {LabeledCore, CServer2} = label_core(Core, CServer1),  if AnalType =:= old_style ->       compile_core_old(Mod, LabeledCore, NoWarn, Callgraph, CServer2, 		       AnalType, Plt);     true ->       store_code_and_build_callgraph(Mod, LabeledCore, none, Callgraph, 				     CServer2, AnalType, NoWarn)  end.compile_core_old(Mod, LabeledCore, NoWarn, Callgraph, CServer, AnalType, Plt) ->  try     AnnCore = dialyzer_dataflow:annotate_module(LabeledCore, Plt),    TransCore = cerl:to_records(AnnCore),    case hipe:compile_core(Mod, TransCore, [], ?HIPE_COMPILE_OPTS) of      {error, Why} -> {error, io_lib:format("~p", [Why])};      {ok, Icode} ->	store_code_and_build_callgraph(Mod, TransCore, Icode, Callgraph, 				       CServer, AnalType, NoWarn)    end  catch    _:What ->       {error, io_lib:format("~p", [{What, erlang:get_stacktrace()}])}  end.  abs_get_nowarn(Abs, M) ->  [{M, F, A}    || {attribute, _, compile, {nowarn_unused_function, {F, A}}} <- Abs].beam_get_exports(File) ->  case beam_lib:chunks(File, [exports]) of    {ok,{_,[{exports, List}]}} ->      M = list_to_atom(filename:basename(File, ".beam")),      [{M, F, A} || {F, A} <- List];    error ->      []  end.beam_get_nowarn(File) ->  case beam_lib:chunks(File, [compile_info]) of    {ok,{_,[{compile_info, List}]}} ->      M = list_to_atom(filename:basename(File, ".beam")),      [{M, F, A} || {nowarn_unused_function, {F, A}} <- List];    error ->      []  end.core_get_exports(Core) ->  Tree = cerl:from_records(Core),  Exports1 = cerl:module_exports(Tree),    Exports2 = [cerl:var_name(V) || V <- Exports1],  M = cerl:atom_val(cerl:module_name(Tree)),  [{M, F, A} || {F, A} <- Exports2].label_core(Core, CServer) ->  NextLabel = dialyzer_codeserver:next_core_label(CServer),  CoreTree = cerl:from_records(Core),  {LabeledTree, NewNextLabel} = cerl_trees:label(CoreTree, NextLabel),  {cerl:to_records(LabeledTree),    dialyzer_codeserver:update_next_core_label(NewNextLabel, CServer)}.store_code_and_build_callgraph(Mod, Core, Icode, Callgraph, CServer, 			       AnalType, NoWarn) ->  if AnalType =:= old_style ->      %% When building the callgraph from core that is not lambda      %% lifted, we lose the lifted functions. We solve this for now      %% by scanning the icode instead of the core code. I can't wait      %% to get rid of the icode.      CServer1 = dialyzer_codeserver:insert(Icode, icode, CServer),      NewCallgraph = dialyzer_callgraph:scan_icode(Icode, Callgraph),      {ok, NewCallgraph, NoWarn, CServer1};     true ->      CoreTree = cerl:from_records(Core),      NewCallgraph = dialyzer_callgraph:scan_core_tree(CoreTree, Callgraph),      CServer2 = dialyzer_codeserver:insert([{Mod, CoreTree}], core, CServer),      {ok, NewCallgraph, NoWarn, CServer2}  end.%%____________________________________________________________%%%% Utilities%%expand_files(Analysis) ->  Files = Analysis#analysis.files,  Ext =     case Analysis#analysis.start_from of      byte_code -> ".beam";      src_code -> ".erl"    end,  case expand_files(Files, Ext, []) of    [] ->      exit({error, "No files to analyze. Check analysis type."});    NewFiles ->      Analysis#analysis{files=NewFiles}  end.expand_files([File|Left], Ext, Acc) ->  case filelib:is_dir(File) of    true ->      {ok, List} = file:list_dir(File),      NewFiles = [filename:join(File, X)		  || X <- List, filename:extension(X) =:= Ext],      expand_files(Left, Ext, NewFiles++Acc);    false ->      expand_files(Left, Ext, [File|Acc])  end;expand_files([], _Ext, Acc) ->  ordsets:from_list(Acc).default_includes(Dir) ->  L1 = ["..", "../incl", "../inc", "../include"],  [{i, filename:join(Dir, X)}||X<-L1].  %%____________________________________________________________%%%% Handle Messages%%send_log(Parent, Msg) ->  Parent ! {self(), log, Msg}.send_warnings(_Parent, []) ->  ok;send_warnings(Parent, Warnings) ->  Parent ! {self(), warnings, Warnings}.filter_warnings(LegalWarnings, Warnings) ->  [Warning || {Tag, Warning} <- Warnings, lists:member(Tag, LegalWarnings)].send_analysis_done(Parent) ->  Parent ! {self(), done}.send_error(Parent, Msg) ->  Parent ! {self(), error, Msg}.send_scan_fail(_Parent, []) ->  ok;send_scan_fail(Parent, [{FailFile, Reason}|Left]) ->  Msg = io_lib:format("Error scanning file: ~p\n~s\n", 		      [FailFile, Reason]),  send_error(Parent, Msg),  send_scan_fail(Parent, Left).  send_ext_calls(Parent, ExtCalls) ->  Parent ! {self(), ext_calls, ExtCalls}.send_bad_calls(Parent, BadCalls, old_style, _CodeServer) ->  Warnings =     [{?WARN_CALLGRAPH,       {From, io_lib:format("Call to missing or unexported function ~w\n", 			   [To])}}     || {From, {_,F,A} = To} <- BadCalls,	not(F =:= module_info andalso (A =:= 0 orelse A =:= 1))],  send_warnings(Parent, Warnings);send_bad_calls(Parent, BadCalls, _AnalType, CodeServer) ->  send_warnings(Parent, format_bad_calls(BadCalls, CodeServer, [])).format_bad_calls([{{_, _, _}, {_, module_info, A}}|Left], CodeServer, Acc)   when A =:= 0; A =:= 1 ->  format_bad_calls(Left, CodeServer, Acc);format_bad_calls([{From, To}|Left], 		 CodeServer, Acc) ->  {ok, Tree} = dialyzer_codeserver:lookup(From, core, CodeServer),  Msg = io_lib:format("Call to missing or unexported function ~w\n", 		      [To]),  FileLine = find_call_file_and_line(Tree, To),  NewAcc = [{?WARN_CALLGRAPH, {FileLine, Msg}}|Acc],  format_bad_calls(Left, CodeServer, NewAcc);format_bad_calls([], _CodeServer, Acc) ->  Acc.find_call_file_and_line({_Var, Tree}, MFA) ->  Fun =     fun(SubTree, Acc) ->	case cerl:is_c_call(SubTree) of	  true ->	    M = cerl:call_module(SubTree),	    F = cerl:call_name(SubTree),	    A = cerl:call_arity(SubTree),	    case cerl:is_c_atom(M) andalso cerl:is_c_atom(F) of	      true ->		case {cerl:concrete(M), cerl:concrete(F), A} of		  MFA ->		    Ann = cerl:get_ann(SubTree),		    [{get_file(Ann), get_line(Ann)}|Acc];		  _ -> Acc		end;	      false -> Acc	    end;	  false -> Acc	end    end,  hd(cerl_trees:fold(Fun, [], Tree)).get_line([Line|_]) when is_integer(Line) -> Line;get_line([_|Tail]) -> get_line(Tail);get_line([]) -> -1.get_file([{file, File}|_]) -> File;get_file([_|Tail]) -> get_file(Tail).%%____________________________________________________________%%%% Handle the PLT%%init_plt(#analysis{init_plt=InitPlt, user_plt=Plt, plt_info=Info}) ->  dialyzer_plt:copy(InitPlt, Plt),  case Info of    none -> ok;    {MD5, Libs} ->       dialyzer_plt:insert(Plt, {md5, MD5}),      dialyzer_plt:insert(Plt, {libs, Libs}),      ok  end.

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?