aboutsummaryrefslogtreecommitdiffstats
path: root/lib/diameter/src/info/diameter_dbg.erl
diff options
context:
space:
mode:
Diffstat (limited to 'lib/diameter/src/info/diameter_dbg.erl')
-rw-r--r--lib/diameter/src/info/diameter_dbg.erl514
1 files changed, 514 insertions, 0 deletions
diff --git a/lib/diameter/src/info/diameter_dbg.erl b/lib/diameter/src/info/diameter_dbg.erl
new file mode 100644
index 0000000000..e1d2086871
--- /dev/null
+++ b/lib/diameter/src/info/diameter_dbg.erl
@@ -0,0 +1,514 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2010-2016. All Rights Reserved.
+%%
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
+%%
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+%%
+%% Information and debug functions.
+%%
+
+-module(diameter_dbg).
+
+-export([table/1,
+ tables/0,
+ fields/1,
+ modules/0,
+ versions/0,
+ version_info/0,
+ compiled/0,
+ procs/0,
+ latest/0,
+ nl/0,
+ sizes/0]).
+
+-export([diameter_config/0,
+ diameter_peer/0,
+ diameter_reg/0,
+ diameter_request/0,
+ diameter_sequence/0,
+ diameter_service/0,
+ diameter_stats/0]).
+
+-export([pp/1,
+ subscriptions/0,
+ children/0]).
+
+%% Trace help.
+-export([tracer/0, tracer/1,
+ p/0, p/1,
+ stop/0,
+ tpl/1,
+ tp/1]).
+
+-include_lib("diameter/include/diameter.hrl").
+
+-define(APP, diameter).
+-define(I, diameter_info).
+
+-define(LOCAL, [diameter_config,
+ diameter_peer,
+ diameter_reg,
+ diameter_request,
+ diameter_sequence,
+ diameter_service,
+ diameter_stats]).
+
+-define(VALUES(Rec), tl(tuple_to_list(Rec))).
+
+%% ----------------------------------------------------------
+%% # sizes/0
+%%
+%% Return sizes of named tables.
+%% ----------------------------------------------------------
+
+sizes() ->
+ [{T, ets:info(T, size)} || T <- ?LOCAL, T /= diameter_peer].
+
+%% ----------------------------------------------------------
+%% # table/1
+%%
+%% Pretty-print a diameter table. Returns the number of records
+%% printed, or undefined.
+%% ----------------------------------------------------------
+
+table(T)
+ when (T == diameter_peer) orelse (T == diameter_reg) ->
+ ?I:format(collect(T), fields(T), fun ?I:split/2);
+
+table(Table)
+ when is_atom(Table) ->
+ case fields(Table) of
+ undefined = No ->
+ No;
+ Fields ->
+ ?I:format(Table, Fields, fun split/2)
+ end.
+
+split([started, name | Fs], [S, N | Vs]) ->
+ {name, [started | Fs], N, [S | Vs]};
+split([[F|FT]|Fs], [Rec|Vs]) ->
+ [_, V | VT] = tuple_to_list(Rec),
+ {F, FT ++ Fs, V, VT ++ Vs};
+split([F|Fs], [V|Vs]) ->
+ {F, Fs, V, Vs}.
+
+%% ----------------------------------------------------------
+%% # TableName/0
+%% ----------------------------------------------------------
+
+-define(TABLE(Name), Name() -> table(Name)).
+
+?TABLE(diameter_config).
+?TABLE(diameter_peer).
+?TABLE(diameter_reg).
+?TABLE(diameter_request).
+?TABLE(diameter_sequence).
+?TABLE(diameter_service).
+?TABLE(diameter_stats).
+
+%% ----------------------------------------------------------
+%% # tables/0
+%%
+%% Pretty-print diameter tables from all nodes. Returns the number of
+%% records printed.
+%% ----------------------------------------------------------
+
+tables() ->
+ ?I:format(field(?LOCAL), fun split/3, fun collect/1).
+
+field(Tables) ->
+ lists:map(fun(T) -> {T, fields(T)} end, lists:sort(Tables)).
+
+split(_, Fs, Vs) ->
+ split(Fs, Vs).
+
+%% ----------------------------------------------------------
+%% # modules/0
+%% ----------------------------------------------------------
+
+modules() ->
+ Path = filename:join([appdir(), atom_to_list(?APP) ++ ".app"]),
+ {ok, [{application, ?APP, Attrs}]} = file:consult(Path),
+ {modules, Mods} = lists:keyfind(modules, 1, Attrs),
+ Mods.
+
+appdir() ->
+ [_|_] = code:lib_dir(?APP, ebin).
+
+%% ----------------------------------------------------------
+%% # versions/0
+%% ----------------------------------------------------------
+
+versions() ->
+ ?I:versions(modules()).
+
+%% ----------------------------------------------------------
+%% # version_info/0
+%% ----------------------------------------------------------
+
+version_info() ->
+ ?I:version_info(modules()).
+
+%% ----------------------------------------------------------
+%% # compiled/0
+%% ----------------------------------------------------------
+
+compiled() ->
+ ?I:compiled(modules()).
+
+%% ----------------------------------------------------------
+%% # procs/0
+%% ----------------------------------------------------------
+
+procs() ->
+ ?I:procs(?APP).
+
+%% ----------------------------------------------------------
+%% # latest/0
+%% ----------------------------------------------------------
+
+latest() ->
+ ?I:latest(modules()).
+
+%% ----------------------------------------------------------
+%% # nl/0
+%% ----------------------------------------------------------
+
+nl() ->
+ lists:foreach(fun(M) -> abcast = c:nl(M) end, modules()).
+
+%% ----------------------------------------------------------
+%% # pp/1
+%%
+%% Description: Pretty-print a message binary.
+%% ----------------------------------------------------------
+
+%% Network byte order = big endian.
+
+pp(<<Version:8, MsgLength:24,
+ Rbit:1, Pbit:1, Ebit:1, Tbit:1, Reserved:4, CmdCode:24,
+ ApplId:32,
+ HbHid:32,
+ E2Eid:32,
+ AVPs/binary>>) ->
+ ?I:sep(),
+ ppp(["Version",
+ "Message length",
+ "[Actual length]",
+ "R(equest)",
+ "P(roxiable)",
+ "E(rror)",
+ "T(Potential retrans)",
+ "Reserved bits",
+ "Command code",
+ "Application id",
+ "Hop by hop id",
+ "End to end id"],
+ [Version, MsgLength, size(AVPs) + 20,
+ Rbit, Pbit, Ebit, Tbit, Reserved,
+ CmdCode,
+ ApplId,
+ HbHid,
+ E2Eid]),
+ N = avp_loop({AVPs, MsgLength - 20}, 0),
+ ?I:sep(),
+ N;
+
+pp(<<_Version:8, MsgLength:24, _/binary>> = Bin) ->
+ {bad_message_length, MsgLength, size(Bin)};
+
+pp(Bin)
+ when is_binary(Bin) ->
+ {truncated_binary, size(Bin)};
+
+pp(_) ->
+ not_binary.
+
+%% avp_loop/2
+
+avp_loop({Bin, Size}, N) ->
+ avp_loop(avp(Bin, Size), N+1);
+avp_loop(ok, N) ->
+ N;
+avp_loop([_E, _Rest] = L, N) ->
+ io:format("! ~s: ~p~n", L),
+ N;
+avp_loop([E, Rest, Fmt | Values], N)
+ when is_binary(Rest) ->
+ io:format("! ~s (" ++ Fmt ++ "): ~p~n", [E|Values] ++ [Rest]),
+ N.
+
+%% avp/2
+
+avp(<<>>, 0) ->
+ ok;
+avp(<<Code:32, Flags:1/binary, Length:24, Rest/binary>>,
+ Size) ->
+ avp(Code, Flags, Length, Rest, Size);
+avp(Bin, _) ->
+ ["truncated AVP header", Bin].
+
+%% avp/5
+
+avp(Code, Flags, Length, Rest, Size) ->
+ <<V:1, M:1, P:1, Res:5>>
+ = Flags,
+ b(),
+ ppp(["AVP Code",
+ "V(endor)",
+ "M(andatory)",
+ "P(Security)",
+ "R(eserved)",
+ "Length"],
+ [Code, V, M, P, Res, Length]),
+ avp(V, Rest, Length - 8, Size - 8).
+
+%% avp/4
+
+avp(1, <<V:32, Data/binary>>, Length, Size) ->
+ ppp({"Vendor-ID", V}),
+ data(Data, Length - 4, Size - 4);
+avp(1, Bin, _, _) ->
+ ["truncated Vendor-ID", Bin];
+avp(0, Data, Length, Size) ->
+ data(Data, Length, Size).
+
+data(Bin, Length, Size)
+ when size(Bin) >= Length ->
+ <<AVP:Length/binary, Rest/binary>> = Bin,
+ ppp({"Data", AVP}),
+ unpad(Rest, Size - Length, Length rem 4);
+
+data(Bin, _, _) ->
+ ["truncated AVP data", Bin].
+
+%% Remove padding bytes up to the next word boundary.
+unpad(Bin, Size, 0) ->
+ {Bin, Size};
+unpad(Bin, Size, N) ->
+ un(Bin, Size, 4 - N).
+
+un(Bin, Size, N)
+ when size(Bin) >= N ->
+ ppp({"Padding bytes", N}),
+ <<Pad:N/binary, Rest/binary>> = Bin,
+ Bits = N*8,
+ case Pad of
+ <<0:Bits>> ->
+ {Rest, Size - N};
+ _ ->
+ ["non-zero padding", Bin, "~p", N]
+ end;
+
+un(Bin, _, _) ->
+ ["truncated padding", Bin].
+
+b() ->
+ io:format("#~n").
+
+ppp(Fields, Values) ->
+ lists:foreach(fun ppp/1, lists:zip(Fields, Values)).
+
+ppp({Field, Value}) ->
+ io:format(": ~-22s : ~p~n", [Field, Value]).
+
+%% ----------------------------------------------------------
+%% # subscriptions/0
+%%
+%% Returns a list of {SvcName, Pid}.
+%% ----------------------------------------------------------
+
+subscriptions() ->
+ diameter_service:subscriptions().
+
+%% ----------------------------------------------------------
+%% # children/0
+%% ----------------------------------------------------------
+
+children() ->
+ diameter_sup:tree().
+
+%% ----------------------------------------------------------
+
+%% tracer/[12]
+
+tracer(Port)
+ when is_integer(Port) ->
+ dbg:tracer(port, dbg:trace_port(ip, Port));
+
+tracer(Path)
+ when is_list(Path) ->
+ dbg:tracer(port, dbg:trace_port(file, Path)).
+
+tracer() ->
+ dbg:tracer(process, {fun p/2, ok}).
+
+p(T,_) ->
+ io:format("+ ~p~n", [T]).
+
+%% p/[01]
+
+p() ->
+ p([c,timestamp]).
+
+p(T) ->
+ dbg:p(all,T).
+
+%% stop/0
+
+stop() ->
+ dbg:ctp(),
+ dbg:stop_clear().
+
+%% tpl/1
+%% tp/1
+
+tpl(T) ->
+ dbg(tpl, T).
+
+tp(T) ->
+ dbg(tp, T).
+
+%% dbg/2
+
+dbg(F, L)
+ when is_list(L) ->
+ [dbg(F, X) || X <- L];
+
+dbg(F, M)
+ when is_atom(M) ->
+ apply(dbg, F, [M, x]);
+
+dbg(F, T)
+ when is_tuple(T) ->
+ apply(dbg, F, tuple_to_list(T)).
+
+%% ===========================================================================
+%% ===========================================================================
+
+%% collect/1
+
+collect(diameter_peer) ->
+ lists:flatmap(fun peers/1, diameter:services());
+
+collect(diameter_reg) ->
+ diameter_reg:terms();
+
+collect(Name) ->
+ c(ets:info(Name), Name).
+
+c(undefined, _) ->
+ [];
+c(_, Name) ->
+ ets:tab2list(Name).
+
+%% peers/1
+
+peers(Name) ->
+ peers(Name, diameter:service_info(Name, transport)).
+
+peers(_, undefined) ->
+ [];
+peers(Name, Ts) ->
+ lists:flatmap(fun(T) -> mk_peers(Name, T) end, Ts).
+
+mk_peers(Name, [_, {type, connect} | _] = Ts) ->
+ [[Name | mk_peer(Ts)]];
+mk_peers(Name, [R, {type, listen}, O, {accept = A, As} | _]) ->
+ [[Name | mk_peer([R, {type, A}, O | Ts])] || Ts <- As].
+%% This is a bit lame: service_info works to build this list and out
+%% of something like what we want here and then we take it apart.
+
+mk_peer(Vs) ->
+ [Type, Ref, State, Opts, WPid, TPid, SApps, Caps]
+ = get_values(Vs, [type,ref,state,options,watchdog,peer,apps,caps]),
+ [Ref, State, [{type, Type} | Opts], s(WPid), s(TPid), SApps, Caps].
+
+get_values(Vs, Ks) ->
+ [proplists:get_value(K, Vs) || K <- Ks].
+
+s(undefined = T) ->
+ T;
+s({Pid, _Started, _State}) ->
+ state(Pid);
+s({Pid, _Started}) ->
+ state(Pid).
+
+%% Collect states from watchdog/transport pids.
+state(Pid) ->
+ MRef = erlang:monitor(process, Pid),
+ Pid ! {state, self()},
+ receive
+ {'DOWN', MRef, process, _, _} ->
+ Pid;
+ {Pid, _} = T ->
+ erlang:demonitor(MRef, [flush]),
+ T
+ end.
+
+%% fields/1
+
+-define(FIELDS(Table), fields(Table) -> record_info(fields, Table)).
+
+fields(diameter_config) ->
+ [];
+
+fields(T)
+ when T == diameter_request;
+ T == diameter_sequence ->
+ fun kv/1;
+
+fields(diameter_stats) ->
+ fun({Ctr, N}) when not is_pid(Ctr) ->
+ {[counter, value], [Ctr, N]};
+ (_) ->
+ []
+ end;
+
+fields(diameter_service) ->
+ [started,
+ name,
+ record_info(fields, diameter_service),
+ watchdogT,
+ peerT,
+ shared_peers,
+ local_peers,
+ monitor,
+ options];
+
+?FIELDS(diameter_event);
+?FIELDS(diameter_uri);
+?FIELDS(diameter_avp);
+?FIELDS(diameter_header);
+?FIELDS(diameter_packet);
+?FIELDS(diameter_app);
+?FIELDS(diameter_caps);
+
+fields(diameter_peer) ->
+ [service, ref, state, options, watchdog, peer, applications, capabilities];
+
+fields(diameter_reg) ->
+ [property, pids];
+
+fields(_) ->
+ undefined.
+
+kv({_,_}) ->
+ [key, value];
+kv(_) ->
+ [].