📄 cover.erl
字号:
after 0 -> {?SERVER,Node} ! Request, Return = receive {'DOWN', Ref, _Type, _Object, _Info} -> {error,node_dead}; {?SERVER,Reply} -> Reply end, erlang:demonitor(Ref), Return end. remote_reply(MainNode,Reply) -> {?SERVER,MainNode} ! {?SERVER,Reply}.%%%----------------------------------------------------------------------%%% cover_server on main node%%%----------------------------------------------------------------------init_main(Starter) -> register(?SERVER,self()), ets:new(?COVER_TABLE, [set, public, named_table]), ets:new(?BINARY_TABLE, [set, named_table]), ets:new(?COLLECTION_TABLE, [set, public, named_table]), process_flag(trap_exit,true), Starter ! {?SERVER,started}, main_process_loop(#main_state{}).main_process_loop(State) -> receive {From, {start_nodes,Nodes}} -> ThisNode = node(), StartedNodes = lists:foldl( fun(Node,Acc) -> case rpc:call(Node,cover,remote_start,[ThisNode]) of {ok,RPid} -> link(RPid), [Node|Acc]; Error -> io:format("Could not start cover on ~w: ~p\n", [Node,Error]), Acc end end, [], Nodes), %% In case some of the compiled modules have been unloaded they %% should not be loaded on the new node. {_LoadedModules,Compiled} = get_compiled_still_loaded(State#main_state.nodes, State#main_state.compiled), remote_load_compiled(StartedNodes,Compiled), State1 = State#main_state{nodes = State#main_state.nodes ++ StartedNodes, compiled = Compiled}, reply(From, {ok,StartedNodes}), main_process_loop(State1); {From, {compile, File, Options}} -> case do_compile(File, Options) of {ok, Module} -> remote_load_compiled(State#main_state.nodes,[{Module,File}]), reply(From, {ok, Module}), Compiled = add_compiled(Module, File, State#main_state.compiled), Imported = remove_imported(Module,State#main_state.imported), main_process_loop(State#main_state{compiled = Compiled, imported = Imported}); error -> reply(From, {error, File}), main_process_loop(State) end; {From, {compile_beam, Module, BeamFile0}} -> Compiled0 = State#main_state.compiled, case get_beam_file(Module,BeamFile0,Compiled0) of {ok,BeamFile} -> {Reply,Compiled} = case do_compile_beam(Module,BeamFile) of {ok, Module} -> remote_load_compiled(State#main_state.nodes, [{Module,BeamFile}]), C = add_compiled(Module,BeamFile,Compiled0), {{ok,Module},C}; error -> {{error, BeamFile}, Compiled0}; {error,Reason} -> % no abstract code {{error, {Reason, BeamFile}}, Compiled0} end, reply(From,Reply), Imported = remove_imported(Module,State#main_state.imported), main_process_loop(State#main_state{compiled = Compiled, imported = Imported}); {error,no_beam} -> %% The module has first been compiled from .erl, and now %% someone tries to compile it from .beam reply(From, {error,{already_cover_compiled,no_beam_found,Module}}), main_process_loop(State) end; {From, {export,OutFile,Module}} -> case file:open(OutFile,[write,binary,raw]) of {ok,Fd} -> Reply = case Module of '_' -> export_info(State#main_state.imported), collect(State#main_state.nodes), do_export_table(State#main_state.compiled, State#main_state.imported, Fd); _ -> export_info(Module,State#main_state.imported), case is_loaded(Module, State) of {loaded, File} -> [{Module,Clauses}] = ets:lookup(?COVER_TABLE,Module), collect(Module, Clauses, State#main_state.nodes), do_export_table([{Module,File}],[],Fd); {imported, File, ImportFiles} -> %% don't know if I should allow this - %% export a module which is only imported Imported = [{Module,File,ImportFiles}], do_export_table([],Imported,Fd); _NotLoaded -> {error,{not_cover_compiled,Module}} end end, file:close(Fd), reply(From, Reply); {error,Reason} -> reply(From, {error, {cant_open_file,OutFile,Reason}}) end, main_process_loop(State); {From, {import,File}} -> case file:open(File,[read,binary,raw]) of {ok,Fd} -> Imported = do_import_to_table(Fd,File, State#main_state.imported), reply(From, ok), main_process_loop(State#main_state{imported=Imported}); {error,Reason} -> reply(From, {error, {cant_open_file,File,Reason}}), main_process_loop(State) end; {From, modules} -> %% Get all compiled modules which are still loaded {LoadedModules,Compiled} = get_compiled_still_loaded(State#main_state.nodes, State#main_state.compiled), reply(From, LoadedModules), main_process_loop(State#main_state{compiled=Compiled}); {From, imported_modules} -> %% Get all modules with imported data ImportedModules = lists:map(fun({Mod,_File,_ImportFile}) -> Mod end, State#main_state.imported), reply(From, ImportedModules), main_process_loop(State); {From, imported} -> %% List all imported files reply(From, get_all_importfiles(State#main_state.imported,[])), main_process_loop(State); {From, which_nodes} -> %% List all imported files reply(From, State#main_state.nodes), main_process_loop(State); {From, reset} -> lists:foreach( fun({Module,_File}) -> do_reset_main_node(Module,State#main_state.nodes) end, State#main_state.compiled), reply(From, ok), main_process_loop(State#main_state{imported=[]}); {From, {stop,Nodes}} -> remote_collect('_',Nodes,true), reply(From, ok), State1 = State#main_state{nodes=State#main_state.nodes--Nodes}, main_process_loop(State1); {From, stop} -> lists:foreach( fun(Node) -> remote_call(Node,{remote,stop}) end, State#main_state.nodes), reload_originals(State#main_state.compiled), reply(From, ok); {From, {Request, Module}} -> case is_loaded(Module, State) of {loaded, File} -> {Reply,State1} = case Request of {analyse, Analysis, Level} -> analyse_info(Module,State#main_state.imported), [{Module,Clauses}] = ets:lookup(?COVER_TABLE,Module), collect(Module,Clauses,State#main_state.nodes), R = do_analyse(Module, Analysis, Level, Clauses), {R,State}; {analyse_to_file, OutFile, Opts} -> R = case find_source(File) of {beam,_BeamFile} -> {error,no_source_code_found}; ErlFile -> Imported = State#main_state.imported, analyse_info(Module,Imported), [{Module,Clauses}] = ets:lookup(?COVER_TABLE,Module), collect(Module, Clauses, State#main_state.nodes), HTML = lists:member(html,Opts), do_analyse_to_file(Module,OutFile, ErlFile,HTML) end, {R,State}; is_compiled -> {{file, File},State}; reset -> R = do_reset_main_node(Module, State#main_state.nodes), Imported = remove_imported(Module, State#main_state.imported), {R,State#main_state{imported=Imported}} end, reply(From, Reply), main_process_loop(State1); {imported,File,_ImportFiles} -> {Reply,State1} = case Request of {analyse, Analysis, Level} -> analyse_info(Module,State#main_state.imported), [{Module,Clauses}] = ets:lookup(?COLLECTION_TABLE,Module), R = do_analyse(Module, Analysis, Level, Clauses), {R,State}; {analyse_to_file, OutFile, Opts} -> R = case find_source(File) of {beam,_BeamFile} -> {error,no_source_code_found}; ErlFile -> Imported = State#main_state.imported, analyse_info(Module,Imported), HTML = lists:member(html,Opts), do_analyse_to_file(Module,OutFile, ErlFile,HTML) end, {R,State}; is_compiled -> {false,State}; reset -> R = do_reset_collection_table(Module), Imported = remove_imported(Module, State#main_state.imported), {R,State#main_state{imported=Imported}} end, reply(From, Reply), main_process_loop(State1); NotLoaded -> Reply = case Request of is_compiled -> false; _ -> {error, {not_cover_compiled,Module}} end, Compiled = case NotLoaded of unloaded -> do_clear(Module), remote_unload(State#main_state.nodes,[Module]), update_compiled([Module], State#main_state.compiled); false -> State#main_state.compiled end, reply(From, Reply), main_process_loop(State#main_state{compiled=Compiled}) end; {'EXIT',Pid,_Reason} -> %% Exit is trapped on the main node only, so this will only happen %% there. I assume that I'm only linked to cover_servers on remote %% nodes, so this must be one of them crashing. %% Remove node from list! State1 = State#main_state{nodes=State#main_state.nodes--[node(Pid)]}, main_process_loop(State1); get_status -> io:format("~p~n",[State]), main_process_loop(State) end.%%%----------------------------------------------------------------------%%% cover_server on remote node%%%----------------------------------------------------------------------init_remote(Starter,MainNode) -> register(?SERVER,self()), ets:new(?COVER_TABLE, [set, public, named_table]), Starter ! {self(),started}, remote_process_loop(#remote_state{main_node=MainNode}).remote_process_loop(State) -> receive {remote,load_compiled,Compiled} -> Compiled1 = load_compiled(Compiled,State#remote_state.compiled), remote_reply(State#remote_state.main_node, ok), remote_process_loop(State#remote_state{compiled=Compiled1}); {remote,unload,UnloadedModules} -> unload(UnloadedModules), Compiled = update_compiled(UnloadedModules, State#remote_state.compiled), remote_reply(State#remote_state.main_node, ok), remote_process_loop(State#remote_state{compiled=Compiled}); {remote,reset,Module} -> do_reset(Module), remote_reply(State#remote_state.main_node, ok), remote_process_loop(State); {remote,collect,Module,CollectorPid} -> MS = case Module of '_' -> ets:fun2ms(fun({M,C}) when is_atom(M) -> C end); _ -> ets:fun2ms(fun({M,C}) when M=:=Module -> C end) end, AllClauses = lists:flatten(ets:select(?COVER_TABLE,MS)), %% Sending clause by clause in order to avoid large lists lists:foreach( fun({M,F,A,C,_L}) -> Pattern = {#bump{module=M, function=F, arity=A, clause=C}, '_'}, Bumps = ets:match_object(?COVER_TABLE, Pattern), %% Reset lists:foreach(fun({Bump,_N}) -> ets:insert(?COVER_TABLE, {Bump,0}) end, Bumps), CollectorPid ! {chunk,Bumps} end, AllClauses), CollectorPid ! done, remote_reply(State#remote_state.main_node, ok), remote_process_loop(State); {remote,stop} -> reload_originals(State#remote_state.compiled), remote_reply(State#remote_state.main_node, ok); get_status -> io:format("~p~n",[State]), remote_process_loop(State); M -> io:format("WARNING: remote cover_server received\n~p\n",[M]), case M of {From,_} -> case is_from(From) of true -> reply(From,{error,not_main_node}); false -> ok end; _ -> ok end, remote_process_loop(State) end.reload_originals([{Module,_File}|Compiled]) -> do_reload_original(Module), reload_originals(Compiled);reload_originals([]) -> ok.do_reload_original(Module) -> case code:which(Module) of ?TAG -> code:purge(Module), % remove code marked as 'old' code:delete(Module), % mark cover compiled code as 'old' %% Note: original beam code must be loaded before the cover %% compiled code is purged, in order to for references to %% 'fun M:F/A' and %% 'fun F/A' funs to be correct (they %% refer to (M:)F/A in the *latest* version of the module) code:load_file(Module), % load original code code:purge(Module); % remove cover compiled code _ -> ignore end.load_compiled([{Module,File,Binary,InitialTable}|Compiled],Acc) -> NewAcc = case code:load_binary(Module, ?TAG, Binary) of {module,Module} -> insert_initial_data(InitialTable), add_compiled(Module, File, Acc); _ -> Acc end, load_compiled(Compiled,NewAcc);load_compiled([],Acc) -> Acc.insert_initial_data([Item|Items]) -> ets:insert(?COVER_TABLE, Item), insert_initial_data(Items);insert_initial_data([]) -> ok. unload([Module|Modules]) -> do_clear(Module), do_reload_original(Module), unload(Modules);unload([]) -> ok.%%%----------------------------------------------------------------------%%% Internal functions%%%----------------------------------------------------------------------%%%--Handling of remote nodes--------------------------------------------%% start the cover_server on a remote noderemote_start(MainNode) -> case whereis(?SERVER) of undefined -> Starter = self(), Pid = spawn(fun() -> init_remote(Starter,MainNode) end), Ref = erlang:monitor(process,Pid), Return = receive {Pid,started} -> {ok,Pid}; {'DOWN', Ref, _Type, _Object, Info} -> {error,Info} end, erlang:demonitor(Ref), Return; Pid -> {error,{already_started,Pid}}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -