⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 cover.erl

📁 OTP是开放电信平台的简称
💻 ERL
📖 第 1 页 / 共 4 页
字号:
    end.%% Load a set of cover compiled modules on remote nodesremote_load_compiled(Nodes,Compiled0) ->    Compiled = lists:map(fun get_data_for_remote_loading/1,Compiled0),    lists:foreach(      fun(Node) -> 	      remote_call(Node,{remote,load_compiled,Compiled})      end,      Nodes).%% Read all data needed for loading a cover compiled module on a remote node%% Binary is the beam code for the module and InitialTable is the initial%% data to insert in ?COVER_TABLE.get_data_for_remote_loading({Module,File}) ->    [{Module,Binary}] = ets:lookup(?BINARY_TABLE,Module),    %%! The InitialTable list will be long if the module is big - what to do??    InitialTable = ets:select(?COVER_TABLE,ms(Module)),    {Module,File,Binary,InitialTable}.%% Create a match spec which returns the clause info {Module,InitInfo} and %% all #bump keys for the given module with 0 number of calls.ms(Module) ->    ets:fun2ms(fun({Module,InitInfo})  -> 		       {Module,InitInfo};		  ({Key,_}) when is_record(Key,bump),Key#bump.module=:=Module -> 		       {Key,0}	       end).%% Unload modules on remote nodesremote_unload(Nodes,UnloadedModules) ->    lists:foreach(      fun(Node) -> 	      remote_call(Node,{remote,unload,UnloadedModules})      end,      Nodes).    %% Reset one or all modules on remote nodesremote_reset(Module,Nodes) ->    lists:foreach(      fun(Node) -> 	      remote_call(Node,{remote,reset,Module})      end,      Nodes).        %% Collect data from remote nodes - used for analyse or stop(Node)remote_collect(Module,Nodes,Stop) ->    CollectorPid = spawn(fun() -> collector_proc(length(Nodes)) end),    lists:foreach(      fun(Node) -> 	      remote_call(Node,{remote,collect,Module,CollectorPid}),	      if Stop -> remote_call(Node,{remote,stop});		 true -> ok	      end      end,      Nodes).%% Process which receives chunks of data from remote nodes - either when%% analysing or when stopping cover on the remote nodes.collector_proc(0) ->    ok;collector_proc(N) ->    receive 	{chunk,Chunk} ->	    insert_in_collection_table(Chunk),	    collector_proc(N);	done ->	    collector_proc(N-1)    end.insert_in_collection_table([{Key,Val}|Chunk]) ->    insert_in_collection_table(Key,Val),    insert_in_collection_table(Chunk);insert_in_collection_table([]) ->    ok.insert_in_collection_table(Key,Val) ->    case ets:member(?COLLECTION_TABLE,Key) of	true ->	    ets:update_counter(?COLLECTION_TABLE,			       Key,Val);	false ->	    ets:insert(?COLLECTION_TABLE,{Key,Val})    end.remove_myself([Node|Nodes],Acc) when Node=:=node() ->    remove_myself(Nodes,Acc);remove_myself([Node|Nodes],Acc) ->    remove_myself(Nodes,[Node|Acc]);remove_myself([],Acc) ->    Acc.    %%%--Handling of modules state data--------------------------------------analyse_info(_Module,[]) ->    ok;analyse_info(Module,Imported) ->    imported_info("Analysis",Module,Imported).export_info(_Module,[]) ->    ok;export_info(Module,Imported) ->    imported_info("Export",Module,Imported).export_info([]) ->    ok;export_info(Imported) ->    AllImportFiles = get_all_importfiles(Imported,[]),    io:format("Export includes data from imported files\n~p\n",[AllImportFiles]).get_all_importfiles([{_M,_F,ImportFiles}|Imported],Acc) ->    NewAcc = do_get_all_importfiles(ImportFiles,Acc),    get_all_importfiles(Imported,NewAcc);get_all_importfiles([],Acc) ->    Acc.do_get_all_importfiles([ImportFile|ImportFiles],Acc) ->    case lists:member(ImportFile,Acc) of	true ->	    do_get_all_importfiles(ImportFiles,Acc);	false ->	    do_get_all_importfiles(ImportFiles,[ImportFile|Acc])    end;do_get_all_importfiles([],Acc) ->    Acc.imported_info(Text,Module,Imported) ->    case lists:keysearch(Module,1,Imported) of	{value,{Module,_File,ImportFiles}} ->	    io:format("~s includes data from imported files\n~p\n",		      [Text,ImportFiles]);	false ->	    ok    end.        add_imported(Module, File, ImportFile, Imported) ->    add_imported(Module, File, filename:absname(ImportFile), Imported, []).add_imported(M, F1, ImportFile, [{M,_F2,ImportFiles}|Imported], Acc) ->    case lists:member(ImportFile,ImportFiles) of	true ->	    io:fwrite("WARNING: Module ~w already imported from ~p~n"		      "Not importing again!~n",[M,ImportFile]),	    dont_import;	false ->	    NewEntry = {M, F1, [ImportFile | ImportFiles]},	    {ok, reverse([NewEntry | Acc]) ++ Imported}    end;add_imported(M, F, ImportFile, [H|Imported], Acc) ->    add_imported(M, F, ImportFile, Imported, [H|Acc]);add_imported(M, F, ImportFile, [], Acc) ->    {ok, reverse([{M, F, [ImportFile]} | Acc])}.    %% Removes a module from the list of imported modules and writes a warning%% This is done when a module is compiled.remove_imported(Module,Imported) ->    case lists:keysearch(Module,1,Imported) of	{value,{Module,_,ImportFiles}} ->	    io:fwrite("WARNING: Deleting data for module ~w imported from~n"		      "~p~n",[Module,ImportFiles]),	    lists:keydelete(Module,1,Imported);	false ->	    Imported    end.%% Adds information to the list of compiled modules, preserving time order%% and without adding duplicate entries.add_compiled(Module, File1, [{Module,_File2}|Compiled]) ->    [{Module,File1}|Compiled];add_compiled(Module, File, [H|Compiled]) ->    [H|add_compiled(Module, File, Compiled)];add_compiled(Module, File, []) ->    [{Module,File}].is_loaded(Module, State) ->    case get_file(Module, State#main_state.compiled) of	{ok, File} ->	    case code:which(Module) of		?TAG -> {loaded, File};		_ -> unloaded	    end;	false ->	    case get_file(Module,State#main_state.imported) of		{ok,File,ImportFiles} ->		    {imported, File, ImportFiles};		false ->		    false	    end    end.get_file(Module, [{Module, File}|_T]) ->    {ok, File};get_file(Module, [{Module, File, ImportFiles}|_T]) ->    {ok, File, ImportFiles};get_file(Module, [_H|T]) ->    get_file(Module, T);get_file(_Module, []) ->    false.get_beam_file(Module,?TAG,Compiled) ->    {value,{Module,File}} = lists:keysearch(Module,1,Compiled),    case filename:extension(File) of	".erl" -> {error,no_beam};	".beam" -> {ok,File}    end;get_beam_file(_Module,BeamFile,_Compiled) ->    {ok,BeamFile}.get_modules(Compiled) ->    lists:map(fun({Module, _File}) -> Module end, Compiled).update_compiled([Module|Modules], [{Module,_File}|Compiled]) ->    update_compiled(Modules, Compiled);update_compiled(Modules, [H|Compiled]) ->    [H|update_compiled(Modules, Compiled)];update_compiled(_Modules, []) ->    [].%% Get all compiled modules which are still loaded, and possibly an%% updated version of the Compiled list.get_compiled_still_loaded(Nodes,Compiled0) ->    %% Find all Cover compiled modules which are still loaded    CompiledModules = get_modules(Compiled0),    LoadedModules = lists:filter(fun(Module) ->					 case code:which(Module) of					     ?TAG -> true;					     _ -> false					 end				 end,				 CompiledModules),    %% If some Cover compiled modules have been unloaded, update the database.    UnloadedModules = CompiledModules--LoadedModules,    Compiled = 	case UnloadedModules of	    [] ->		Compiled0;	    _ ->		lists:foreach(fun(Module) -> do_clear(Module) end,			      UnloadedModules),		remote_unload(Nodes,UnloadedModules),		update_compiled(UnloadedModules, Compiled0)	end,    {LoadedModules,Compiled}.%%%--Compilation---------------------------------------------------------%% do_compile(File, Options) -> {ok,Module} | {error,Error}do_compile(File, UserOptions) ->    Options = [debug_info,binary,report_errors,report_warnings] ++ UserOptions,    case compile:file(File, Options) of	{ok, Module, Binary} ->	    do_compile_beam(Module,Binary);	error ->	    error    end.%% Beam is a binary or a .beam file namedo_compile_beam(Module,Beam) ->    %% Clear database    do_clear(Module),        %% Extract the abstract format and insert calls to bump/6 at    %% every executable line and, as a side effect, initiate    %% the database        case get_abstract_code(Module, Beam) of	no_abstract_code=E ->	    {error,E};	encrypted_abstract_code=E ->	    {error,E};	{Vsn,Code} ->            Forms0 = epp:interpret_file_attribute(Code),	    {Forms,Vars} = transform(Vsn, Forms0, Module, Beam),	    %% Compile and load the result	    %% It's necessary to check the result of loading since it may	    %% fail, for example if Module resides in a sticky directory	    {ok, Module, Binary} = compile:forms(Forms, []),	    case code:load_binary(Module, ?TAG, Binary) of		{module, Module} ->		    		    %% Store info about all function clauses in database		    InitInfo = reverse(Vars#vars.init_info),		    ets:insert(?COVER_TABLE, {Module, InitInfo}),		    		    %% Store binary code so it can be loaded on remote nodes		    ets:insert(?BINARY_TABLE, {Module, Binary}),		    {ok, Module};				_Error ->		    do_clear(Module),		    error	    end    end.get_abstract_code(Module, Beam) ->    case beam_lib:chunks(Beam, [abstract_code]) of	{ok, {Module, [{abstract_code, AbstractCode}]}} ->	    AbstractCode;	{error,beam_lib,{key_missing_or_invalid,_,_}} ->	    encrypted_abstract_code;	Error -> Error    end.transform(Vsn, Code, Module, Beam) when Vsn=:=abstract_v1; Vsn=:=abstract_v2 ->    Vars0 = #vars{module=Module, vsn=Vsn},    MainFile=find_main_filename(Code),    {ok, MungedForms,Vars} = transform_2(Code,[],Vars0,MainFile,on),        %% Add module and export information to the munged forms    %% Information about module_info must be removed as this function    %% is added at compilation    {ok, {Module, [{exports,Exports1}]}} = beam_lib:chunks(Beam, [exports]),    Exports2 = lists:filter(fun(Export) ->				    case Export of					{module_info,_} -> false;					_ -> true				    end			    end,			    Exports1),    Forms = [{attribute,1,module,Module},	     {attribute,2,export,Exports2}]++ MungedForms,    {Forms,Vars};transform(Vsn=raw_abstract_v1, Code, Module, _Beam) ->    MainFile=find_main_filename(Code),    Vars0 = #vars{module=Module, vsn=Vsn},    {ok,MungedForms,Vars} = transform_2(Code,[],Vars0,MainFile,on),    {MungedForms,Vars}.    %% Helpfunction which returns the first found file-attribute, which can%% be interpreted as the name of the main erlang source file.find_main_filename([{attribute,_,file,{MainFile,_}}|_]) ->    MainFile;find_main_filename([_|Rest]) ->    find_main_filename(Rest).transform_2([Form|Forms],MungedForms,Vars,MainFile,Switch) ->    case munge(Form,Vars,MainFile,Switch) of	ignore ->	    transform_2(Forms,MungedForms,Vars,MainFile,Switch);	{MungedForm,Vars2,NewSwitch} ->	    transform_2(Forms,[MungedForm|MungedForms],Vars2,MainFile,NewSwitch)    end;transform_2([],MungedForms,Vars,_,_) ->    {ok, reverse(MungedForms), Vars}.%% This code traverses the abstract code, stored as the abstract_code%% chunk in the BEAM file, as described in absform(3) for Erlang/OTP R8B%% (Vsn=abstract_v2).%% The abstract format after preprocessing differs slightly from the abstract%% format given eg using epp:parse_form, this has been noted in comments.%% The switch is turned off when we encounter other files then the main file.%% This way we will be able to exclude functions defined in include files.munge({function,0,module_info,_Arity,_Clauses},_Vars,_MainFile,_Switch) ->    ignore; % module_info will be added again when the forms are recompiledmunge(Form={function,_,'MNEMOSYNE QUERY',_,_},Vars,_MainFile,Switch) ->    {Form,Vars,Switch};                 % No bumps in Mnemosyne code.munge(Form={function,_,'MNEMOSYNE RULE',_,_},Vars,_MainFile,Switch) ->    {Form,Vars,Switch};munge(Form={function,_,'MNEMOSYNE RECFUNDEF',_,_},Vars,_MainFile,Switch) ->    {Form,Vars,Switch};munge({function,Line,Function,Arity,Clauses},Vars,_MainFile,on) ->    Vars2 = Vars#vars{function=Function,		      arity=Arity,		      clause=1,		      lines=[],		      depth=1},    {MungedClauses, Vars3} = munge_clauses(Clauses, Vars2, []),    {{function,Line,Function,Arity,MungedClauses},Vars3,on};munge(Form={attribute,_,file,{MainFile,_}},Vars,MainFile,_Switch) ->    {Form,Vars,on};                     % Switch on tranformation!munge(Form={attribute,_,file,{_InclFile,_}},Vars,_MainFile,_Switch) ->    {Form,Vars,off};                    % Switch off transformation!munge({attribute,_,compile,{parse_transform,_}},_Vars,_MainFile,_Switch) ->    %% Don't want to run parse transforms more than once.    ignore;munge(Form,Vars,_MainFile,Switch) ->    % Other attributes and skipped includes.    {Form,Vars,Switch}.munge_clauses([{clause,Line,Pattern,Guards,Body}|Clauses], Vars, MClauses) ->    {MungedGuards, _Vars} = munge_exprs(Guards, Vars#vars{is_guard=true},[]),    case Vars#vars.depth of	1 -> % function clause	    {MungedBody, Vars2} = munge_body(Body, Vars#vars{depth=2}, []),	    ClauseInfo = {Vars2#vars.module,			  Vars2#vars.function,			  Vars2#vars.arity,			  Vars2#vars.clause,			  length(Vars2#vars.lines)},	    InitInfo = [ClauseInfo | Vars2#vars.init_info],	    Vars3 = Vars2#vars{init_info=InitInfo,			       clause=(Vars2#vars.clause)+1,			       lines=[],			       depth=1},	    munge_clauses(Clauses, Vars3,			  [{clause,Line,Pattern,MungedGuards,MungedBody}|			   MClauses]);	2 -> % receive-,  case- or if clause	    {MungedBody, Vars2} = munge_body(Body, Vars, []),	    munge_clauses(Clauses, Vars2,			  [{clause,Line,Pattern,MungedGuards,MungedBody}|			   MClauses])    end;munge_clauses([], Vars, MungedClauses) ->     {reverse(MungedClauses), Vars}.munge_body([Expr|Body], Vars, MungedBody) ->    %% Here is the place to add a call to cover:bump/6!    Line = element(2, Expr),    Lines = Vars#vars.lines,    case lists:member(Line,Lines) of	true -> % already a bump at this line!	    {MungedExpr, Vars2} = munge_expr(Expr, Vars),	    munge_body(Body, Vars2, [MungedExpr|MungedBody]);	false ->	    ets:insert(?COVER_TABLE, {#bump{module   = Vars#vars.module,					    function = Vars#vars.function,					    arity    = Vars#vars.arity,					    clause   = Vars#vars.clause,					    line     = Line},				      0}),	    Bump={call,0,{remote,0,{atom,0,ets},{atom,0,update_counter}},		  [{atom,0,?COVER_TABLE},		   {tuple,0,[{atom,0,?BUMP_REC_NAME},			     {atom,0,Vars#vars.module},			     {atom,0,Vars#vars.function},			     {integer,0,Vars#vars.arity},			     {integer,0,Vars#vars.clause},			     {integer,0,Line}]},		   {integer,0,1}]},%	    Bump = {call, 0, {remote, 0, {atom,0,cover}, {atom,0,bump}},%		    [{atom, 0, Vars#vars.module},%		     {atom, 0, Vars#vars.function},%		     {integer, 0, Vars#vars.arity},%		     {integer, 0, Vars#vars.clause},%		     {integer, 0, Line}]},	    Lines2 = [Line|Lines],	    {MungedExpr, Vars2} = munge_expr(Expr, Vars#vars{lines=Lines2}),	    munge_body(Body, Vars2, [MungedExpr,Bump|MungedBody])    end;munge_body([], Vars, MungedBody) ->    {reverse(MungedBody), Vars}.munge_expr({match,Line,ExprL,ExprR}, Vars) ->    {MungedExprL, Vars2} = munge_expr(ExprL, Vars),    {MungedExprR, Vars3} = munge_expr(ExprR, Vars2),    {{match,Line,MungedExprL,MungedExprR}, Vars3};munge_expr({tuple,Line,Exprs}, Vars) ->    {MungedExprs, Vars2} = munge_exprs(Exprs, Vars, []),    {{tuple,Line,MungedExprs}, Vars2};munge_expr({record,Line,Expr,Exprs}, Vars) ->    %% Only for Vsn=raw_abstract_v1    {MungedExprName, Vars2} = munge_expr(Expr, Vars),    {MungedExprFields, Vars3} = munge_exprs(Exprs, Vars2, []),    {{record,Line,MungedExprName,MungedExprFields}, Vars3};munge_expr({record_field,Line,ExprL,ExprR}, Vars) ->    %% Only for Vsn=raw_abstract_v1    {MungedExprL, Vars2} = munge_expr(ExprL, Vars),    {MungedExprR, Vars3} = munge_expr(ExprR, Vars2),    {{record_field,Line,MungedExprL,MungedExprR}, Vars3};munge_expr({cons,Line,ExprH,ExprT}, Vars) ->    {MungedExprH, Vars2} = munge_expr(ExprH, Vars),    {MungedExprT, Vars3} = munge_expr(ExprT, Vars2),    {{cons,Line,MungedExprH,MungedExprT}, Vars3};munge_expr({op,Line,Op,ExprL,ExprR}, Vars) ->    {MungedExprL, Vars2} = munge_expr(ExprL, Vars),    {MungedExprR, Vars3} = munge_expr(ExprR, Vars2),

⌨️ 快捷键说明

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