fix(bpapi): Optimize BPAPI dump size
This commit is contained in:
parent
2c3af8d9fe
commit
64378be9a0
|
@ -16,21 +16,23 @@
|
||||||
|
|
||||||
-module(emqx_bpapi_static_checks).
|
-module(emqx_bpapi_static_checks).
|
||||||
|
|
||||||
-export([run/1, run/0]).
|
-export([dump/1, dump/0]).
|
||||||
|
|
||||||
-include_lib("emqx/include/logger.hrl").
|
-include_lib("emqx/include/logger.hrl").
|
||||||
|
|
||||||
%% `emqx_bpapi:call' enriched with dialyzer spec
|
-type api_dump() :: #{{emqx_bpapi:api(), emqx_bpapi:api_version()} =>
|
||||||
-type typed_call() :: {emqx_bpapi:call(), _DialyzerSpec}.
|
#{ calls := [emqx_bpapi:rpc()]
|
||||||
-type typed_rpc() :: {typed_call(), typed_call()}.
|
, casts := [emqx_bpapi:rpc()]
|
||||||
|
|
||||||
-type fulldump() :: #{emqx_bpapi:api() =>
|
|
||||||
#{emqx_bpapi:api_version() =>
|
|
||||||
#{ calls := [typed_rpc()]
|
|
||||||
, casts := [typed_rpc()]
|
|
||||||
}
|
|
||||||
}}.
|
}}.
|
||||||
|
|
||||||
|
-type dialyzer_spec() :: {_Type, [_Type]}.
|
||||||
|
|
||||||
|
-type dialyzer_dump() :: #{mfa() => dialyzer_spec()}.
|
||||||
|
|
||||||
|
-type fulldump() :: #{ api => api_dump()
|
||||||
|
, signatures => dialyzer_dump()
|
||||||
|
}.
|
||||||
|
|
||||||
%% Applications we wish to ignore in the analysis:
|
%% Applications we wish to ignore in the analysis:
|
||||||
-define(IGNORED_APPS, "gen_rpc, recon, observer_cli, snabbkaffe, ekka, mria").
|
-define(IGNORED_APPS, "gen_rpc, recon, observer_cli, snabbkaffe, ekka, mria").
|
||||||
%% List of known RPC backend modules:
|
%% List of known RPC backend modules:
|
||||||
|
@ -40,24 +42,26 @@
|
||||||
|
|
||||||
-define(XREF, myxref).
|
-define(XREF, myxref).
|
||||||
|
|
||||||
run() ->
|
dump() ->
|
||||||
case {filelib:wildcard("*_plt"), filelib:wildcard("_build/emqx*/lib")} of
|
case {filelib:wildcard("*_plt"), filelib:wildcard("_build/emqx*/lib")} of
|
||||||
{[PLT|_], [RelDir|_]} ->
|
{[PLT|_], [RelDir|_]} ->
|
||||||
run(#{plt => PLT, reldir => RelDir});
|
dump(#{plt => PLT, reldir => RelDir});
|
||||||
_ ->
|
_ ->
|
||||||
error("failed to guess run options")
|
error("failed to guess run options")
|
||||||
end.
|
end.
|
||||||
|
|
||||||
-spec run(map()) -> boolean().
|
%% Collect the local BPAPI modules to a dump file:
|
||||||
run(Opts) ->
|
-spec dump(map()) -> boolean().
|
||||||
|
dump(Opts) ->
|
||||||
put(bpapi_ok, true),
|
put(bpapi_ok, true),
|
||||||
PLT = prepare(Opts),
|
PLT = prepare(Opts),
|
||||||
%% First we run XREF to find all callers of any known RPC backend:
|
%% First we run XREF to find all callers of any known RPC backend:
|
||||||
Callers = find_remote_calls(Opts),
|
Callers = find_remote_calls(Opts),
|
||||||
{BPAPICalls, NonBPAPICalls} = lists:partition(fun is_bpapi_call/1, Callers),
|
{BPAPICalls, NonBPAPICalls} = lists:partition(fun is_bpapi_call/1, Callers),
|
||||||
warn_nonbpapi_rpcs(NonBPAPICalls),
|
warn_nonbpapi_rpcs(NonBPAPICalls),
|
||||||
CombinedAPI = collect_bpapis(BPAPICalls, PLT),
|
APIDump = collect_bpapis(BPAPICalls),
|
||||||
dump_api(CombinedAPI),
|
DialyzerDump = collect_signatures(PLT, APIDump),
|
||||||
|
dump_api(#{api => APIDump, signatures => DialyzerDump}),
|
||||||
erase(bpapi_ok).
|
erase(bpapi_ok).
|
||||||
|
|
||||||
prepare(#{reldir := RelDir, plt := PLT}) ->
|
prepare(#{reldir := RelDir, plt := PLT}) ->
|
||||||
|
@ -100,45 +104,58 @@ is_bpapi_call({Module, _Function, _Arity}) ->
|
||||||
end.
|
end.
|
||||||
|
|
||||||
-spec dump_api(fulldump()) -> ok.
|
-spec dump_api(fulldump()) -> ok.
|
||||||
dump_api(Term) ->
|
dump_api(Term = #{api := _, signatures := _}) ->
|
||||||
Filename = filename:join(code:priv_dir(emqx_bpapi), emqx_app:get_release() ++ ".bpapi"),
|
Filename = filename:join(code:priv_dir(emqx), emqx_app:get_release() ++ ".bpapi"),
|
||||||
file:write_file(Filename, io_lib:format("~0p.", [Term])).
|
file:write_file(Filename, io_lib:format("~0p.", [Term])).
|
||||||
|
|
||||||
-spec collect_bpapis([mfa()], _PLT) -> fulldump().
|
-spec collect_bpapis([mfa()]) -> api_dump().
|
||||||
collect_bpapis(L, PLT) ->
|
collect_bpapis(L) ->
|
||||||
Modules = lists:usort([M || {M, _F, _A} <- L]),
|
Modules = lists:usort([M || {M, _F, _A} <- L]),
|
||||||
lists:foldl(fun(Mod, Acc) ->
|
lists:foldl(fun(Mod, Acc) ->
|
||||||
#{ api := API
|
#{ api := API
|
||||||
, version := Vsn
|
, version := Vsn
|
||||||
, calls := Calls0
|
, calls := Calls
|
||||||
, casts := Casts0
|
, casts := Casts
|
||||||
} = Mod:bpapi_meta(),
|
} = Mod:bpapi_meta(),
|
||||||
Calls = enrich(PLT, Calls0),
|
Acc#{{API, Vsn} => #{ calls => Calls
|
||||||
Casts = enrich(PLT, Casts0),
|
|
||||||
Acc#{API => #{Vsn => #{ calls => Calls
|
|
||||||
, casts => Casts
|
, casts => Casts
|
||||||
}}}
|
}}
|
||||||
end,
|
end,
|
||||||
#{},
|
#{},
|
||||||
Modules
|
Modules
|
||||||
).
|
).
|
||||||
|
|
||||||
%% Add information about types from the PLT
|
-spec collect_signatures(_PLT, api_dump()) -> dialyzer_dump().
|
||||||
-spec enrich(_PLT, [emqx_bpapi:rpc()]) -> [typed_rpc()].
|
collect_signatures(PLT, APIs) ->
|
||||||
enrich(PLT, Calls) ->
|
maps:fold(fun(_APIAndVersion, #{calls := Calls, casts := Casts}, Acc0) ->
|
||||||
[case {lookup_type(PLT, From), lookup_type(PLT, To)} of
|
Acc1 = lists:foldl(fun enrich/2, {Acc0, PLT}, Calls),
|
||||||
|
{Acc, PLT} = lists:foldl(fun enrich/2, Acc1, Casts),
|
||||||
|
Acc
|
||||||
|
end,
|
||||||
|
#{},
|
||||||
|
APIs).
|
||||||
|
|
||||||
|
%% Add information about the call types from the PLT
|
||||||
|
-spec enrich(emqx_bpapi:rpc(), {dialyzer_dump(), _PLT}) -> {dialyzer_dump(), _PLT}.
|
||||||
|
enrich({From0, To0}, {Acc0, PLT}) ->
|
||||||
|
From = call_to_mfa(From0),
|
||||||
|
To = call_to_mfa(To0),
|
||||||
|
case {dialyzer_plt:lookup(PLT, From), dialyzer_plt:lookup(PLT, To)} of
|
||||||
{{value, TFrom}, {value, TTo}} ->
|
{{value, TFrom}, {value, TTo}} ->
|
||||||
{{From, TFrom}, {To, TTo}};
|
Acc = Acc0#{ From => TFrom
|
||||||
|
, To => TTo
|
||||||
|
},
|
||||||
|
{Acc, PLT};
|
||||||
{_, none} ->
|
{_, none} ->
|
||||||
setnok(),
|
setnok(),
|
||||||
?CRITICAL("Backplane API function ~s calls a missing remote function ~s",
|
?CRITICAL("Backplane API function ~s calls a missing remote function ~s",
|
||||||
[format_call(From), format_call(To)]),
|
[format_call(From0), format_call(To0)]),
|
||||||
error(missing_target)
|
error(missing_target)
|
||||||
end
|
end.
|
||||||
|| {From, To} <- Calls].
|
|
||||||
|
|
||||||
lookup_type(PLT, {M, F, A}) ->
|
-spec call_to_mfa(emqx_bpapi:call()) -> mfa().
|
||||||
dialyzer_plt:lookup(PLT, {M, F, length(A)}).
|
call_to_mfa({M, F, A}) ->
|
||||||
|
{M, F, length(A)}.
|
||||||
|
|
||||||
format_call({M, F, A}) ->
|
format_call({M, F, A}) ->
|
||||||
io_lib:format("~p:~p/~p", [M, F, length(A)]).
|
io_lib:format("~p:~p/~p", [M, F, length(A)]).
|
||||||
|
|
Loading…
Reference in New Issue