aboutsummaryrefslogtreecommitdiffstats
path: root/lib/runtime_tools/src
diff options
context:
space:
mode:
Diffstat (limited to 'lib/runtime_tools/src')
-rw-r--r--lib/runtime_tools/src/Makefile1
-rw-r--r--lib/runtime_tools/src/dbg.erl8
-rw-r--r--lib/runtime_tools/src/erts_alloc_config.erl169
-rw-r--r--lib/runtime_tools/src/observer_backend.erl47
-rw-r--r--lib/runtime_tools/src/runtime_tools.app.src2
-rw-r--r--lib/runtime_tools/src/runtime_tools.appup.src10
-rw-r--r--lib/runtime_tools/src/runtime_tools_sup.erl1
-rw-r--r--lib/runtime_tools/src/system_information.erl554
-rw-r--r--lib/runtime_tools/src/ttb_autostart.erl1
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]>