diff options
Diffstat (limited to 'lib/runtime_tools/src')
-rw-r--r-- | lib/runtime_tools/src/Makefile | 1 | ||||
-rw-r--r-- | lib/runtime_tools/src/dbg.erl | 8 | ||||
-rw-r--r-- | lib/runtime_tools/src/erts_alloc_config.erl | 169 | ||||
-rw-r--r-- | lib/runtime_tools/src/observer_backend.erl | 47 | ||||
-rw-r--r-- | lib/runtime_tools/src/runtime_tools.app.src | 2 | ||||
-rw-r--r-- | lib/runtime_tools/src/runtime_tools.appup.src | 10 | ||||
-rw-r--r-- | lib/runtime_tools/src/runtime_tools_sup.erl | 1 | ||||
-rw-r--r-- | lib/runtime_tools/src/system_information.erl | 554 | ||||
-rw-r--r-- | lib/runtime_tools/src/ttb_autostart.erl | 1 |
9 files changed, 708 insertions, 85 deletions
diff --git a/lib/runtime_tools/src/Makefile b/lib/runtime_tools/src/Makefile index 2347986c53..8d2bcfe3d1 100644 --- a/lib/runtime_tools/src/Makefile +++ b/lib/runtime_tools/src/Makefile @@ -42,6 +42,7 @@ MODULES= \ dbg \ dyntrace \ percept_profile \ + system_information \ observer_backend \ ttb_autostart HRL_FILES= ../include/observer_backend.hrl diff --git a/lib/runtime_tools/src/dbg.erl b/lib/runtime_tools/src/dbg.erl index 6b2fb0460f..186563ab74 100644 --- a/lib/runtime_tools/src/dbg.erl +++ b/lib/runtime_tools/src/dbg.erl @@ -1113,7 +1113,7 @@ transform_flags([sos|Tail],Acc) -> transform_flags(Tail,[set_on_spawn|Acc]); transform_flags([sol|Tail],Acc) -> transform_flags(Tail,[set_on_link|Acc]); transform_flags([sofs|Tail],Acc) -> transform_flags(Tail,[set_on_first_spawn|Acc]); transform_flags([sofl|Tail],Acc) -> transform_flags(Tail,[set_on_first_link|Acc]); -transform_flags([all|_],_Acc) -> all(); +transform_flags([all|_],_Acc) -> all()--[silent]; transform_flags([F|Tail]=List,Acc) when is_atom(F) -> case lists:member(F, all()) of true -> transform_flags(Tail,[F|Acc]); @@ -1124,7 +1124,7 @@ transform_flags(Bad,_Acc) -> {error,{bad_flags,Bad}}. all() -> [send,'receive',call,procs,garbage_collection,running, set_on_spawn,set_on_first_spawn,set_on_link,set_on_first_link, - timestamp,arity,return_to]. + timestamp,arity,return_to,silent]. display_info([Node|Nodes]) -> io:format("~nNode ~w:~n",[Node]), @@ -1786,12 +1786,12 @@ h(get_tracer) -> " - Returns the process or port to which all trace messages are sent."]); h(stop) -> help_display( - ["stop() -> stopped", + ["stop() -> ok", " - Stops the dbg server and the tracing of all processes.", " Does not clear any trace patterns."]); h(stop_clear) -> help_display( - ["stop_clear() -> stopped", + ["stop_clear() -> ok", " - Stops the dbg server and the tracing of all processes,", " and clears all trace patterns."]). diff --git a/lib/runtime_tools/src/erts_alloc_config.erl b/lib/runtime_tools/src/erts_alloc_config.erl index 284e88d4a7..b9a26dc0dc 100644 --- a/lib/runtime_tools/src/erts_alloc_config.erl +++ b/lib/runtime_tools/src/erts_alloc_config.erl @@ -39,6 +39,8 @@ need_config_change, alloc_util, instances, + strategy, + acul, low_mbc_blocks_size, high_mbc_blocks_size, sbct, @@ -54,8 +56,6 @@ -define(SERVER, '__erts_alloc_config__'). --define(MAX_ALLOCATOR_INSTANCES, 16). - -define(KB, 1024). -define(MB, 1048576). @@ -99,23 +99,11 @@ {ets_alloc, 131072}, {fix_alloc, 131072}, {eheap_alloc, 524288}, - {ll_alloc, 2097152}, + {ll_alloc, 131072}, {sl_alloc, 131072}, {temp_alloc, 131072}, {driver_alloc, 131072}]). --define(MMMBC_DEFAULTS, - [{binary_alloc, 10}, - {std_alloc, 10}, - {ets_alloc, 10}, - {fix_alloc, 10}, - {eheap_alloc, 10}, - {ll_alloc, 0}, - {sl_alloc, 10}, - {temp_alloc, 10}, - {driver_alloc, 10}]). - - %%% %%% Exported interface %%% @@ -230,20 +218,72 @@ server_loop(State) -> end, server_loop(NewState). -allocator_instances(temp_alloc) -> - erlang:system_info(schedulers) + 1; -allocator_instances(ll_alloc) -> +carrier_migration_support(aoff) -> + true; +carrier_migration_support(aoffcbf) -> + true; +carrier_migration_support(aoffcaobf) -> + true; +carrier_migration_support(_) -> + false. + +allocator_instances(ll_alloc, Strategy) -> + case carrier_migration_support(Strategy) of + true -> erlang:system_info(schedulers); + false -> 1 + end; +allocator_instances(_A, undefined) -> 1; -allocator_instances(_Allocator) -> - case erlang:system_info(schedulers) of - Schdlrs when Schdlrs =< ?MAX_ALLOCATOR_INSTANCES -> Schdlrs; - _Schdlrs -> ?MAX_ALLOCATOR_INSTANCES +allocator_instances(_A, _Strategy) -> + erlang:system_info(schedulers). + +strategy(temp_alloc, _AI) -> + af; +strategy(A, AI) -> + try + {A, OptList} = lists:keyfind(A, 1, AI), + {as, S} = lists:keyfind(as, 1, OptList), + S + catch + _ : _ -> + undefined + end. + +strategy_str(af) -> + "A fit"; +strategy_str(gf) -> + "Good fit"; +strategy_str(bf) -> + "Best fit"; +strategy_str(aobf) -> + "Address order best fit"; +strategy_str(aoff) -> + "Address order first fit"; +strategy_str(aoffcbf) -> + "Address order first fit carrier best fit"; +strategy_str(aoffcaobf) -> + "Address order first fit carrier adress order best fit". + +default_acul(A, S) -> + case carrier_migration_support(S) of + false -> + 0; + true -> + case A of + ll_alloc -> 85; + eheap_alloc -> 45; + _ -> 60 + end end. - + make_state() -> + {_, _, _, AI} = erlang:system_info(allocator), #state{alloc = lists:map(fun (A) -> + S = strategy(A, AI), #alloc{name = A, - instances = allocator_instances(A)} + strategy = S, + acul = default_acul(A, S), + instances = allocator_instances(A, S)} end, ?ALLOCATORS)}. @@ -345,7 +385,7 @@ do_save_scenario(AlcList) -> conf_size(Bytes) when is_integer(Bytes), Bytes < 0 -> exit({bad_value, Bytes}); conf_size(Bytes) when is_integer(Bytes), Bytes < 1*?MB -> - ?ROUNDUP(?B2KB(Bytes), 128); + ?ROUNDUP(?B2KB(Bytes), 256); conf_size(Bytes) when is_integer(Bytes), Bytes < 10*?MB -> ?ROUNDUP(?B2KB(Bytes), ?B2KB(1*?MB)); conf_size(Bytes) when is_integer(Bytes), Bytes < 100*?MB -> @@ -376,28 +416,25 @@ mmbcs(#conf{format_to = FTO}, temp_alloc -> BlocksSize; _ -> BlocksSize div Insts end, - case BS > default_mmbcs(A, Insts) of - true -> + DefMMBCS = default_mmbcs(A, Insts), + case {Insts, BS > DefMMBCS} of + {1, true} -> MMBCS = conf_size(BS), fc(FTO, "Main mbc size of ~p kilobytes.", [MMBCS]), format(FTO, " +M~cmmbcs ~p~n", [alloc_char(A), MMBCS]); - false -> + _ -> + MMBCS = ?B2KB(DefMMBCS), + fc(FTO, "Main mbc size of ~p kilobytes.", [MMBCS]), + format(FTO, " +M~cmmbcs ~p~n", [alloc_char(A), MMBCS]), ok end. -smbcs_lmbcs_mmmbc(#conf{format_to = FTO}, - #alloc{name = A, instances = Insts, segments = Segments}) -> - MMMBC = case {A, Insts} of - {_, 1} -> Segments#segment.number; - {temp_alloc, _} -> Segments#segment.number; - _ -> (Segments#segment.number div Insts) + 1 - end, +smbcs_lmbcs(#conf{format_to = FTO}, + #alloc{name = A, segments = Segments}) -> MBCS = Segments#segment.size, AC = alloc_char(A), fc(FTO, "Mseg mbc size of ~p kilobytes.", [MBCS]), format(FTO, " +M~csmbcs ~p +M~clmbcs ~p~n", [AC, MBCS, AC, MBCS]), - fc(FTO, "Max ~p mseg mbcs.", [MMMBC]), - format(FTO, " +M~cmmmbc ~p~n", [AC, MMMBC]), ok. alloc_char(binary_alloc) -> $B; @@ -462,6 +499,8 @@ au_conf_alloc(#conf{format_to = FTO} = Conf, #alloc{name = A, alloc_util = true, instances = Insts, + acul = Acul, + strategy = Strategy, low_mbc_blocks_size = Low, high_mbc_blocks_size = High} = Alc) -> fcp(FTO, "Usage of mbcs: ~p - ~p kilobytes", [?B2KB(Low), ?B2KB(High)]), @@ -470,31 +509,49 @@ au_conf_alloc(#conf{format_to = FTO} = Conf, fc(FTO, "One instance used."), format(FTO, " +M~ct false~n", [alloc_char(A)]); _ -> - fc(FTO, "~p instances used.", + fc(FTO, "~p + 1 instances used.", [Insts]), - format(FTO, " +M~ct true~n", [alloc_char(A)]) - end, + format(FTO, " +M~ct true~n", [alloc_char(A)]), + case Strategy of + undefined -> + ok; + _ -> + fc(FTO, "Allocation strategy: ~s.", + [strategy_str(Strategy)]), + format(FTO, " +M~cas ~s~n", [alloc_char(A), + atom_to_list(Strategy)]) + end, + case carrier_migration_support(Strategy) of + false -> + ok; + true -> + fc(FTO, "Abandon carrier utilization limit of ~p%.", [Acul]), + format(FTO, " +M~cacul ~p~n", [alloc_char(A), Acul]) + end + end, mmbcs(Conf, Alc), - smbcs_lmbcs_mmmbc(Conf, Alc), + smbcs_lmbcs(Conf, Alc), sbct(Conf, Alc). -large_growth(Low, High) -> - High - Low >= ?LARGE_GROWTH_ABS_LIMIT. - calc_seg_size(Growth, Segs) -> conf_size(round(Growth*?FRAG_FACT*?GROWTH_SEG_FACT) div Segs). calc_growth_segments(Conf, AlcList0) -> - CalcSmall = fun (#alloc{name = ll_alloc} = Alc, Acc) -> - {Alc#alloc{segments = #segment{size = 0, + CalcSmall = fun (#alloc{name = ll_alloc, instances = 1} = Alc, Acc) -> + {Alc#alloc{segments = #segment{size = conf_size(0), number = 0}}, Acc}; (#alloc{alloc_util = true, - low_mbc_blocks_size = Low, + instances = Insts, + low_mbc_blocks_size = LowMBC, high_mbc_blocks_size = High} = Alc, {SL, AL}) -> + Low = case Insts of + 1 -> LowMBC; + _ -> 0 + end, Growth = High - Low, - case large_growth(Low, High) of + case Growth >= ?LARGE_GROWTH_ABS_LIMIT of true -> {Alc, {SL, AL+1}}; false -> @@ -522,8 +579,13 @@ calc_growth_segments(Conf, AlcList0) -> end, CalcLarge = fun (#alloc{alloc_util = true, segments = undefined, - low_mbc_blocks_size = Low, + instances = Insts, + low_mbc_blocks_size = LowMBC, high_mbc_blocks_size = High} = Alc) -> + Low = case Insts of + 1 -> LowMBC; + _ -> 0 + end, Growth = High - Low, SegSize = calc_seg_size(Growth, SegsPerAlloc), @@ -560,15 +622,10 @@ format_header(FTO) -> case erlang:system_info(schedulers) of 1 -> ok; Schdlrs -> - MinSchdlrs = case Schdlrs > ?MAX_ALLOCATOR_INSTANCES of - true -> ?MAX_ALLOCATOR_INSTANCES; - false -> Schdlrs - end, fcp(FTO, "NOTE: This configuration was made for ~p schedulers. " - "It is very important that at least ~p schedulers " - "are used.", - [Schdlrs, MinSchdlrs]) + "It is very important that ~p schedulers are used.", + [Schdlrs, Schdlrs]) end, fcp(FTO, "This configuration is intended as a suggestion and " diff --git a/lib/runtime_tools/src/observer_backend.erl b/lib/runtime_tools/src/observer_backend.erl index 670e216d97..fea0854042 100644 --- a/lib/runtime_tools/src/observer_backend.erl +++ b/lib/runtime_tools/src/observer_backend.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2002-2013. All Rights Reserved. +%% Copyright Ericsson AB 2002-2014. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -53,6 +53,13 @@ sys_info() -> Mem -> Mem catch _:_ -> [] end, + + SchedulersOnline = erlang:system_info(schedulers_online), + SchedulersAvailable = case erlang:system_info(multi_scheduling) of + enabled -> SchedulersOnline; + _ -> 1 + end, + {{_,Input},{_,Output}} = erlang:statistics(io), [{process_count, erlang:system_info(process_count)}, {process_limit, erlang:system_info(process_limit)}, @@ -60,9 +67,13 @@ sys_info() -> {run_queue, erlang:statistics(run_queue)}, {io_input, Input}, {io_output, Output}, + {logical_processors, erlang:system_info(logical_processors)}, - {logical_processors_available, erlang:system_info(logical_processors_available)}, {logical_processors_online, erlang:system_info(logical_processors_online)}, + {logical_processors_available, erlang:system_info(logical_processors_available)}, + {schedulers, erlang:system_info(schedulers)}, + {schedulers_online, SchedulersOnline}, + {schedulers_available, SchedulersAvailable}, {otp_release, erlang:system_info(otp_release)}, {version, erlang:system_info(version)}, @@ -77,8 +88,8 @@ sys_info() -> | MemInfo]. alloc_info() -> - {_,_,AllocTypes,_} = erlang:system_info(allocator), - try erlang:system_info({allocator_sizes,AllocTypes}) of + AlcuAllocs = erlang:system_info(alloc_util_allocators), + try erlang:system_info({allocator_sizes, AlcuAllocs}) of Allocators -> Allocators catch _:_ -> [] end. @@ -216,12 +227,14 @@ fetch_stats(Parent, Time) -> fetch_stats_loop(Parent, Time) -> erlang:system_flag(scheduler_wall_time, true), receive - _Msg -> erlang:system_flag(scheduler_wall_time, false) + _Msg -> + %% erlang:system_flag(scheduler_wall_time, false) + ok after Time -> _M = Parent ! {stats, 1, erlang:statistics(scheduler_wall_time), erlang:statistics(io), - erlang:memory()}, + try erlang:memory() catch _:_ -> [] end}, fetch_stats_loop(Parent, Time) end. %% @@ -233,17 +246,6 @@ etop_collect(Collector) -> %% utilization in etop). Next time the flag will be true and then %% there will be a measurement. SchedulerWallTime = erlang:statistics(scheduler_wall_time), - - %% Turn off the flag while collecting data per process etc. - case erlang:system_flag(scheduler_wall_time,false) of - false -> - %% First time and the flag was false - start a monitoring - %% process to set the flag back to false when etop is stopped. - spawn(fun() -> flag_holder_proc(Collector) end); - _ -> - ok - end, - ProcInfo = etop_collect(processes(), []), Collector ! {self(),#etop_info{now = now(), @@ -253,13 +255,22 @@ etop_collect(Collector) -> memi = etop_memi(), procinfo = ProcInfo }}, + + case SchedulerWallTime of + undefined -> + spawn(fun() -> flag_holder_proc(Collector) end); + _ -> + ok + end, + erlang:system_flag(scheduler_wall_time,true). flag_holder_proc(Collector) -> Ref = erlang:monitor(process,Collector), receive {'DOWN',Ref,_,_,_} -> - erlang:system_flag(scheduler_wall_time,false) + %% erlang:system_flag(scheduler_wall_time,false) + ok end. etop_memi() -> diff --git a/lib/runtime_tools/src/runtime_tools.app.src b/lib/runtime_tools/src/runtime_tools.app.src index 602048dc21..d46cfe1f32 100644 --- a/lib/runtime_tools/src/runtime_tools.app.src +++ b/lib/runtime_tools/src/runtime_tools.app.src @@ -21,7 +21,7 @@ {vsn, "%VSN%"}, {modules, [appmon_info, dbg,observer_backend,percept_profile, runtime_tools,runtime_tools_sup,erts_alloc_config, - ttb_autostart,dyntrace]}, + ttb_autostart,dyntrace,system_information]}, {registered, [runtime_tools_sup]}, {applications, [kernel, stdlib]}, {env, []}, diff --git a/lib/runtime_tools/src/runtime_tools.appup.src b/lib/runtime_tools/src/runtime_tools.appup.src index 7a435e9b22..0c2bab316f 100644 --- a/lib/runtime_tools/src/runtime_tools.appup.src +++ b/lib/runtime_tools/src/runtime_tools.appup.src @@ -1,7 +1,7 @@ -%% +%% -*- erlang -*- %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2001-2009. All Rights Reserved. +%% Copyright Ericsson AB 2001-2014. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -15,5 +15,7 @@ %% under the License. %% %% %CopyrightEnd% -%% -{"%VSN%",[],[]}. +{"%VSN%", + [{<<".*">>,[{restart_application, runtime_tools}]}], + [{<<".*">>,[{restart_application, runtime_tools}]}] +}. diff --git a/lib/runtime_tools/src/runtime_tools_sup.erl b/lib/runtime_tools/src/runtime_tools_sup.erl index ab9fa534d5..32770397dd 100644 --- a/lib/runtime_tools/src/runtime_tools_sup.erl +++ b/lib/runtime_tools/src/runtime_tools_sup.erl @@ -1,4 +1,3 @@ -%% -*- coding: utf-8 -*- %% %% %CopyrightBegin% %% diff --git a/lib/runtime_tools/src/system_information.erl b/lib/runtime_tools/src/system_information.erl new file mode 100644 index 0000000000..603b698d5e --- /dev/null +++ b/lib/runtime_tools/src/system_information.erl @@ -0,0 +1,554 @@ +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2013. All Rights Reserved. +%% +%% The contents of this file are subject to the Erlang Public License, +%% Version 1.1, (the "License"); you may not use this file except in +%% compliance with the License. You should have received a copy of the +%% Erlang Public License along with this software. If not, it can be +%% retrieved online at http://www.erlang.org/. +%% +%% Software distributed under the License is distributed on an "AS IS" +%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +%% the License for the specific language governing rights and limitations +%% under the License. +%% +%% %CopyrightEnd% +%% + + +%% The main purpose of system_information is to aggregate all information +%% deemed useful for investigation, i.e. system_information:report/0. + +%% The server and all other utilities surrounding this is for inspecting +%% reported values. Functions will be added to this as time goes by. + +-module(system_information). +-behaviour(gen_server). + +%% API +-export([ + report/0, + from_file/1, + to_file/1 + ]). +-export([ + start/0, stop/0, + load_report/0, load_report/2, + applications/0, applications/1, + application/1, application/2, + environment/0, environment/1, + module/1, module/2, + modules/1 + ]). + +%% gen_server callbacks +-export([ + init/1, + handle_call/3, + handle_cast/2, + handle_info/2, + terminate/2, + code_change/3 + ]). + +-define(SERVER, ?MODULE). + +%% change version if parsing of file changes +-define(REPORT_FILE_VSN, "1.0"). + +-record(state, { + report + }). + +%%=================================================================== +%% API +%%=================================================================== + +start() -> + gen_server:start({local, ?SERVER}, ?MODULE, [], []). + +stop() -> + gen_server:call(?SERVER, stop). + +load_report() -> load_report(data, report()). + +load_report(file, File) -> load_report(data, from_file(File)); +load_report(data, Report) -> + start(), gen_server:call(?SERVER, {load_report, Report}). + +report() -> [ + {init_arguments, init:get_arguments()}, + {code_paths, code:get_path()}, + {code, code()}, + {system_info, erlang_system_info()}, + {erts_compile_info, erlang:system_info(compile_info)}, + {beam_dynamic_libraries, get_dynamic_libraries()}, + {environment_erts, os_getenv_erts_specific()}, + {environment, [split_env(Env) || Env <- os:getenv()]} + ]. + +to_file(File) -> + file:write_file(File, iolist_to_binary([ + io_lib:format("{system_information_version, ~p}.~n", [ + ?REPORT_FILE_VSN + ]), + io_lib:format("{system_information, ~p}.~n", [ + report() + ]) + ])). + +from_file(File) -> + case file:consult(File) of + {ok, Data} -> + case get_value([system_information_version], Data) of + ?REPORT_FILE_VSN -> + get_value([system_information], Data); + Vsn -> + erlang:error({unknown_version, Vsn}) + end; + _ -> + erlang:error(bad_report_file) + end. + +applications() -> applications([]). +applications(Opts) when is_list(Opts) -> + gen_server:call(?SERVER, {applications, Opts}). + +application(App) when is_atom(App) -> application(App, []). +application(App, Opts) when is_atom(App), is_list(Opts) -> + gen_server:call(?SERVER, {application, App, Opts}). + +environment() -> environment([]). +environment(Opts) when is_list(Opts) -> + gen_server:call(?SERVER, {environment, Opts}). + +module(M) when is_atom(M) -> module(M, []). +module(M, Opts) when is_atom(M), is_list(Opts) -> + gen_server:call(?SERVER, {module, M, Opts}). + +modules(Opt) when is_atom(Opt) -> + gen_server:call(?SERVER, {modules, Opt}). + +%%=================================================================== +%% gen_server callbacks +%%=================================================================== + +init([]) -> + {ok, #state{}}. + +handle_call(stop, _From, S) -> + {stop, normal, ok, S}; + +handle_call({load_report, Report}, _From, S) -> + Version = get_value([system_info, system_version], Report), + io:format("Loaded report from system version: ~s~n", [Version]), + {reply, ok, S#state{ report = Report }}; + +handle_call(_Req, _From, #state{ report = undefined } = S) -> + {reply, {error, report_not_loaded}, S}; + +handle_call({applications, Opts}, _From, #state{ report = Report } = S) -> + ok = print_applications(get_value([code], Report), Opts), + {reply, ok, S}; + +handle_call({application, App, Opts}, _From, #state{ report = Report } = S) -> + Data = get_value([App], [AppInfo||{application, AppInfo}<-get_value([code], Report)]), + ok = print_application({App, Data}, Opts), + {reply, ok, S}; + + +handle_call({environment, Opts}, _From, #state{ report = Report } = S) -> + Choices = case proplists:get_bool(full, Opts) of + true -> [environment]; + false -> [environment_erts] + end, + ok = print_environments(get_value(Choices, Report), Opts), + {reply, ok, S}; + + +handle_call({module, M, Opts}, _From, #state{ report = Report } = S) -> + Mods = find_modules_from_code(M, get_value([code], Report)), + print_modules_from_code(M, Mods, Opts), + {reply, ok, S}; + +handle_call({modules, native}, _From, #state{ report = Report } = S) -> + Codes = get_native_modules_from_code(get_value([code],Report)), + io:format("~p~n", [Codes]), + {reply, ok, S}; + + +handle_call(_Request, _From, State) -> + {reply, ok, State}. + +handle_cast(_Msg, State) -> + {noreply, State}. + +handle_info(_Info, State) -> + {noreply, State}. + +terminate(_Reason, _State) -> + ok. + +code_change(_OldVsn, State, _Extra) -> + {ok, State}. + +%%=================================================================== +%% Internal functions +%%=================================================================== + +%% handle report values + +get_value([], Data) -> Data; +get_value([K|Ks], Data) -> + get_value(Ks, proplists:get_value(K, Data, [])). + +find_modules_from_code(M, [{code, Info}|Codes]) -> + case find_modules(M, get_value([modules], Info)) of + [] -> find_modules_from_code(M, Codes); + Mods -> + Path = get_value([path], Info), + [{Path, Mods}|find_modules_from_code(M, Codes)] + end; +find_modules_from_code(M, [{application, {App, Info}}|Codes]) -> + case find_modules(M, get_value([modules], Info)) of + [] -> find_modules_from_code(M, Codes); + Mods -> + Path = get_value([path], Info), + Vsn = get_value([vsn], Info), + [{App, Vsn, Path, Mods}|find_modules_from_code(M, Codes)] + end; +find_modules_from_code(_, []) -> []. + +find_modules(M, [{M, _}=Info|Ms]) -> [Info|find_modules(M,Ms)]; +find_modules(M, [_|Ms]) -> find_modules(M, Ms); +find_modules(_, []) -> []. + +get_native_modules_from_code([{application, {App, Info}}|Cs]) -> + case get_native_modules(get_value([modules], Info)) of + [] -> get_native_modules_from_code(Cs); + Mods -> + Path = get_value([path], Info), + Vsn = get_value([vsn], Info), + [{App, Vsn, Path, Mods}|get_native_modules_from_code(Cs)] + end; +get_native_modules_from_code([{code, Info}|Cs]) -> + case get_native_modules(get_value([modules], Info)) of + [] -> get_native_modules_from_code(Cs); + Mods -> + Path = get_value([path], Info), + [{Path, Mods}|get_native_modules_from_code(Cs)] + end; +get_native_modules_from_code([]) -> []. + +get_native_modules([]) -> []; +get_native_modules([{Mod, Info}|Ms]) -> + case proplists:get_value(native, Info) of + false -> get_native_modules(Ms); + _ -> [Mod|get_native_modules(Ms)] + end. + + +%% print information + +print_applications([{application, App}|Apps], Opts) -> + print_application(App, Opts), + print_applications(Apps, Opts); +print_applications([{code,_}|Apps], Opts) -> + print_applications(Apps, Opts); +print_applications([], _) -> + ok. + +print_application({App, Info}, Opts) -> + Vsn = get_value([vsn], Info), + io:format(" * ~w-~s~n", [App, Vsn]), + case proplists:get_bool(full, Opts) of + true -> + _ = [ begin + print_module(Minfo) + end || Minfo <- get_value([modules], Info) ], + ok; + false -> + ok + end. + +print_environments([Env|Envs],Opts) -> + print_environment(Env,Opts), + print_environments(Envs,Opts); +print_environments([],_) -> + ok. + +print_environment({_Key, false},_) -> ok; +print_environment({Key, Value},_) -> + io:format(" - ~s = ~ts~n", [Key, Value]). + +print_modules_from_code(M, [Info|Ms], Opts) -> + print_module_from_code(M, Info), + case proplists:get_bool(full, Opts) of + true -> print_modules_from_code(M, Ms, Opts); + false -> ok + end; +print_modules_from_code(_, [], _) -> + ok. + +print_module_from_code(M, {Path, [{M,ModInfo}]}) -> + io:format(" from path \"~ts\" (no application):~n", [Path]), + io:format(" - compiler: ~s~n", [get_value([compiler], ModInfo)]), + io:format(" - md5: ~s~n", [get_value([md5], ModInfo)]), + io:format(" - native: ~w~n", [get_value([native], ModInfo)]), + io:format(" - loaded: ~w~n", [get_value([loaded], ModInfo)]), + ok; +print_module_from_code(M, {App,Vsn,Path,[{M,ModInfo}]}) -> + io:format(" from path \"~ts\" (~w-~s):~n", [Path,App,Vsn]), + io:format(" - compiler: ~s~n", [get_value([compiler], ModInfo)]), + io:format(" - md5: ~s~n", [get_value([md5], ModInfo)]), + io:format(" - native: ~w~n", [get_value([native], ModInfo)]), + io:format(" - loaded: ~w~n", [get_value([loaded], ModInfo)]), + ok. + +print_module({Mod, ModInfo}) -> + io:format(" - ~w:~n", [Mod]), + io:format(" - compiler: ~s~n", [get_value([compiler], ModInfo)]), + io:format(" - md5: ~s~n", [get_value([md5], ModInfo)]), + io:format(" - native: ~w~n", [get_value([native], ModInfo)]), + io:format(" - loaded: ~w~n", [get_value([loaded], ModInfo)]), + ok. + + + +%% get useful information from erlang:system_info/1 + +erlang_system_info() -> + erlang_system_info([ + allocator, + check_io, + otp_release, + port_limit, + process_limit, + % procs, % not needed + smp_support, + system_version, + system_architecture, + threads, + thread_pool_size, + {wordsize,internal}, + {wordsize,external}, + {cpu_topology, defined}, + {cpu_topology, detected}, + scheduler_bind_type, + scheduler_bindings, + compat_rel, + schedulers_state, + build_type, + logical_processors, + logical_processors_online, + logical_processors_available, + driver_version, + emu_args, + ethread_info, + beam_jump_table, + taints + ]). + +erlang_system_info([]) -> []; +erlang_system_info([Type|Types]) -> + [{Type, erlang:system_info(Type)}|erlang_system_info(Types)]. + + +%% get known useful erts environment + +os_getenv_erts_specific() -> + os_getenv_erts_specific([ + "BINDIR", + "DIALYZER_EMULATOR", + "CERL_DETACHED_PROG", + "EMU", + "ERL_CONSOLE_MODE", + "ERL_CRASH_DUMP", + "ERL_CRASH_DUMP_NICE", + "ERL_CRASH_DUMP_SECONDS", + "ERL_EPMD_PORT", + "ERL_EMULATOR_DLL", + "ERL_FULLSWEEP_AFTER", + "ERL_LIBS", + "ERL_MALLOC_LIB", + "ERL_MAX_PORTS", + "ERL_MAX_ETS_TABLES", + "ERL_NO_VFORK", + "ERL_NO_KERNEL_POLL", + "ERL_THREAD_POOL_SIZE", + "ERLC_EMULATOR", + "ESCRIPT_EMULATOR", + "HOME", + "HOMEDRIVE", + "HOMEPATH", + "LANG", + "LC_ALL", + "LC_CTYPE", + "PATH", + "PROGNAME", + "RELDIR", + "ROOTDIR", + "TERM", + %"VALGRIND_LOG_XML", + + %% heart + "COMSPEC", + "HEART_COMMAND", + + %% run_erl + "RUN_ERL_LOG_ALIVE_MINUTES", + "RUN_ERL_LOG_ACTIVITY_MINUTES", + "RUN_ERL_LOG_ALIVE_FORMAT", + "RUN_ERL_LOG_ALIVE_IN_UTC", + "RUN_ERL_LOG_GENERATIONS", + "RUN_ERL_LOG_MAXSIZE", + "RUN_ERL_DISABLE_FLOWCNTRL", + + %% driver getenv + "CALLER_DRV_USE_OUTPUTV", + "ERL_INET_GETHOST_DEBUG", + "ERL_EFILE_THREAD_SHORT_CIRCUIT", + "ERL_WINDOW_TITLE", + "ERL_ABORT_ON_FAILURE", + "TTYSL_DEBUG_LOG" + ]). + +os_getenv_erts_specific([]) -> []; +os_getenv_erts_specific([Key|Keys]) -> + [{Key, os:getenv(Key)}|os_getenv_erts_specific(Keys)]. + +split_env(Env) -> + split_env(Env, []). + +split_env([$=|Vs], Key) -> {lists:reverse(Key), Vs}; +split_env([I|Vs], Key) -> split_env(Vs, [I|Key]); +split_env([], KV) -> lists:reverse(KV). % should not happen. + +%% get applications + +code() -> + % order is important + get_code_from_paths(code:get_path()). + +get_code_from_paths([]) -> []; +get_code_from_paths([Path|Paths]) -> + case is_application_path(Path) of + true -> + [{application, get_application_from_path(Path)}|get_code_from_paths(Paths)]; + false -> + [{code, [ + {path, Path}, + {modules, get_modules_from_path(Path)} + ]}|get_code_from_paths(Paths)] + end. + +is_application_path(Path) -> + case filelib:wildcard(filename:join(Path, "*.app")) of + [] -> false; + _ -> true + end. + +get_application_from_path(Path) -> + [Appfile|_] = filelib:wildcard(filename:join(Path, "*.app")), + case file:consult(Appfile) of + {ok, [{application, App, Info}]} -> + {App, [ + {description, proplists:get_value(description, Info, [])}, + {vsn, proplists:get_value(vsn, Info, [])}, + {path, Path}, + {modules, get_modules_from_path(Path)} + ]} + end. + +get_modules_from_path(Path) -> + [ + begin + {ok,{Mod, Md5}} = beam_lib:md5(Beam), + Loaded = case code:is_loaded(Mod) of + false -> false; + _ -> true + end, + {Mod, [ + {loaded, Loaded}, + {native, beam_is_native_compiled(Beam)}, + {compiler, get_compiler_version(Beam)}, + {md5, hexstring(Md5)} + ]} + end || Beam <- filelib:wildcard(filename:join(Path, "*.beam")) + ]. + +hexstring(Bin) when is_binary(Bin) -> + lists:flatten([io_lib:format("~2.16.0b", [V]) || <<V>> <= Bin]). + +%% inspect beam files for information + +get_compiler_version(Beam) -> + case beam_lib:chunks(Beam, [compile_info]) of + {ok,{_,[{compile_info, Info}]}} -> + proplists:get_value(version, Info); + _ -> undefined + end. + +%% we don't know the specific chunk names of native code +%% we don't want to load the code to check it +beam_is_native_compiled(Beam) -> + Chunks = get_value([chunks], beam_lib:info(Beam)), + case check_known_hipe_chunks(Chunks) of + [] -> false; + [Arch] -> {true, Arch}; + Archs -> {true, Archs} + end. + + +check_known_hipe_chunks([{Tag,_,_}|Cs]) -> + case is_chunk_tag_hipe_arch(Tag) of + false -> check_known_hipe_chunks(Cs); + {true, Arch} -> [Arch|check_known_hipe_chunks(Cs)] + end; +check_known_hipe_chunks([]) -> []. + +%% these values are taken from hipe_unified_loader +%% perhaps these should be exported in that module? + +-define(HS8P_TAG,"HS8P"). +-define(HPPC_TAG,"HPPC"). +-define(HP64_TAG,"HP64"). +-define(HARM_TAG,"HARM"). +-define(HX86_TAG,"HX86"). +-define(HA64_TAG,"HA64"). + +is_chunk_tag_hipe_arch(Tag) -> + case Tag of + ?HA64_TAG -> {true, amd64}; %% HiPE, x86_64, (implicit: 64-bit, Unix) + ?HARM_TAG -> {true, arm}; %% HiPE, arm, v5 (implicit: 32-bit, Linux) + ?HPPC_TAG -> {true, powerpc}; %% HiPE, PowerPC (implicit: 32-bit, Linux) + ?HP64_TAG -> {true, ppc64}; %% HiPE, ppc64 (implicit: 64-bit, Linux) + ?HS8P_TAG -> {true, ultrasparc}; %% HiPE, SPARC, V8+ (implicit: 32-bit) + %% Future: HSV9 %% HiPE, SPARC, V9 (implicit: 64-bit) + %% HW32 %% HiPE, x86, Win32 + _ -> false + end. + + +get_dynamic_libraries() -> + Beam = filename:join([os:getenv("BINDIR"),get_beam_name()]), + case os:type() of + {unix, darwin} -> os:cmd("otool -L " ++ Beam); + _ -> os:cmd("ldd " ++ Beam) + end. + +get_beam_name() -> + Type = case erlang:system_info(build_type) of + opt -> ""; + TypeName -> "." ++ atom_to_list(TypeName) + end, + Flavor = case erlang:system_info(smp_support) of + false -> ""; + true -> ".smp" + end, + Beam = case os:getenv("EMU") of + false -> "beam"; + Value -> Value + end, + Beam ++ Type ++ Flavor. diff --git a/lib/runtime_tools/src/ttb_autostart.erl b/lib/runtime_tools/src/ttb_autostart.erl index 5339507cec..4c6971c119 100644 --- a/lib/runtime_tools/src/ttb_autostart.erl +++ b/lib/runtime_tools/src/ttb_autostart.erl @@ -1,4 +1,3 @@ -%%%-*- coding: utf-8 -*- %%%------------------------------------------------------------------- %%% File : ttb_autostart.erl %%% Author : Bartłomiej Puzoń <[email protected]> |