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/erts_alloc_config.erl169
-rw-r--r--lib/runtime_tools/src/observer_backend.erl28
-rw-r--r--lib/runtime_tools/src/runtime_tools.app.src4
-rw-r--r--lib/runtime_tools/src/runtime_tools.appup.src10
-rw-r--r--lib/runtime_tools/src/system_information.erl282
5 files changed, 416 insertions, 77 deletions
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 68ef04f20c..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
@@ -227,7 +227,9 @@ 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),
@@ -244,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(),
@@ -264,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 d46cfe1f32..0a70802c08 100644
--- a/lib/runtime_tools/src/runtime_tools.app.src
+++ b/lib/runtime_tools/src/runtime_tools.app.src
@@ -25,6 +25,8 @@
{registered, [runtime_tools_sup]},
{applications, [kernel, stdlib]},
{env, []},
- {mod, {runtime_tools, []}}]}.
+ {mod, {runtime_tools, []}},
+ {runtime_dependencies, ["stdlib-2.0","mnesia-4.12","kernel-3.0",
+ "erts-6.0"]}]}.
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/system_information.erl b/lib/runtime_tools/src/system_information.erl
index 603b698d5e..f541d6e449 100644
--- a/lib/runtime_tools/src/system_information.erl
+++ b/lib/runtime_tools/src/system_information.erl
@@ -39,7 +39,8 @@
application/1, application/2,
environment/0, environment/1,
module/1, module/2,
- modules/1
+ modules/1,
+ sanity_check/0
]).
%% gen_server callbacks
@@ -85,9 +86,14 @@ report() -> [
{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()]}
+ {environment, [split_env(Env) || Env <- os:getenv()]},
+ {sanity_check, sanity_check()}
].
+-spec to_file(FileName) -> ok | {error, Reason} when
+ FileName :: file:name_all(),
+ Reason :: file:posix() | badarg | terminated | system_limit.
+
to_file(File) ->
file:write_file(File, iolist_to_binary([
io_lib:format("{system_information_version, ~p}.~n", [
@@ -130,6 +136,27 @@ module(M, Opts) when is_atom(M), is_list(Opts) ->
modules(Opt) when is_atom(Opt) ->
gen_server:call(?SERVER, {modules, Opt}).
+
+-spec sanity_check() -> ok | {failed, Failures} when
+ Application :: atom(),
+ ApplicationVersion :: string(),
+ MissingRuntimeDependencies :: {missing_runtime_dependencies,
+ ApplicationVersion,
+ [ApplicationVersion]},
+ InvalidApplicationVersion :: {invalid_application_version,
+ ApplicationVersion},
+ InvalidAppFile :: {invalid_app_file, Application},
+ Failure :: MissingRuntimeDependencies
+ | InvalidApplicationVersion
+ | InvalidAppFile,
+ Failures :: [Failure].
+
+sanity_check() ->
+ case check_runtime_dependencies() of
+ [] -> ok;
+ Issues -> {failed, Issues}
+ end.
+
%%===================================================================
%% gen_server callbacks
%%===================================================================
@@ -457,6 +484,8 @@ get_application_from_path(Path) ->
{description, proplists:get_value(description, Info, [])},
{vsn, proplists:get_value(vsn, Info, [])},
{path, Path},
+ {runtime_dependencies,
+ proplists:get_value(runtime_dependencies, Info, [])},
{modules, get_modules_from_path(Path)}
]}
end.
@@ -552,3 +581,252 @@ get_beam_name() ->
Value -> Value
end,
Beam ++ Type ++ Flavor.
+
+%% Check runtime dependencies...
+
+vsnstr2vsn(VsnStr) ->
+ list_to_tuple(lists:map(fun (Part) ->
+ list_to_integer(Part)
+ end,
+ string:tokens(VsnStr, "."))).
+
+rtdepstrs2rtdeps([]) ->
+ [];
+rtdepstrs2rtdeps([RTDep | RTDeps]) ->
+ [AppStr, VsnStr] = string:tokens(RTDep, "-"),
+ [{list_to_atom(AppStr), vsnstr2vsn(VsnStr)} | rtdepstrs2rtdeps(RTDeps)].
+
+build_app_table([], AppTab) ->
+ AppTab;
+build_app_table([App | Apps], AppTab0) ->
+ AppTab1 = try
+ %% We may have multiple application versions installed
+ %% of the same application! It is therefore important
+ %% to look up the application version that actually will
+ %% be used via code server.
+ AppFile = code:where_is_file(atom_to_list(App) ++ ".app"),
+ {ok, [{application, App, Info}]} = file:consult(AppFile),
+ VsnStr = proplists:get_value(vsn, Info),
+ Vsn = vsnstr2vsn(VsnStr),
+ RTDepStrs = proplists:get_value(runtime_dependencies,
+ Info, []),
+ RTDeps = rtdepstrs2rtdeps(RTDepStrs),
+ gb_trees:insert(App, {Vsn, RTDeps}, AppTab0)
+ catch
+ _ : _ ->
+ AppTab0
+ end,
+ build_app_table(Apps, AppTab1).
+
+meets_min_req(Vsn, Vsn) ->
+ true;
+meets_min_req({X}, VsnReq) ->
+ meets_min_req({X, 0, 0}, VsnReq);
+meets_min_req({X, Y}, VsnReq) ->
+ meets_min_req({X, Y, 0}, VsnReq);
+meets_min_req(Vsn, {X}) ->
+ meets_min_req(Vsn, {X, 0, 0});
+meets_min_req(Vsn, {X, Y}) ->
+ meets_min_req(Vsn, {X, Y, 0});
+meets_min_req({X, _Y, _Z}, {XReq, _YReq, _ZReq}) when X > XReq ->
+ true;
+meets_min_req({X, Y, _Z}, {X, YReq, _ZReq}) when Y > YReq ->
+ true;
+meets_min_req({X, Y, Z}, {X, Y, ZReq}) when Z > ZReq ->
+ true;
+meets_min_req({_X, _Y, _Z}, {_XReq, _YReq, _ZReq}) ->
+ false;
+meets_min_req(Vsn, VsnReq) ->
+ gp_meets_min_req(mk_gp_vsn_list(Vsn), mk_gp_vsn_list(VsnReq)).
+
+gp_meets_min_req([X, Y, Z | _Vs], [X, Y, Z]) ->
+ true;
+gp_meets_min_req([X, Y, Z | _Vs], [XReq, YReq, ZReq]) ->
+ meets_min_req({X, Y, Z}, {XReq, YReq, ZReq});
+gp_meets_min_req([X, Y, Z | Vs], [X, Y, Z | VReqs]) ->
+ gp_meets_min_req_tail(Vs, VReqs);
+gp_meets_min_req(_Vsn, _VReq) ->
+ %% Versions on different version branches, i.e., the minimum
+ %% required functionality is not included in Vsn.
+ false.
+
+gp_meets_min_req_tail([V | Vs], [V | VReqs]) ->
+ gp_meets_min_req_tail(Vs, VReqs);
+gp_meets_min_req_tail([], []) ->
+ true;
+gp_meets_min_req_tail([_V | _Vs], []) ->
+ true;
+gp_meets_min_req_tail([V | _Vs], [VReq]) when V > VReq ->
+ true;
+gp_meets_min_req_tail(_Vs, _VReqs) ->
+ %% Versions on different version branches, i.e., the minimum
+ %% required functionality is not included in Vsn.
+ false.
+
+mk_gp_vsn_list(Vsn) ->
+ [X, Y, Z | Tail] = tuple_to_list(Vsn),
+ [X, Y, Z | remove_trailing_zeroes(Tail)].
+
+remove_trailing_zeroes([]) ->
+ [];
+remove_trailing_zeroes([0 | Vs]) ->
+ case remove_trailing_zeroes(Vs) of
+ [] -> [];
+ NewVs -> [0 | NewVs]
+ end;
+remove_trailing_zeroes([V | Vs]) ->
+ [V | remove_trailing_zeroes(Vs)].
+
+mk_app_vsn_str({App, Vsn}) ->
+ mk_app_vsn_str(App, Vsn).
+
+mk_app_vsn_str(App, Vsn) ->
+ VsnList = tuple_to_list(Vsn),
+ lists:flatten([atom_to_list(App),
+ $-,
+ integer_to_list(hd(VsnList)),
+ lists:map(fun (Part) ->
+ [$., integer_to_list(Part)]
+ end, tl(VsnList))]).
+
+otp_17_0_vsns_orddict() ->
+ [{asn1,{3,0}},
+ {common_test,{1,8}},
+ {compiler,{5,0}},
+ {cosEvent,{2,1,15}},
+ {cosEventDomain,{1,1,14}},
+ {cosFileTransfer,{1,1,16}},
+ {cosNotification,{1,1,21}},
+ {cosProperty,{1,1,17}},
+ {cosTime,{1,1,14}},
+ {cosTransactions,{1,2,14}},
+ {crypto,{3,3}},
+ {debugger,{4,0}},
+ {dialyzer,{2,7}},
+ {diameter,{1,6}},
+ {edoc,{0,7,13}},
+ {eldap,{1,0,3}},
+ {erl_docgen,{0,3,5}},
+ {erl_interface,{3,7,16}},
+ {erts,{6,0}},
+ {et,{1,5}},
+ {eunit,{2,2,7}},
+ {gs,{1,5,16}},
+ {hipe,{3,10,3}},
+ {ic,{4,3,5}},
+ {inets,{5,10}},
+ {jinterface,{1,5,9}},
+ {kernel,{3,0}},
+ {megaco,{3,17,1}},
+ {mnesia,{4,12}},
+ {observer,{2,0}},
+ {odbc,{2,10,20}},
+ {orber,{3,6,27}},
+ {os_mon,{2,2,15}},
+ {ose,{1,0}},
+ {otp_mibs,{1,0,9}},
+ {parsetools,{2,0,11}},
+ {percept,{0,8,9}},
+ {public_key,{0,22}},
+ {reltool,{0,6,5}},
+ {runtime_tools,{1,8,14}},
+ {sasl,{2,4}},
+ {snmp,{4,25,1}},
+ {ssh,{3,0,1}},
+ {ssl,{5,3,4}},
+ {stdlib,{2,0}},
+ {syntax_tools,{1,6,14}},
+ {test_server,{3,7}},
+ {tools,{2,6,14}},
+ {typer,{0,9,6}},
+ {webtool,{0,8,10}},
+ {wx,{1,2}},
+ {xmerl,{1,3,7}}].
+
+otp_17_0_vsns_tab() ->
+ gb_trees:from_orddict(otp_17_0_vsns_orddict()).
+
+check_runtime_dependency({App, DepVsn}, AppTab) ->
+ case gb_trees:lookup(App, AppTab) of
+ none ->
+ false;
+ {value, {Vsn, _}} ->
+ meets_min_req(Vsn, DepVsn)
+ end.
+
+check_runtime_dependencies(App, AppTab, OtpMinVsnTab) ->
+ case gb_trees:lookup(App, AppTab) of
+ none ->
+ [{invalid_app_file, App}];
+ {value, {Vsn, RTDeps}} ->
+ RTD = case lists:foldl(
+ fun (RTDep, Acc) ->
+ case check_runtime_dependency(RTDep, AppTab) of
+ true ->
+ Acc;
+ false ->
+ [mk_app_vsn_str(RTDep) | Acc]
+ end
+ end,
+ [],
+ RTDeps) of
+ [] ->
+ [];
+ MissingDeps ->
+ [{missing_runtime_dependencies,
+ mk_app_vsn_str(App, Vsn),
+ MissingDeps}]
+ end,
+ case gb_trees:lookup(App, OtpMinVsnTab) of
+ none ->
+ RTD;
+ {value, MinVsn} ->
+ case meets_min_req(Vsn, MinVsn) of
+ true ->
+ RTD;
+ false ->
+ [{invalid_application_version,
+ mk_app_vsn_str(App, Vsn)} | RTD]
+ end
+ end
+ end.
+
+app_file_to_app(AF) ->
+ list_to_atom(filename:basename(AF, ".app")).
+
+get_apps() ->
+ get_apps(code:get_path(), []).
+
+get_apps([], Apps) ->
+ lists:usort(Apps);
+get_apps([Path|Paths], Apps) ->
+ case filelib:wildcard(filename:join(Path, "*.app")) of
+ [] ->
+ %% Not app or invalid app
+ get_apps(Paths, Apps);
+ [AppFile] ->
+ get_apps(Paths, [app_file_to_app(AppFile) | Apps]);
+ [_AppFile| _] = AppFiles ->
+ %% Strange with multple .app files... Lets put them
+ %% all in the list and see what we get...
+ lists:map(fun (AF) ->
+ app_file_to_app(AF)
+ end, AppFiles) ++ Apps
+ end.
+
+check_runtime_dependencies() ->
+ OtpMinVsnTab = otp_17_0_vsns_tab(),
+ Apps = get_apps(),
+ AppTab = build_app_table(Apps, gb_trees:empty()),
+ lists:foldl(fun (App, Acc) ->
+ case check_runtime_dependencies(App,
+ AppTab,
+ OtpMinVsnTab) of
+ [] -> Acc;
+ Issues -> Issues ++ Acc
+ end
+ end,
+ [],
+ Apps).
+
+%% End of runtime dependency checks