From 44d5bb0a532f6759e6b139ce032d38471995eb7c Mon Sep 17 00:00:00 2001 From: SisMaker <1713699517@qq.com> Date: Sun, 3 Jan 2021 15:58:00 +0800 Subject: [PATCH] =?UTF-8?q?=E9=83=A8=E5=88=86=E4=BB=A3=E7=A0=81=E6=95=B4?= =?UTF-8?q?=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/dynamicCompile/smerl.erl | 1045 ----------------- src/dynamicCompile/{mmake.erl => utMMake.erl} | 3 +- src/hotUpdate/h.erl | 4 +- src/{srvNodeMgr => measure}/test.erl | 0 src/{srvNodeMgr => measure}/tools.erl | 0 src/measure/utProf.erl | 22 + src/srvNodeMgr/main.erl | 149 --- src/srvNodeMgr/mmake.erl | 350 ------ src/srvNodeMgr/server_control_main.erl | 145 --- src/srvNodeMgr/utVMInfo.erl | 186 ++- 10 files changed, 110 insertions(+), 1794 deletions(-) delete mode 100644 src/dynamicCompile/smerl.erl rename src/dynamicCompile/{mmake.erl => utMMake.erl} (99%) rename src/{srvNodeMgr => measure}/test.erl (100%) rename src/{srvNodeMgr => measure}/tools.erl (100%) delete mode 100644 src/srvNodeMgr/main.erl delete mode 100644 src/srvNodeMgr/mmake.erl delete mode 100644 src/srvNodeMgr/server_control_main.erl diff --git a/src/dynamicCompile/smerl.erl b/src/dynamicCompile/smerl.erl deleted file mode 100644 index e81cb45..0000000 --- a/src/dynamicCompile/smerl.erl +++ /dev/null @@ -1,1045 +0,0 @@ -%% @author Yariv Sadan [http://yarivsblog.com] -%% @copyright Yariv Sadan 2006-2007 -%% -%% @doc Smerl: Simple Metaprogramming for Erlang -%% -%% Smerl is an Erlang library -%% that simplifies the creation and manipulation of Erlang modules in -%% runtime. -%% -%% You don't need to know Smerl in order to use ErlyWeb; Smerl -%% is included in ErlyWeb because ErlyWeb uses it internally. -%% -%% Smerl uses Erlang's capabilities for hot code swapping and -%% abstract syntax tree transformations to do its magic. Smerl is inspired by -%% the rdbms_codegen.erl module in the RDBMS application written by -%% Ulf Wiger. RDBMS is part of Jungerl ([http://jungerl.sf.net]). -%% -%% Here's a quick example illustrating how to use Smerl: -%% ``` -%% test_smerl() -> -%% M1 = smerl:new(foo), -%% {ok, M2} = smerl:add_func(M1, "bar() -> 1 + 1."), -%% smerl:compile(M2), -%% foo:bar(), % returns 2`` -%% smerl:has_func(M2, bar, 0). % returns true -%% ''' -%% -%% New functions can be expressed either as strings of Erlang code -%% or as abstract forms. For more information, read the Abstract Format -%% section in the ERTS User's guide -%% ([http://erlang.org/doc/doc-5.5/erts-5.5/doc/html/absform.html#4]). -%% -%% Using the abstract format, the 3rd line of the above example -%% would be written as -%% ``` -%% {ok,M2} = smerl:add_func(M1, {function,1,bar,0, -%% [{clause,1,[],[], -%% [{op,1,'+',{integer,1,1},{integer,1,1}}]}]). -%% ''' -%% -%%

The abstact format may look more verbose in this example, but -%% it's also easier to manipulate in code.

-%% - -%% Copyright (c) Yariv Sadan 2006-2007 -%% -%% Permission is hereby granted, free of charge, to any person -%% obtaining a copy of this software and associated documentation -%% files (the "Software"), to deal in the Software without restriction, -%% including without limitation the rights to use, copy, modify, merge, -%% publish, distribute, sublicense, and/or sell copies of the Software, -%% and to permit persons to whom the Software is furnished to do -%% so, subject to the following conditions: -%% -%% The above copyright notice and this permission notice shall be included -%% in all copies or substantial portions of the Software. -%% -%% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -%% EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -%% MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. -%% IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY -%% CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, -%% TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE -%% SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - - --module(smerl). --author("Yariv Sadan (yarivsblog@gmail.com, http://yarivsblog.com"). --export([new/1, - for_module/1, - for_module/2, - for_module/3, - for_file/1, - for_file/2, - for_file/3, - get_module/1, - set_module/2, - get_forms/1, - set_forms/2, - get_exports/1, - set_exports/2, - get_export_all/1, - set_export_all/2, - remove_export/3, - get_attribute/2, - add_func/2, - add_func/3, - remove_func/3, - has_func/3, - get_func/3, - replace_func/2, -% replace_func/3, - compile/1, - compile/2, - rename/2, - curry/2, - curry/4, - curry/5, - curry_add/3, - curry_add/4, - curry_add/5, - curry_add/6, - curry_replace/3, - curry_replace/4, - embed_params/2, - embed_params/4, - embed_params/5, - embed_all/2, - extend/2, - extend/3, - extend/4, - to_src/1, - to_src/2 -]). - --define(L(Obj), io:format("LOG ~s ~w ~p\n", [?FILE, ?LINE, Obj])). --define(S(Obj), io:format("LOG ~s ~w ~s\n", [?FILE, ?LINE, Obj])). - --include_lib("kernel/include/file.hrl"). - -%% @type meta_mod(). A data structure holding the abstract representation -%% for a module. -%% @type func_form(). The abstract form for the function, as described -%% in the ERTS Users' manual. - -%% The record type holding the abstract representation for a module. --record(meta_mod, {module, file, exports = [], forms = [], - export_all = false}). - -%% @doc Create a new meta_mod for a module with the given name. -%% -%% @spec new(Module::atom()) -> meta_mod() -new(ModuleName) when is_atom(ModuleName) -> - #meta_mod{module = ModuleName}. - - -%% @equiv for_module(ModuleName, []) -for_module(ModuleName) -> - for_module(ModuleName, []). - -%% @equiv for_module(ModuleName, IncludePaths, []) -for_module(ModuleName, IncludePaths) -> - for_module(ModuleName, IncludePaths, []). - -%% @doc Create a meta_mod tuple for an existing module. If ModuleName is a -%% string, it is interpreted as a file name (this is the same as calling -%% @{link smerl:for_file}). If ModuleName is an atom, Smerl attempts to -%% find its abstract represtation either from its source file or from -%% its .beam file directly (if it has been compiled with debug_info). -%% If the abstract representation can't be found, this function returns -%% an error. -%% -%% The IncludePaths parameter is used when 'ModuleName' is a file name. -%% -%% @spec for_module(ModuleName::atom() | string(), IncludePaths::[string()], -%% Macros::[{atom(), term()}]) -> -%% {ok, meta_mod()} | {error, Error} -for_module(ModuleName, IncludePaths, Macros) when is_list(ModuleName) -> - for_file(ModuleName, IncludePaths, Macros); -for_module(ModuleName, IncludePaths, Macros) when is_atom(ModuleName) -> - [_Module, _Exports, _Attributes, - {compile, [_Options, _Version, _Time, {source, Path}]}, - {native, _}, {md5, _}] = - ModuleName:module_info(), - case for_file(Path, IncludePaths, Macros) of - {ok, _Mod} = Res -> - Res; - _Err -> - case code:which(ModuleName) of - Path1 when is_list(Path1) -> - case get_forms(ModuleName, Path1) of - {ok, Forms} -> - mod_for_forms(Forms); - _Other -> - {error, {invalid_module, ModuleName}} - end; - _Err -> - {error, {invalid_module, ModuleName}} - end - end. - -%% @equiv for_file(SrcFilePath, []) -for_file(SrcFilePath) -> - for_file(SrcFilePath, []). - -%% @equiv for_file(SrcFilePath, IncludePaths, []) -for_file(SrcFilePath, IncludePaths) -> - for_file(SrcFilePath, IncludePaths, []). - -%% @doc Create a meta_mod for a module from its source file. -%% -%% @spec for_file(SrcFilePath::string(), IncludePaths::[string()], -%% Macros::[{atom(), term()}]) -> -%% {ok, meta_mod()} | {error, invalid_module} -for_file(SrcFilePath, IncludePaths, Macros) -> - case epp:parse_file(SrcFilePath, [filename:dirname(SrcFilePath) | - IncludePaths], Macros) of - {ok, Forms} -> - mod_for_forms(Forms); - _err -> - {error, {invalid_module, SrcFilePath}} - end. - -mod_for_forms([{attribute, _, file, {FileName, _FileNum}}, - {attribute, _, module, ModuleName} | Forms]) -> - {Exports, OtherForms, ExportAll} = - lists:foldl( - fun({attribute, _, export, ExportList}, - {ExportsAcc, FormsAcc, ExportAll}) -> - {ExportList ++ ExportsAcc, FormsAcc, ExportAll}; - ({attribute, _, compile, export_all}, - {ExportsAcc, FormsAcc, _ExportAll}) -> - {ExportsAcc, FormsAcc, true}; - ({eof, _}, Acc) -> - Acc; - (Form, {ExportsAcc, FormsAcc, ExportAll}) -> - {ExportsAcc, [Form | FormsAcc], ExportAll} - end, {[], [], false}, Forms), - {ok, #meta_mod{module = ModuleName, - file = FileName, - exports = Exports, - forms = OtherForms, - export_all = ExportAll - }}; -mod_for_forms(Mod) -> - {error, {invalid_module, Mod}}. - -%% @doc Return the module name for the meta_mod. -%% -%% @spec(MetaMod::meta_mod()) -> atom() -get_module(MetaMod) -> - MetaMod#meta_mod.module. - -%% @doc Set the meta_mod's module name. -%% -%% @spec set_module(MetaMod::meta_mod(), NewName::atom()) -> -%% NewMod::meta_mod() -set_module(MetaMod, NewName) -> - MetaMod#meta_mod{module = NewName}. - -%% @doc Return the list of function forms in the meta_mod. -%% -%% @spec get_forms(MetaMod::meta_mod()) -> [Form] -get_forms(MetaMod) -> - MetaMod#meta_mod.forms. - -set_forms(MetaMod, Forms) -> - MetaMod#meta_mod{forms = Forms}. - -%% @doc Return the list of exports in the meta_mod. -%% -%% @spec get_exports(MetaMod::meta_mod()) -> -%% [{FuncName::atom(), Arity::integer()}] -get_exports(MetaMod) -> - case MetaMod#meta_mod.export_all of - false -> - MetaMod#meta_mod.exports; - true -> - lists:foldl( - fun({function, _L, Name, Arity, _Clauses}, Exports) -> - [{Name, Arity} | Exports]; - (_Form, Exports) -> - Exports - end, [], MetaMod#meta_mod.forms) - end. - -%% @doc Set the meta_mod's export list to the new list. -%% -%% @spec set_exports(MetaMod::meta_mod(), -%% Exports::[{FuncName::atom(), Arity::integer()}]) -> NewMod::meta_mod() -set_exports(MetaMod, Exports) -> - MetaMod#meta_mod{exports = Exports}. - -%% @doc Get the export_all value for the module. -%% -%% @spec get_export_all(MetaMod::meta_mod) -> true | false -get_export_all(MetaMod) -> - MetaMod#meta_mod.export_all. - -%% @doc Set the export_all value for the module. -%% -%% @spec set_export_all(MetaMod::meta_mod(), Val::true | false) -> -%% NewMetaMod::meta_mod() -set_export_all(MetaMod, Val) -> - MetaMod#meta_mod{export_all = Val}. - -%% @doc Remove the export from the list of exports in the meta_mod. -%% -%% @spec remove_export(MetaMod::meta_mod(), FuncName::atom(), -%% Arity::integer()) -> NewMod::meta_mod() -remove_export(MetaMod, FuncName, Arity) -> - MetaMod#meta_mod{exports = - lists:delete({FuncName, Arity}, - MetaMod#meta_mod.exports)}. - -%% @doc Get the value a the module's attribute. -%% -%% @spec get_attribute(MetaMod::meta_mod(), AttName::atom()) -> -%% {ok, Val} | error -get_attribute(MetaMod, AttName) -> - case lists:keysearch(AttName, 3, get_forms(MetaMod)) of - {value, {attribute, _, _, Val}} -> - {ok, Val}; - _ -> error - end. - - -%% Get the abstract representation, if available, for the module. -%% -%% Strategy: -%% 1) Try to get the abstract code from the module if it's compiled -%% with debug_info. -%% 2) Look for the source file in the beam file's directory. -%% 3) If the file's directory ends with 'ebin', then search in -%% [beamdir]/../src -get_forms(Module, Path) -> - case beam_lib:chunks(Path, [abstract_code]) of - {ok, {_, [{abstract_code, {raw_abstract_v1, Forms}}]}} -> - {ok, Forms}; - _Err -> - case filename:find_src(Module, [{"ebin", "src"}]) of - {error, _} = Err -> - get_forms_from_binary(Module, Err); - {SrcPath, _} -> - Filename = SrcPath ++ ".erl", - epp:parse_file(Filename, [filename:dirname(Filename)], []) - end - end. - -get_dirs_in_dir(Dir) -> - case file:list_dir(Dir) of - {error, _} -> - undefined; - {ok, Listing} -> - lists:foldl( - fun(Name, Acc) -> - Path = Dir ++ "/" ++ Name, - case file:read_file_info(Path) of - {ok, #file_info{type = directory}} -> [Path | Acc]; - _ -> Acc - end - end, [], Listing) - end. - -%% @doc Try to infer module source files from the beam code path. -get_forms_from_binary(Module, OrigErr) -> - Ret = - case code:where_is_file(atom_to_list(Module) ++ ".beam") of - non_existing -> - OrigErr; - Filename -> - %% We could automatically obtain a list of all dirs under this dir, but we just do - %% a hack for now. - Basedir = filename:dirname(Filename), - Lastdir = filename:basename(Basedir), - if Lastdir == "ebin" -> - Rootdir = filename:dirname(Basedir), - DirList = [Rootdir ++ "/src"], - get_forms_from_file_list(Module, Rootdir, - DirList ++ get_dirs_in_dir(Rootdir ++ "/src")); - true -> - DirList = [Basedir], - get_forms_from_file_list(Module, Basedir, DirList) - end - end, - if Ret == [] -> OrigErr; - true -> Ret - end. -get_forms_from_file_list(_Module, _Basedir, []) -> - []; -get_forms_from_file_list(Module, Basedir, [H | T]) -> - Filename = H ++ "/" ++ atom_to_list(Module) ++ ".erl", - case file:read_file_info(Filename) of - {ok, #file_info{type = regular}} -> - epp:parse_file(Filename, [filename:dirname(Filename)], []); - _ -> - get_forms_from_file_list(Module, Basedir, T) - end. - -%% @doc Add a new function to the meta_mod and return the resulting meta_mod. -%% This function calls add_func(MetaMod, Form, true). -%% -%% @spec add_func(MetaMod::meta_mod(), Form::func_form() | string()) -> -%% {ok, NewMod::meta_mod()} | {error, parse_error} -add_func(MetaMod, Form) -> - add_func(MetaMod, Form, true). - -%% @doc Add a new function to the meta_mod and return the new MetaMod -%% record. Export is a boolean variable indicating if the function should -%% be added to the module's exports. -%% -%% @spec add_func(MetaMod::meta_mod(), Func::func_form() | string(), -%% Export::boolean()) -> -%% {ok, NewMod::meta_mod()} | {error, parse_error} -add_func(MetaMod, Func, Export) when is_list(Func) -> - case parse_func_string(Func) of - {ok, Form} -> - add_func(MetaMod, Form, Export); - Err -> - Err - end; -add_func(MetaMod, {function, _Line, FuncName, Arity, _Clauses} = Form, - true) -> - Foo = {ok, MetaMod#meta_mod{ - exports = [{FuncName, Arity} | MetaMod#meta_mod.exports], - forms = [Form | MetaMod#meta_mod.forms] - }}, - Foo; -add_func(MetaMod, {function, _Line, _FuncName, _Arity, _Clauses} = Form, - false) -> - {ok, MetaMod#meta_mod{forms = [Form | MetaMod#meta_mod.forms]}}; - -%%add_func(MetaMod, Name, Fun) when is_function(Fun) -> -%% add_func(MetaMod, Name, Fun, true); - -add_func(_, _, _) -> - {error, parse_error}. - -%% add_func(MetaMod, Name, Fun, Export) when is_function(Fun) -> -%% case form_for_fun(Name, Fun) of -%% {ok, Form} -> -%% add_func(MetaMod, Form, Export); -%% Err -> -%% Err -%% end. - -%% form_for_fun(Name, Fun) -> -%% Line = 999, -%% Info = erlang:fun_info(Fun), -%% case Info of -%% [{module, _ModName}, _FuncName, _Arity, _Env, {type, external}] -> -%% {error, cant_add_external_funcs}; -%% [_Pid, _Module, _NewIdx, _NewUniq, _Index, _Uniq, _Name, -%% {arity, Arity}, -%% {env, [Vars, _Unknown1, _Unknown2, Clauses]}, -%% {type, local}] -> -%% EnvVars = lists:map( -%% fun({VarName, Val}) -> -%% {match,Line,{var,Line,VarName}, -%% erl_parse:abstract(Val)} -%% end, Vars), -%% NewClauses = lists:map( -%% fun({clause, Line1, Params, Guards, Exprs}) -> -%% {clause, Line1, Params, Guards, -%% EnvVars ++ Exprs} -%% end, Clauses), -%% {ok, {function, Line, Name, Arity, NewClauses}}; -%% _Other -> -%% {error, bad_fun} -%% end. - - -parse_func_string(Func) -> - case erl_scan:string(Func) of - {ok, Toks, _} -> - case erl_parse:parse_form(Toks) of - {ok, _Form} = Res -> - Res; - _Err -> - {error, parse_error} - end; - _Err -> - {error, parse_error} - end. - -%% @doc Try to remove the function from the meta_mod. -%% If the function exists, the new meta_mod is returned. Otherwise, -%% original meta_mod is returned. -%% -%% @spec remove_func(MetaMod::meta_mod(), FuncName::string(), Arity::integer()) -%% -> NewMod::meta_mod() -%% -remove_func(MetaMod, FuncName, Arity) -> - MetaMod#meta_mod{forms = - lists:filter( - fun({function, _Line, FuncName1, Arity1, _Clauses}) - when FuncName1 =:= FuncName, Arity =:= Arity1 -> - false; - (_) -> - true - end, MetaMod#meta_mod.forms), - exports = - lists:filter( - fun({FuncName1, Arity1}) - when FuncName1 =:= FuncName, - Arity1 =:= Arity -> - false; - (_) -> - true - end, MetaMod#meta_mod.exports) - }. - -%% @doc Check whether the meta_mod has a function with the given name -%% and arity. -%% @spec has_func(MetaMod::meta_mod(), FuncName::atom(), Arity::integer()) -> -%% bool() -has_func(MetaMod, FuncName, Arity) -> - lists:any(fun({function, _Line, FuncName1, Arity1, _Clauses}) - when FuncName1 == FuncName, Arity1 == Arity -> - true; - (_) -> - false - end, MetaMod#meta_mod.forms). - - -%% @doc Get the form for the function with the specified arity in the -%% meta_mod. -%% -%% @spec get_func(MetaMod::meta_mod() | atom(), -%% FuncName::atom(), Arity::integer()) -> -%% {ok, func_form()} | {error, Err} -get_func(Module, FuncName, Arity) when is_atom(Module) -> - case smerl:for_module(Module) of - {ok, C1} -> - get_func(C1, FuncName, Arity); - Err -> - Err - end; -get_func(MetaMod, FuncName, Arity) -> - get_func2(MetaMod#meta_mod.forms, FuncName, Arity). - -get_func2([], FuncName, Arity) -> - {error, {function_not_found, {FuncName, Arity}}}; -get_func2([{function, _Line, FuncName, Arity, _Clauses} = Form | _Rest], - FuncName, Arity) -> - {ok, Form}; -get_func2([_Form | Rest], FuncName, Arity) -> - get_func2(Rest, FuncName, Arity). - - -%% @doc -%% Replace an existing function with the new one. If the function doesn't exist -%% the new function is added to the meta_mod. -%% This is tantamount to calling smerl:remove_func followed by smerl:add_func. -%% -%% @spec replace_func(MetaMod::meta_mod(), Function::string() | func_form()) -> -%% {ok, NewMod::meta_mod()} | {error, Error} -replace_func(MetaMod, Function) when is_list(Function) -> - case parse_func_string(Function) of - {ok, Form} -> - replace_func(MetaMod, Form); - Err -> - Err - end; -replace_func(MetaMod, {function, _Line, FuncName, Arity, _Clauses} = Form) -> - Mod1 = remove_func(MetaMod, FuncName, Arity), - add_func(Mod1, Form); -replace_func(_MetaMod, _) -> - {error, parse_error}. - -%% %% @doc Simliar to replace_func/2, but accepts a function -%% %% name + fun expression. -%% %% -%% %% @spec replace_func(MetaMod::meta_mod(), Name::atom(), Fun::function()) -> -%% %% {ok, NewMod::meta_mod()} | {error, Error} -%% replace_func(MetaMod, Name, Fun) when is_function(Fun) -> -%% case form_for_fun(Name, Fun) of -%% {ok, Form} -> -%% replace_func(MetaMod, Form); -%% Err -> -%% Err -%% end. - -%% @doc Compile the module represented by the meta_mod and load the -%% resulting BEAM into the emulator. This function calls -%% compile(MetaMod, [report_errors, report_warnings]). -%% -%% @spec compile(MetaMod::meta_mod()) -> ok | {error, Error} -compile(MetaMod) -> - compile(MetaMod, undefined). - -%% @doc Compile the module represented by the meta_mod and load the -%% resulting BEAM into the emulator. 'Options' is a list of options as -%% described in the 'compile' module in the Erlang documentation. -%% -%% If the 'outdir' option is provided, -%% the .beam file is written to the destination directory. -%% -%% @spec compile(MetaMod::meta_mod(), Options::[term()]) -> ok | {error, Error} -compile(MetaMod, undefined) -> - compile(MetaMod, [report_errors, report_warnings, - return_errors]); - -compile(MetaMod, Options) -> - Forms = [{attribute, 2, module, MetaMod#meta_mod.module}, - {attribute, 3, export, get_exports(MetaMod)}], - FileName = - case MetaMod#meta_mod.file of - undefined -> atom_to_list(get_module(MetaMod)); - Val -> Val - end, - - Forms1 = [{attribute, 1, file, {FileName, 1}} | Forms], - Forms2 = Forms1 ++ lists:reverse(MetaMod#meta_mod.forms), - - case compile:forms(Forms2, Options) of - {ok, Module, Bin} -> - Res = - case lists:keysearch(outdir, 1, Options) of - {value, {outdir, OutDir}} -> - file:write_file( - OutDir ++ - ['/' | atom_to_list(MetaMod#meta_mod.module)] ++ - ".beam", Bin); - false -> ok - end, - case Res of - ok -> - code:purge(Module), - case code:load_binary( - Module, - atom_to_list(Module) ++ ".erl", Bin) of - {module, _Module} -> - ok; - Err -> - Err - end; - Err -> - Err - end; - Err -> - Err - end. - -%% @doc Change the name of the function represented by the form. -%% -%% @spec rename(Form::func_form(), NewName::atom()) -> -%% {ok, NewForm::func_form()} | {error, Err} -rename({function, Line, _Name, Arity, Clauses}, NewName) -> - {function, Line, NewName, Arity, Clauses}. - - -%% @doc Get the curried form for the function and parameter(s). Currying -%% involves replacing one or more of the function's leading parameters -%% with predefined values. -%% -%% @spec curry(Form::func_form(), Param::term() | [term()]) -> -%% {ok, NewForm::func_form()} | {error, Err} -curry(Form, Param) when not is_list(Param) -> - curry(Form, [Param]); -curry({function, _Line, _Name, Arity, _Clauses}, Params) - when length(Params) > Arity -> - {error, too_many_params}; -curry({function, Line, Name, Arity, Clauses}, NewParams) -> - NewClauses = - lists:foldl( - fun(Clause, Clauses1) -> - [curry_clause(Clause, NewParams) | Clauses1] - end, [], Clauses), - {ok, {function, Line, Name, Arity - length(NewParams), NewClauses}}. - -curry_clause({clause, L1, ExistingParams, Guards, _Exprs} = Clause, - NewParams) -> - {FirstParams, LastParams} = - lists:split(length(NewParams), ExistingParams), -%% Matches = -%% lists:foldl( -%% fun({Var, NewVal}, Acc) -> -%% [{match, 1, Var, erl_parse:abstract(NewVal)} | Acc] -%% end, [], lists:zip(FirstParams, NewParams)), -%% {clause, L1, LastParams, Guards, Matches ++ Exprs}. - - Vals = - lists:foldl( - fun({{var, _, Name}, NewVal}, Acc) -> - [{Name, erl_parse:abstract(NewVal)} | Acc]; - (_, Acc) -> - Acc - end, [], lists:zip(FirstParams, NewParams)), - - NewExprs = replace_vars(Clause, Vals), - - {clause, L1, LastParams, Guards, NewExprs}. - -replace_vars(Clause, Vals) -> - Tree = - erl_syntax_lib:map( - fun({var, _L2, Name} = Expr) -> - case proplists:lookup(Name, Vals) of - none -> - Expr; - {_, Val} -> - Val - end; - (Expr) -> - Expr - end, Clause), - {clause, _, _, _, NewExprs} = erl_syntax:revert(Tree), - NewExprs. - - -%% @doc Curry the function from the module with the given param(s) -%% -%% @spec curry(ModName::atom(), Name::atom(), Arity::integer(), -%% Params::term() | [term()]) -> -%% {ok, NewForm} | {error, Err} -curry(ModName, Name, Arity, Params) when is_atom(ModName) -> - case for_module(ModName) of - {ok, MetaMod} -> - curry(MetaMod, Name, Arity, Params); - Err -> - Err - end; - -%% @doc Curry the function from the meta_mod with -%% the given param(s) -%% -%% @spec curry(MetaMod::meta_mod(), Name::atom(), arity::integer(), -%% Params::term() | [terms()]) -> -%% {ok, NewForm} | {error, Err} -curry(MetaMod, Name, Arity, Params) -> - case get_func(MetaMod, Name, Arity) of - {ok, Form} -> - curry(Form, Params); - Err -> - Err - end. - - -%% @doc Curry the function from the module or meta_mod -%% with the param(s), and return its renamed form. -%% -%% @spec curry(Module::atom() | meta_mod(), Name::atom(), Arity::integer(), -%% Params::term() | [terms()], NewName::atom()) -> -%% {ok, NewForm} | {error, Err} -curry(Module, Name, Arity, Params, NewName) -> - case curry(Module, Name, Arity, Params) of - {ok, NewForm} -> - {ok, rename(NewForm, NewName)}; - Err -> - Err - end. - - -%% @doc Add the curried form of the function in the meta_mod -%% with its curried form. -%% -%% @spec curry_add(MetaMod::meta_mod(), Form::func_form(), -%% Params::term() | [term()]) -> -%% {ok, NewMetaMod::meta_mod()} | {error, Err} -curry_add(MetaMod, {function, _Line, Name, Arity, _Clauses}, Params) -> - curry_add(MetaMod, Name, Arity, Params). - -%% @doc Add the curried form of the function -%% in the meta_mod with its curried form. -%% -%% @spec curry_add(MetaMod::meta_mod(), Name::atom(), Arity::integer(), -%% Params::term() | [term()]) -> -%% {ok, NewMetaMod::meta_mod()} | {error, Err} -curry_add(MetaMod, Name, Arity, Params) -> - curry_change(MetaMod, Name, Arity, Params, false). - -%% @doc Curry the function form from the meta_mod, then add it -%% to the other meta_mod with a new name. -%% -%% @spec curry_add(MetaMod::meta_mod(), Name::atom(), Arity::integer(), -%% Params::[term()], NewName::atom()) -> {ok, NewMod::meta_mod()} | -%% {error, Err} -curry_add(MetaMod, Name, Arity, Params, NewName) -> - curry_add(MetaMod, MetaMod, Name, Arity, Params, NewName). - -%% @doc Curry the function in the module, rename the curried form, and -%% add it to the meta_mod. -%% -%% @spec curry_add(MetaMod::meta_mod(), Module::atom() | meta_mod(), -%% Name::atom(), Arity::integer(), Params::term() | [term()], -%% NewName::atom()) -> -%% {ok, NewMod::meta_mod()} | {error, Error} -curry_add(MetaMod, Module, Name, Arity, Params, NewName) -> - case curry(Module, Name, Arity, Params, NewName) of - {ok, Form} -> - add_func(MetaMod, Form); - Err -> - Err - end. - -curry_change(MetaMod, Name, Arity, Params, Remove) -> - case get_func(MetaMod, Name, Arity) of - {ok, OldForm} -> - case curry(OldForm, Params) of - {ok, NewForm} -> - MetaMod1 = - case Remove of - true -> - remove_func(MetaMod, Name, Arity); - false -> - MetaMod - end, - add_func(MetaMod1, NewForm); - Err -> - Err - end; - Err -> - Err - end. - -%% @doc Replace the function in the meta_mod with -%% its curried form. -%% -%% @spec curry_replace(MetaMod::meta_mod(), Form::func_form(), -%% Params::term() | [term()]) -> -%% {ok, NewMetaMod::meta_mod()} | {error, Err} -curry_replace(MetaMod, {function, _Line, Name, Arity, _Clauses}, Params) -> - curry_replace(MetaMod, Name, Arity, Params). - - -%% @doc Replace the function in the meta_mod with -%% its curried form. -%% -%% @spec curry_replace(MetaMod::meta_mod(), Name::atom(), -%% Arity::integer(), Params::term() | list()) -> -%% {ok, NewMetaMod::meta_mod()} | {error, Err} -curry_replace(MetaMod, Name, Arity, Params) -> - curry_change(MetaMod, Name, Arity, Params, true). - - -%% @doc This function takes a function form and list of name/value pairs, -%% and replaces all the function's parameters that whose names match an -%% element from the list with the predefined value. -%% -%% @spec embed_params(Func::func_form(), -%% Vals::[{Name::atom(), Value::term()}]) -> NewForm::func_form() -embed_params({function, L, Name, Arity, Clauses}, Vals) -> - NewClauses = - lists:map( - fun({clause, L1, Params, Guards, _Exprs} = Clause) -> - {EmbeddedVals, OtherParams} = - lists:foldr( - fun({var, _, VarName} = Param, {Embedded, Rest}) -> - case proplists:lookup(VarName, Vals) of - none -> - {Embedded, [Param | Rest]}; - {_, Val} -> - {[{VarName, erl_parse:abstract(Val)} | - Embedded], Rest} - end; - (Param, {Embedded, Rest}) -> - {Embedded, [Param | Rest]} - end, {[], []}, Params), - NewExprs = replace_vars(Clause, EmbeddedVals), - {clause, L1, OtherParams, Guards, NewExprs} - - -%% {Params1, Matches1, _RemainingVals} = -%% lists:foldl( -%% fun({var, _L2, ParamName} = Param, -%% {Params2, Matches2, Vals1}) -> -%% case lists:keysearch(ParamName, 1, Vals1) of -%% {value, {_Name, Val} = Elem} -> -%% Match = {match, L1, Param, -%% erl_parse:abstract(Val)}, -%% {Params2, [Match | Matches2], -%% lists:delete(Elem, Vals1)}; -%% false -> -%% {[Param | Params2], Matches2, Vals1} -%% end; -%% (Param, {Params2, Matches2, Vals1}) -> -%% {[Param | Params2], Matches2, Vals1} -%% end, {[], [], Vals}, Params), -%% [{clause, L1, lists:reverse(Params1), Guards, -%% lists:reverse(Matches1) ++ Exprs} | Clauses1] - end, Clauses), - NewArity = - case NewClauses of - [{clause, _L2, Params, _Guards, _Exprs} | _] -> - length(Params); - _ -> - Arity - end, - {function, L, Name, NewArity, NewClauses}. - -%% @doc Apply {@link embed_params/2} to a function from the meta_mod and -%% add the resulting function to the meta_mod, and return the resulting -%% meta_mod. -%% -%% @spec embed_params(MetaMod::meta_mod(), Name::atom(), Arity::integer(), -%% Values::proplist()) -> {ok, NewMetaMod::meta_mod()} | {error, Err} -embed_params(MetaMod, Name, Arity, Values) -> - embed_params(MetaMod, Name, Arity, Values, Name). - -%% @doc Apply embed_params/2 to the function from the meta_mod and -%% add the resulting function to the meta_mod after renaming the function. -%% -%% @spec embed_params(MetaMod::meta_mod(), Name::atom(), Arity::integer(), -%% Values::proplist(), NewName::atom()) -> -%% {ok, NewMetaMod::meta_mod()} | {error, Err} -embed_params(MetaMod, Name, Arity, Values, NewName) -> - case get_func(MetaMod, Name, Arity) of - {ok, Form} -> - NewForm = embed_params(Form, Values), - add_func(MetaMod, rename(NewForm, NewName)); - Err -> - Err - end. - - -%% @doc Apply the embed_params function with the list of {Name, Value} -%% pairs to all forms in the meta_mod. Exports -%% for functions whose arities change due to the embedding are preserved. -%% -%% @spec embed_all(MetaMod::meta_mod(), Vals::[{Name::atom(), -%% Value::term()}]) -> NewMetaMod::meta_mod() -embed_all(MetaMod, Vals) -> - Forms = get_forms(MetaMod), - Exports = get_exports(MetaMod), - {NewForms, Exports3, NewExports} = - lists:foldl( - fun({function, _L, Name, Arity, _Clauses} = Form, - {Forms1, Exports1, NewExports1}) -> - {function, _, _, NewArity, _} = NewForm = - embed_params(Form, Vals), - Exports2 = lists:delete({Name, Arity}, Exports1), - NewExports2 = - case length(Exports2) == length(Exports1) of - true -> - NewExports1; - false -> - [{Name, NewArity} | NewExports1] - end, - {[NewForm | Forms1], Exports2, NewExports2}; - (Form, {Forms1, Exports1, NewExports1}) -> - {[Form | Forms1], Exports1, NewExports1} - end, {[], Exports, []}, Forms), - #meta_mod{module = get_module(MetaMod), - exports = Exports3 ++ NewExports, - forms = lists:reverse(NewForms), - export_all = get_export_all(MetaMod)}. - -%% @doc extend/2 -%% Add all the parent module's functions that are missing from the child -%% module to the child module. The new functions in the child module are -%% shallow: they have the name and arity as their corresponding functions in -%% the parent meta_mod, but instead of implementing their logic they call -%% the parent module's functions. -%% -%% @spec extend(Parent::atom() | meta_mod(), Child::atom() | meta_mod()) -> -%% NewChildMod::meta_mod() -extend(Parent, Child) -> - extend(Parent, Child, 0). - -%% @doc Similar to extend/2, with the addition of the 'ArityDiff' parameter, -%% which indicates the difference -%% in arities Smerl should use when figuring out which functions to -%% generate based on the modules' exports. This is sometimes -%% useful when calling extend() followed by embed_all(). -%% -%% @spec extend(Parent::atom() | meta_mod(), Child::atom() | meta_mod(), -%% ArityDiff::integer()) -> -%% NewChildMod::meta_mod() -extend(Parent, Child, ArityDiff) -> - extend(Parent, Child, ArityDiff, []). - -extend(Parent, Child, ArityDiff, Options) -> - {{ParentName, ParentExports, ParentMod}, ChildMod} = - get_extend_data(Parent, Child), - ChildExports = get_exports(ChildMod), - ChildExports1 = [{ExportName, ExportArity + ArityDiff} || - {ExportName, ExportArity} <- - ChildExports], - ExportsDiff = ParentExports -- ChildExports1, - NewChild = - lists:foldl( - fun({FuncName, Arity}, ChildMod1) -> - Func = - case lists:member(copy, Options) of - true -> - {ok, ParentFunc} = - smerl:get_func(ParentMod, FuncName, Arity), - ParentFunc; - _ -> - Params = get_params( - ParentMod, FuncName, Arity), - Clause1 = - {clause, 1, Params, [], - [{call, 1, - {remote, 1, {atom, 1, ParentName}, - {atom, 1, FuncName}}, - Params}]}, - {function, 1, FuncName, Arity, [Clause1]} - end, - {ok, ChildMod2} = add_func(ChildMod1, Func), - ChildMod2 - end, ChildMod, ExportsDiff), - NewChild. - -get_extend_data(Parent, Child) when is_atom(Parent) -> - [{exports, Exports} | _] = Parent:module_info(), - Exports1 = Exports -- [{module_info, 0}], - Exports2 = Exports1 -- [{module_info, 1}], - ParentMod = case smerl:for_module(Parent) of - {ok, M} -> M; - {error, _} -> undefined - end, - get_extend_data({Parent, Exports2, ParentMod}, Child); -get_extend_data(Parent, Child) when is_record(Parent, meta_mod) -> - get_extend_data({get_module(Parent), - get_exports(Parent), - Parent}, Child); -get_extend_data(Parent, Child) when is_list(Parent) -> - case for_file(Parent) of - {ok, M1} -> - get_extend_data(M1, Child); - Err -> - Err - end; -get_extend_data({_, _, _} = ParentData, Child) when is_atom(Child); - is_list(Child) -> - case for_module(Child) of - {ok, MetaMod} -> - {ParentData, MetaMod}; - Err -> - Err - end; -get_extend_data(ParentData, Child) when is_record(Child, meta_mod) -> - {ParentData, Child}. - -get_params(_, _, 0) -> []; -get_params(undefined, _FuncName, Arity) -> - [{var, 1, list_to_atom("P" ++ integer_to_list(Num))} - || Num <- lists:seq(1, Arity)]; -get_params(ParentMod, FuncName, Arity) -> - {ok, {function, _L, _Name, _Arity, - [{clause, _, Params, _Guards, _Exprs} | _]}} = - get_func(ParentMod, FuncName, Arity), - Params. - - -%% @doc Return the pretty-printed source code for the module. -%% -%% @spec to_src(MetaMod::meta_mod()) -> string() -to_src(MetaMod) -> - ExportsForm = - {attribute, 1, export, get_exports(MetaMod)}, - AllForms = [{attribute, 1, module, get_module(MetaMod)}, ExportsForm | - get_forms(MetaMod)], - erl_prettypr:format(erl_syntax:form_list(AllForms)). - -%% @doc Write the pretty printed source code for the module -%% to the file with the given file name. -%% -%% @spec to_src(MetaMod::meta_mod(), FileName::string()) -> -%% ok | {error, Error} -to_src(MetaMod, FileName) -> - Src = to_src(MetaMod), - file:write_file(FileName, list_to_binary(Src)). diff --git a/src/dynamicCompile/mmake.erl b/src/dynamicCompile/utMMake.erl similarity index 99% rename from src/dynamicCompile/mmake.erl rename to src/dynamicCompile/utMMake.erl index d1d4c1e..47f9471 100644 --- a/src/dynamicCompile/mmake.erl +++ b/src/dynamicCompile/utMMake.erl @@ -4,7 +4,7 @@ %% 其中mods也可以包含多个模块,当大于1个时, %% 可以启动多个process进行编译,从而提高编译速度. --module(mmake). +-module(utMMake). -include_lib("kernel/include/file.hrl"). -export([ @@ -20,7 +20,6 @@ all(Worker) when is_integer(Worker) -> all(Worker, []). all(Worker, Options) when is_integer(Worker) -> - %% io:format("make all Options:~w", [Options]), {MakeOpts, CompileOpts} = sort_options(Options, [], []), case read_emakefile('Emakefile', CompileOpts) of Files when is_list(Files) -> diff --git a/src/hotUpdate/h.erl b/src/hotUpdate/h.erl index 0af444f..63df0b4 100644 --- a/src/hotUpdate/h.erl +++ b/src/hotUpdate/h.erl @@ -46,10 +46,10 @@ h() -> hh() -> StartTime = unixtime(), - c:l(mmake), + c:l(utMMake), io:format("----------makes----------~n", []), c:cd("../"), - Res = (catch mmake:all(8, [netload])), + Res = (catch utMMake:all(8, [netload])), c:cd("ebin"), EndTime = unixtime(), io:format("MMake result = ~p~nMMake Time : ~p s", [Res, EndTime - StartTime]), diff --git a/src/srvNodeMgr/test.erl b/src/measure/test.erl similarity index 100% rename from src/srvNodeMgr/test.erl rename to src/measure/test.erl diff --git a/src/srvNodeMgr/tools.erl b/src/measure/tools.erl similarity index 100% rename from src/srvNodeMgr/tools.erl rename to src/measure/tools.erl diff --git a/src/measure/utProf.erl b/src/measure/utProf.erl index 96eb9e1..8eacbf5 100644 --- a/src/measure/utProf.erl +++ b/src/measure/utProf.erl @@ -43,3 +43,25 @@ fprof(M, F, A) -> fprof:profile(), fprof:analyse([{dest, "fprof.analysis"}, {sort, own}]), fprof:stop(). + +%% ==================================================================== +%% trace 日志 +%% 会把mod 每次调用详细MFA log 下来,args 太大就不好看了 +%% trace Mod 所有方法的调用 +%% -------------------------------------------------------------------- +trace(Mod) -> + dbg:tracer(), + dbg:tpl(Mod, '_', []), + dbg:p(all, c). + +%% trace Node上指定 Mod 所有方法的调用, 结果将输出到本地shell +trace(Node, Mod) -> + dbg:tracer(), + dbg:n(Node), + dbg:tpl(Mod, '_', []), + dbg:p(all, c). + +%% 停止trace +trace_stop() -> + dbg:stop_clear(). +%% ==================================================================== diff --git a/src/srvNodeMgr/main.erl b/src/srvNodeMgr/main.erl deleted file mode 100644 index 92ab8ee..0000000 --- a/src/srvNodeMgr/main.erl +++ /dev/null @@ -1,149 +0,0 @@ --module(main). - --export([ - server_start/0, - server_stop/0, - server_stop/1, - is_running/0, - is_running/1, - reload/0, - info/0, - psl/0, - psl/1, - psl/2, - get_info/0 -]). - --define(SERVER_APPS, [sasl, crypto, inets, ranch, os_mon, hot]). - -server_start() -> - ok = application:load(hot), - app_misc:init(), - ok = start_applications(?SERVER_APPS). - -%% 加载更新 -reload() -> - reloader:reload_all(). - -%%停止游戏服务器 -server_stop() -> - server_stop(30). - -is_running() -> - is_running(node()). - -is_running(Node) -> - node_misc:is_process_running(Node, server). - -%%停止游戏服务器 -server_stop(_SleepSeconds) -> - app_misc:pause_accept(), - stop_applications(?SERVER_APPS), - ok. - -info() -> - io:format("abormal termination: - ~n Scheduler id: ~p - ~n Num scheduler: ~p - ~n Process count: ~p - ~n Process limit: ~p - ~n Memory used by erlang processes: ~p - ~n Memory allocated by erlang processes: ~p - ~n The total amount of memory allocated: ~p - ~n", - get_info()), - ok. -get_info() -> - SchedId = erlang:system_info(scheduler_id), - SchedNum = erlang:system_info(schedulers), - ProcCount = erlang:system_info(process_count), - ProcLimit = erlang:system_info(process_limit), - ProcMemUsed = erlang:memory(processes_used), - ProcMemAlloc = erlang:memory(processes), - MemTot = erlang:memory(total), - [SchedId, SchedNum, ProcCount, ProcLimit, - ProcMemUsed, ProcMemAlloc, MemTot]. - -psl() -> - psl(100). - -psl(Num) -> - lists:foldl( - fun(P, true) -> - case erlang:process_info(P, message_queue_len) of - {message_queue_len, Count} when Count > Num -> - print_process_info(P), - false; - _ -> - true - end; - (_, false) -> - false - end, true, erlang:processes()). - -psl(ProcessPid, Num) -> - case erlang:process_info(ProcessPid, message_queue_len) of - {message_queue_len, Count} when Count > Num -> - print_process_info(ProcessPid); - _ -> - ok - end. - -print_process_info(P) -> - io:format("~n~n=====process info===~n" - "~p~n~p~n~p~n~p~n~p~n~p~n~p~n~p~n~p~n~p~n~n~n", - [P, - erlang:process_info(P, registered_name), - erlang:process_info(P, current_function), - erlang:process_info(P, message_queue_len), - erlang:process_info(P, status), - erlang:process_info(P, suspending), - erlang:process_info(P, last_calls), - erlang:process_info(P, links), - erlang:process_info(P, dictionary), - erlang:process_info(P, current_stacktrace) - ]). - -%%############辅助调用函数############## -manage_applications(Iterate, Do, Undo, SkipError, ErrorTag, Apps) -> - Iterate(fun(App, Acc) -> - case Do(App) of - ok -> [App | Acc];%合拢 - {error, {SkipError, _}} when is_atom(SkipError) -> - Acc; - {error, {Error, Reason}} when is_list(SkipError) -> - case lists:member(Error, SkipError) of - true -> - Acc; - false -> - io:format( - "App ~p, Reason ~p~n", [App, Reason]), - lists:foreach(Undo, Acc), - throw({error, {ErrorTag, App, Reason}}) - end; - {error, Reason} -> - io:format("App ~p, Reason ~p~n", [App, Reason]), - lists:foreach(Undo, Acc), - throw({error, {ErrorTag, App, Reason}}) - end - end, [], Apps), - ok. - -start_applications(Apps) -> - manage_applications(fun lists:foldl/3, - fun application:start/1, - fun application:stop/1, - [already_started, cannot_start_application], - cannot_start_application, - Apps). - -stop_applications(Apps) -> - io:format("stop_applications stopping.~n", []), - manage_applications(fun lists:foldr/3, - fun application:stop/1, - fun application:start/1, - not_started, - cannot_stop_application, - Apps). - - diff --git a/src/srvNodeMgr/mmake.erl b/src/srvNodeMgr/mmake.erl deleted file mode 100644 index ffaabcb..0000000 --- a/src/srvNodeMgr/mmake.erl +++ /dev/null @@ -1,350 +0,0 @@ -%% 多进程编译,修改自otp/lib/tools/src/make.erl -%% 解析Emakefile,根据获取{mods, options}列表, -%% 按照次序编译每项(解决编译顺序的问题) -%% 其中mods也可以包含多个模块,当大于1个时, -%% 可以启动多个process进行编译,从而提高编译速度. --module(mmake). --export([all/1, all/2, files/2, files/3]). - --include_lib("kernel/include/file.hrl"). - --define(MakeOpts, [noexec, load, netload, noload]). - -all(Worker) when is_integer(Worker) -> - all(Worker, []). - -all(Worker, Options) when is_integer(Worker) -> - {MakeOpts, CompileOpts} = sort_options(Options, [], []), - case read_emakefile('Emakefile', CompileOpts) of - Files when is_list(Files) -> - do_make_files(Worker, Files, MakeOpts); - error -> - error - end. - -files(Worker, Fs) -> - files(Worker, Fs, []). - -files(Worker, Fs0, Options) -> - Fs = [filename:rootname(F, ".erl") || F <- Fs0], - {MakeOpts, CompileOpts} = sort_options(Options, [], []), - case get_opts_from_emakefile(Fs, 'Emakefile', CompileOpts) of - Files when is_list(Files) -> - do_make_files(Worker, Files, MakeOpts); - error -> error - end. - -do_make_files(Worker, Fs, Opts) -> - %io:format("worker:~p~nfs:~p~nopts:~p~n", [Worker, Fs, Opts]), - process(Fs, Worker, lists:member(noexec, Opts), load_opt(Opts)). - -sort_options([H | T], Make, Comp) -> - case lists:member(H, ?MakeOpts) of - true -> - sort_options(T, [H | Make], Comp); - false -> - sort_options(T, Make, [H | Comp]) - end; -sort_options([], Make, Comp) -> - {Make, lists:reverse(Comp)}. - -%%% Reads the given Emakefile and returns a list of tuples: {Mods,Opts} -%%% Mods is a list of module names (strings) -%%% Opts is a list of options to be used when compiling Mods -%%% -%%% Emakefile can contain elements like this: -%%% Mod. -%%% {Mod,Opts}. -%%% Mod is a module name which might include '*' as wildcard -%%% or a list of such module names -%%% -%%% These elements are converted to [{ModList,OptList},...] -%%% ModList is a list of modulenames (strings) -read_emakefile(Emakefile, Opts) -> - case file:consult(Emakefile) of - {ok, Emake} -> - transform(Emake, Opts, [], []); - {error, enoent} -> - %% No Emakefile found - return all modules in current - %% directory and the options given at command line - Mods = [filename:rootname(F) || F <- filelib:wildcard("*.erl")], - [{Mods, Opts}]; - {error, Other} -> - io:format("make: Trouble reading 'Emakefile':~n~p~n", [Other]), - error - end. - -transform([{Mod, ModOpts} | Emake], Opts, Files, Already) -> - case expand(Mod, Already) of - [] -> - transform(Emake, Opts, Files, Already); - Mods -> - transform(Emake, Opts, [{Mods, ModOpts ++ Opts} | Files], Mods ++ Already) - end; -transform([Mod | Emake], Opts, Files, Already) -> - case expand(Mod, Already) of - [] -> - transform(Emake, Opts, Files, Already); - Mods -> - transform(Emake, Opts, [{Mods, Opts} | Files], Mods ++ Already) - end; -transform([], _Opts, Files, _Already) -> - lists:reverse(Files). - -expand(Mod, Already) when is_atom(Mod) -> - expand(atom_to_list(Mod), Already); -expand(Mods, Already) when is_list(Mods), not is_integer(hd(Mods)) -> - lists:concat([expand(Mod, Already) || Mod <- Mods]); -expand(Mod, Already) -> - case lists:member($*, Mod) of - true -> - Fun = fun(F, Acc) -> - M = filename:rootname(F), - case lists:member(M, Already) of - true -> Acc; - false -> [M | Acc] - end - end, - lists:foldl(Fun, [], filelib:wildcard(Mod ++ ".erl")); - false -> - Mod2 = filename:rootname(Mod, ".erl"), - case lists:member(Mod2, Already) of - true -> []; - false -> [Mod2] - end - end. - -%%% Reads the given Emakefile to see if there are any specific compile -%%% options given for the modules. -get_opts_from_emakefile(Mods, Emakefile, Opts) -> - case file:consult(Emakefile) of - {ok, Emake} -> - Modsandopts = transform(Emake, Opts, [], []), - ModStrings = [coerce_2_list(M) || M <- Mods], - get_opts_from_emakefile2(Modsandopts, ModStrings, Opts, []); - {error, enoent} -> - [{Mods, Opts}]; - {error, Other} -> - io:format("make: Trouble reading 'Emakefile':~n~p~n", [Other]), - error - end. - -get_opts_from_emakefile2([{MakefileMods, O} | Rest], Mods, Opts, Result) -> - case members(Mods, MakefileMods, [], Mods) of - {[], _} -> - get_opts_from_emakefile2(Rest, Mods, Opts, Result); - {I, RestOfMods} -> - get_opts_from_emakefile2(Rest, RestOfMods, Opts, [{I, O} | Result]) - end; -get_opts_from_emakefile2([], [], _Opts, Result) -> - Result; -get_opts_from_emakefile2([], RestOfMods, Opts, Result) -> - [{RestOfMods, Opts} | Result]. - -members([H | T], MakefileMods, I, Rest) -> - case lists:member(H, MakefileMods) of - true -> - members(T, MakefileMods, [H | I], lists:delete(H, Rest)); - false -> - members(T, MakefileMods, I, Rest) - end; -members([], _MakefileMods, I, Rest) -> - {I, Rest}. - - -%% Any flags that are not recognixed as make flags are passed directly -%% to the compiler. -%% So for example make:all([load,debug_info]) will make everything -%% with the debug_info flag and load it. -load_opt(Opts) -> - case lists:member(netload, Opts) of - true -> - netload; - false -> - case lists:member(load, Opts) of - true -> - load; - _ -> - noload - end - end. - -%% 处理 -process([{[], _Opts} | Rest], Worker, NoExec, Load) -> - process(Rest, Worker, NoExec, Load); -process([{L, Opts} | Rest], Worker, NoExec, Load) -> - Len = length(L), - Worker2 = erlang:min(Len, Worker), - case catch do_worker(L, Opts, NoExec, Load, Worker2) of - error -> - error; - ok -> - process(Rest, Worker, NoExec, Load) - end; -process([], _Worker, _NoExec, _Load) -> - up_to_date. - -%% worker进行编译 -do_worker(L, Opts, NoExec, Load, Worker) -> - WorkerList = do_split_list(L, Worker), - %io:format("worker:~p worker list(~p)~n", [Worker, length(WorkerList)]), - % 启动进程 - Ref = make_ref(), - Pids = - [begin - start_worker(E, Opts, NoExec, Load, self(), Ref) - end || E <- WorkerList], - do_wait_worker(length(Pids), Ref). - -%% 等待结果 -do_wait_worker(0, _Ref) -> - ok; -do_wait_worker(N, Ref) -> - receive - {ack, Ref} -> - do_wait_worker(N - 1, Ref); - {error, Ref} -> - throw(error); - {'EXIT', _P, _Reason} -> - do_wait_worker(N, Ref); - _Other -> - io:format("receive unknown msg:~p~n", [_Other]), - do_wait_worker(N, Ref) - end. - -%% 将L分割成最多包含N个子列表的列表 -do_split_list(L, N) -> - Len = length(L), - % 每个列表的元素数 - LLen = (Len + N - 1) div N, - do_split_list(L, LLen, []). - -do_split_list([], _N, Acc) -> - lists:reverse(Acc); -do_split_list(L, N, Acc) -> - {L2, L3} = lists:split(erlang:min(length(L), N), L), - do_split_list(L3, N, [L2 | Acc]). - -%% 启动worker进程 -start_worker(L, Opts, NoExec, Load, Parent, Ref) -> - Fun = - fun() -> - [begin - case recompilep(coerce_2_list(F), NoExec, Load, Opts) of - error -> - Parent ! {error, Ref}, - exit(error); - _ -> - ok - end - end || F <- L], - Parent ! {ack, Ref} - end, - spawn_link(Fun). - -recompilep(File, NoExec, Load, Opts) -> - ObjName = lists:append(filename:basename(File), - code:objfile_extension()), - ObjFile = case lists:keysearch(outdir, 1, Opts) of - {value, {outdir, OutDir}} -> - filename:join(coerce_2_list(OutDir), ObjName); - false -> - ObjName - end, - case exists(ObjFile) of - true -> - recompilep1(File, NoExec, Load, Opts, ObjFile); - false -> - recompile(File, NoExec, Load, Opts) - end. - -recompilep1(File, NoExec, Load, Opts, ObjFile) -> - {ok, Erl} = file:read_file_info(lists:append(File, ".erl")), - {ok, Obj} = file:read_file_info(ObjFile), - recompilep1(Erl, Obj, File, NoExec, Load, Opts). - -recompilep1(#file_info{mtime = Te}, - #file_info{mtime = To}, File, NoExec, Load, Opts) when Te > To -> - recompile(File, NoExec, Load, Opts); -recompilep1(_Erl, #file_info{mtime = To}, File, NoExec, Load, Opts) -> - recompile2(To, File, NoExec, Load, Opts). - -%% recompile2(ObjMTime, File, NoExec, Load, Opts) -%% Check if file is of a later date than include files. -recompile2(ObjMTime, File, NoExec, Load, Opts) -> - IncludePath = include_opt(Opts), - case check_includes(lists:append(File, ".erl"), IncludePath, ObjMTime) of - true -> - recompile(File, NoExec, Load, Opts); - false -> - false - end. - -include_opt([{i, Path} | Rest]) -> - [Path | include_opt(Rest)]; -include_opt([_First | Rest]) -> - include_opt(Rest); -include_opt([]) -> - []. - -%% recompile(File, NoExec, Load, Opts) -%% Actually recompile and load the file, depending on the flags. -%% Where load can be netload | load | noload - -recompile(File, true, _Load, _Opts) -> - io:format("Out of date: ~s\n", [File]); -recompile(File, false, noload, Opts) -> - io:format("Recompile: ~s\n", [File]), - compile:file(File, [report_errors, report_warnings, error_summary | Opts]); -recompile(File, false, load, Opts) -> - io:format("Recompile: ~s\n", [File]), - c:c(File, Opts); -recompile(File, false, netload, Opts) -> - io:format("Recompile: ~s\n", [File]), - c:nc(File, Opts). - -exists(File) -> - case file:read_file_info(File) of - {ok, _} -> - true; - _ -> - false - end. - -coerce_2_list(X) when is_atom(X) -> - atom_to_list(X); -coerce_2_list(X) -> - X. - -%%% If you an include file is found with a modification -%%% time larger than the modification time of the object -%%% file, return true. Otherwise return false. -check_includes(File, IncludePath, ObjMTime) -> - Path = [filename:dirname(File) | IncludePath], - case epp:open(File, Path, []) of - {ok, Epp} -> - check_includes2(Epp, File, ObjMTime); - _Error -> - false - end. - -check_includes2(Epp, File, ObjMTime) -> - case epp:parse_erl_form(Epp) of - {ok, {attribute, 1, file, {File, 1}}} -> - check_includes2(Epp, File, ObjMTime); - {ok, {attribute, 1, file, {IncFile, 1}}} -> - case file:read_file_info(IncFile) of - {ok, #file_info{mtime = MTime}} when MTime > ObjMTime -> - epp:close(Epp), - true; - _ -> - check_includes2(Epp, File, ObjMTime) - end; - {ok, _} -> - check_includes2(Epp, File, ObjMTime); - {eof, _} -> - epp:close(Epp), - false; - {error, _Error} -> - check_includes2(Epp, File, ObjMTime) - end. diff --git a/src/srvNodeMgr/server_control_main.erl b/src/srvNodeMgr/server_control_main.erl deleted file mode 100644 index aa85325..0000000 --- a/src/srvNodeMgr/server_control_main.erl +++ /dev/null @@ -1,145 +0,0 @@ --module(server_control_main). - --export([start/0]). - --define(RPC_TIMEOUT, infinity). - -commands_desc() -> - [{"stop", "停止游戏服务器进程"}, - {"stop_all", "停止游戏集群进程"}, - {"stop_app", "关闭server application"}, - {"start_app", "打开server application"}, - {"cluster_status", "集群状态"}]. -opt_spec_list() -> - Node = case get(nodename) of - undefined -> - throw(not_nodename); - V -> - V - end, - [ - {help, $h, "help", undefined, "显示帮助,然后退出"}, - {node, undefined, "node", {atom, Node}, "管理节点"} - ]. -usage() -> - getopt:usage(opt_spec_list(), "server_ctl", " []", commands_desc()), - err_misc:quit(1). -parse_arguments(CmdLine) -> - case getopt:parse(opt_spec_list(), CmdLine) of - {ok, {Opts, [Command | Args]}} -> - {ok, {list_to_atom(Command), Opts, Args}}; - {ok, {_Opts, []}} -> - no_command; - Error -> - io:format("Error ~p~n", [Error]), - no_command - end. - -start() -> - {ok, [[NodeStr | _] | _]} = init:get_argument(nodename), - put(nodename, list_to_atom(NodeStr)), - {Command, Opts, Args} = - case parse_arguments(init:get_plain_arguments()) of - {ok, Res} -> - Res; - no_command -> - usage() - end, - Node = proplists:get_value(node, Opts), - net_adm:ping(Node), - timer:sleep(1000), %% wait auto find node - %% The reason we don't use a try/catch here is that rpc:call turns - %% thrown errors into normal return values - % io:format("Opts ~p~n", [Opts]), - case catch action(Command, Node, Args, Opts) of - ok -> - io:format("done.~n", []), - quit(0); - {ok, Info} -> - io:format("done (~p).~n", [Info]), - quit(0); - Other -> - io:format("other result ~p~n", [Other]), - quit(2) - end. - -action(info, Node, _Args, _Opts) -> - io:format("System info for Node ~p~n", [Node]), - Res = call(Node, {main, get_info, []}), - io:format(" ~n Scheduler id: ~p - ~n Num scheduler: ~p - ~n Process count: ~p - ~n Process limit: ~p - ~n Memory used by erlang processes: ~p - ~n Memory allocated by erlang processes: ~p - ~n The total amount of memory allocated: ~p - ~n", - Res), - ok; -action(backup, Node, _Args, _Opts) -> - case call(Node, {app_misc, backup, []}) of - {error, Msg} -> - io:format("~s~n", [Msg]); - {ok, FileName} -> - io:format("backup file:~s~n", [FileName]), - io:format("backup file to remote ......~n", []), - Result = os:cmd("bash copy_to_remote.sh " ++ FileName), - io:format("~s~n", [Result]) - end, - ok; - -action(pause_accept, Node, _Args, _Opts) -> - io:format("Pause accept new client ~p~n", [Node]), - call(Node, {app_misc, pause_accept, []}), - ok; -action(resume_accept, Node, _Args, _Opts) -> - io:format("Resume accept new client ~p~n", [Node]), - call(Node, {app_misc, resume_accept, []}), - ok; -action(accept_state, Node, _Args, _Opts) -> - Res = call(Node, {app_misc, can_accept_new, []}), - io:format("Node ~p accept state:~p~n ", [Node, Res]), - ok; -action(reload, Node, _Args, _Opts) -> - io:format("Reloading node ~p~n", [Node]), - call(Node, {main, reload, []}); -action(stop_all, MasterNode, _Args, _Opts) -> - io:format("Stopping and halting all node~n", []), - PidMRefs = [{spawn_monitor(fun() -> - call(Node, {main, stop_and_halt, [5]}) - end), Node} - || Node <- nodes() -- [MasterNode]], - [receive - {'DOWN', MRef, process, _, normal} -> - ok; - {'DOWN', MRef, process, _, Reason} -> - io:format("Node ~p Error, Reason ~p", [Node, Reason]) - end || {{_Pid, MRef}, Node} <- PidMRefs], - call(MasterNode, {main, stop_and_halt, [5]}), - ok; -action(stop, Node, _Args, _Opts) -> - io:format("Stopping and halting node ~p~n", [Node]), - call(Node, {main, stop_and_halt, [5]}); -action(Command, _Node, Args, Opts) -> - io:format("Command: ~p Args: ~p Opts: ~p~n", [Command, Args, Opts]), - invalid_command. - -call(Node, {Mod, Fun, Args}) -> - %%rpc_call(Node, Mod, Fun, lists:map(fun list_to_binary/1, Args)). - rpc_call(Node, Mod, Fun, Args). - -rpc_call(Node, Mod, Fun, Args) -> - rpc:call(Node, Mod, Fun, Args, ?RPC_TIMEOUT). - - -quit(Status) -> - case os:type() of - {unix, _} -> - halt(Status); - {win32, _} -> - init:stop(Status), - receive - after infinity -> - ok - end - end. diff --git a/src/srvNodeMgr/utVMInfo.erl b/src/srvNodeMgr/utVMInfo.erl index 36f7693..89bd230 100644 --- a/src/srvNodeMgr/utVMInfo.erl +++ b/src/srvNodeMgr/utVMInfo.erl @@ -1,84 +1,100 @@ -module(utVMInfo). --compile([export_all, nowarn_export_all]). +-export([ + vmInfo/0 %% 打印虚拟机简略信息 + , processInfo/1 %% 打印进程简略信息 + , processMqlInfo/0 %% 消息队列阻塞的进程信息 + , processMqlInfo/1 %% 消息队列阻塞的进程信息 + , showEtsCache/0 %% 打印并排序各个表的缓存消耗 + , processHeapSize/0 %% 进程消耗内存的信息 + , processCnt/0 %% 当前进程数量 + , pstack/1 %% 进程栈 + , gcAll/0 %% 对所有process做gc 进程内存过高时,来一发,看看是内存泄露还是gc不过来 + , gcOne/1 %% 对指定process做gc 进程内存过高时,来一发,看看是内存泄露还是gc不过来 + , scheduler_usage/0 %% 统计下1s每个调度器CPU的实际利用率(因为有spin wait、调度工作, 可能usage 比top显示低很多) + , scheduler_usage/1 %% 统计下1s每个调度器CPU的实际利用率(因为有spin wait、调度工作, 可能usage 比top显示低很多)\ + , scheduler_stat/0 %% 统计下1s内调度进程数量(含义:第一个数字执行进程数量,第二个数字迁移进程数量) + , scheduler_stat/1 %% 统计下1s内调度进程数量(含义:第一个数字执行进程数量,第二个数字迁移进程数量) +]). + +%% 打印虚拟机简略信息 +vmInfo() -> + io:format("abormal termination: + ~n Scheduler id: ~p + ~n Num scheduler: ~p + ~n Process count: ~p + ~n Process limit: ~p + ~n Memory used by erlang processes: ~p + ~n Memory allocated by erlang processes: ~p + ~n The total amount of memory allocated: ~p + ~n", + [erlang:system_info(scheduler_id), erlang:system_info(schedulers), erlang:system_info(process_count), erlang:system_info(process_limit), erlang:memory(processes_used), erlang:memory(processes), erlang:memory(total)]), + ok. + +%% 打印进程简略信息 +processInfo(Pid) -> + io:format("~n~n=====process info===~n~p~n~p~n~p~n~p~n~p~n~p~n~p~n~p~n~p~n~p~n~n~n", + [ + Pid, + erlang:process_info(Pid, registered_name), + erlang:process_info(Pid, current_function), + erlang:process_info(Pid, message_queue_len), + erlang:process_info(Pid, status), + erlang:process_info(Pid, suspending), + erlang:process_info(Pid, last_calls), + erlang:process_info(Pid, links), + erlang:process_info(Pid, dictionary), + erlang:process_info(Pid, current_stacktrace) + ]). + +%% 消息队列阻塞的进程信息 +processMqlInfo() -> + processMqlInfo(100). + +processMqlInfo(Num) -> + [processMqlInfo(Pid, Num) || Pid <- erlang:processes()], + ok. + +%% 消息队列阻塞的进程信息 +processMqlInfo(Pid, Num) -> + case erlang:process_info(Pid, message_queue_len) of + {message_queue_len, Count} when Count > Num -> + processInfo(Pid); + _ -> + ok + end. %% 打印并排序各个表的缓存消耗 -show_cache() -> +showEtsCache() -> io:format("table name | memory | size~n", []), lists:reverse(lists:keysort(2, [{T, ets:info(T, memory), ets:info(T, size)} || T <- ets:all()])). -%% 打印进程消耗内存的信息 -show_process() -> +%% 进程消耗内存排序的信息 +processHeapSize() -> lists:reverse(lists:keysort(2, [{erlang:process_info(P, registered_name), erlang:process_info(P, heap_size)} || P <- erlang:processes()])). -%% 打印当前进程数量 -show_process_count() -> - length(erlang:processes()). - -%% 反编译 -%% 确认线上运行代码是否正确,reltools没掌握好,升级偶尔出现问题 -decompile(Mod) -> - {ok,{_,[{abstract_code,{_,AC}}]}} = beam_lib:chunks(code:which(Mod), [abstract_code]), - io:format("~s~n", [erl_prettypr:format(erl_syntax:form_list(AC))]). +%% 当前进程数量 +processCnt() -> + erlang:system_info(process_count). -%% 进程栈 -%% 类似于jstack,发现大量进程挂起,进程数过高,运行慢,hang住等问题用到 +%% 进程栈 类似于jstack,发现大量进程挂起,进程数过高,运行慢,hang住等问题用到 pstack(Reg) when is_atom(Reg) -> - case whereis(Reg) of - undefined -> undefined; - Pid -> pstack(Pid) - end; -pstack(Pid) -> - io:format("~s~n", [element(2, process_info(Pid, backtrace))]). - -%% ==================================================================== -%% etop -%% 分析内存、cpu占用进程,即使数十w进程node 也能正常使用 -%% 进程CPU占用排名 -%% -------------------------------------------------------------------- -etop() -> - spawn(fun() -> etop:start([{output, text}, {interval, 10}, {lines, 20}, {sort, reductions}]) end). - -%% 进程Mem占用排名 -etop_mem() -> - spawn(fun() -> etop:start([{output, text}, {interval, 10}, {lines, 20}, {sort, memory}]) end). - -%% 停止etop -etop_stop() -> - etop:stop(). -%% ==================================================================== - -%% 对所有process做gc -%% 进程内存过高时,来一发,看看是内存泄露还是gc不过来 -gc_all() -> + case is_atom(Reg) andalso whereis(Reg) of + false -> io:format("~s~n", [element(2, erlang:process_info(Reg, backtrace))]); + undefined -> + undefined; + Pid -> io:format("~s~n", [element(2, erlang:process_info(Pid, backtrace))]) + end. + +%% 对所有process做gc 进程内存过高时,来一发,看看是内存泄露还是gc不过来 +gcAll() -> [erlang:garbage_collect(Pid) || Pid <- processes()], ok. -%% 对MFA 执行分析,会严重减缓运行,建议只对小量业务执行 -%% 结果: -%% fprof 结果比较详细,能够输出热点调用路径 -fprof(M, F, A) -> - fprof:start(), - fprof:apply(M, F, A), - fprof:profile(), - fprof:analyse(), - fprof:stop(). - -%% 对整个节点内所有进程执行eprof, eprof 对线上业务有一定影响,慎用! -%% 建议TimeoutSec<10s,且进程数< 1000,否则可能导致节点crash -%% 结果: -%% 输出每个方法实际执行时间(不会累计方法内其他mod调用执行时间) -%% 只能得到mod - Fun 执行次数 执行耗时 -eprof_all(TimeoutSec) -> - eprof(processes() -- [whereis(eprof)], TimeoutSec). - -eprof(Pids, TimeoutSec) -> - eprof:start(), - eprof:start_profiling(Pids), - timer:sleep(TimeoutSec), - eprof:stop_profiling(), - eprof:analyze(total), - eprof:stop(). +%% 对指定process做gc 进程内存过高时,来一发,看看是内存泄露还是gc不过来 +gcOne(Pid) -> + erlang:garbage_collect(Pid), + ok. %% scheduler usage %% 统计下1s每个调度器CPU的实际利用率(因为有spin wait、调度工作, 可能usage 比top显示低很多) @@ -112,27 +128,7 @@ scheduler_stat(RunMs) -> lists:map(fun({{_Key, In0, Out0}, {Key, In1, Out1}}) -> {Key, In1 - In0, Out1 - Out0} end, lists:zip(Ts0, Ts1)). -%% ==================================================================== -%% trace 日志 -%% 会把mod 每次调用详细MFA log 下来,args 太大就不好看了 -%% trace Mod 所有方法的调用 -%% -------------------------------------------------------------------- -trace(Mod) -> - dbg:tracer(), - dbg:tpl(Mod, '_', []), - dbg:p(all, c). - -%% trace Node上指定 Mod 所有方法的调用, 结果将输出到本地shell -trace(Node, Mod) -> - dbg:tracer(), - dbg:n(Node), - dbg:tpl(Mod, '_', []), - dbg:p(all, c). - -%% 停止trace -trace_stop() -> - dbg:stop_clear(). -%% ==================================================================== + %% 内存高OOM 排查工具 %% etop 无法应对10w+ 进程节点, 下面代码就没问题了;找到可疑proc后通过pstack、message_queu_len 排查原因 @@ -367,19 +363,7 @@ ets_mem() -> io:format("~-30w ~wKb~n", [Tab, Mem / 1024]) end, L1). -%% @spec tcp_links() -> Info -%% @doc 统计tcp链接 -tcp_links() -> - L = erlang:ports(), - F = fun(P) -> - Pinfo = erlang:port_info(P), - case lists:keyfind(name, 1, Pinfo) of - {name, "tcp_inet"} -> true; - _ -> false - end - end, - L1 = lists:filter(F, L), - io:format("~n当前socket数量(包括链接数据库的socket): ~w~n", [length(L1)]). + %% @doc 备份进程信息