aboutsummaryrefslogtreecommitdiffstats
path: root/lib/diameter/src/base
diff options
context:
space:
mode:
authorAnders Svensson <[email protected]>2014-03-29 18:34:38 +0100
committerAnders Svensson <[email protected]>2014-03-29 19:20:45 +0100
commit5ca206af45b3195233991dfe820e9fb636800a33 (patch)
treecad0095bc906fd4e7aa6272e98fd017a2b91469b /lib/diameter/src/base
parentc23b242d0d634d10b03412c19768d381906578ab (diff)
downloadotp-5ca206af45b3195233991dfe820e9fb636800a33.tar.gz
otp-5ca206af45b3195233991dfe820e9fb636800a33.tar.bz2
otp-5ca206af45b3195233991dfe820e9fb636800a33.zip
Move info modules into own subdirectory
Possibly overkill for two modules but it mirrors their different treatment by the makefile.
Diffstat (limited to 'lib/diameter/src/base')
-rw-r--r--lib/diameter/src/base/diameter_dbg.erl516
-rw-r--r--lib/diameter/src/base/diameter_info.erl869
2 files changed, 0 insertions, 1385 deletions
diff --git a/lib/diameter/src/base/diameter_dbg.erl b/lib/diameter/src/base/diameter_dbg.erl
deleted file mode 100644
index 5edbabedeb..0000000000
--- a/lib/diameter/src/base/diameter_dbg.erl
+++ /dev/null
@@ -1,516 +0,0 @@
-%%
-%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2010-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
-%% 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%
-%%
-
-%%
-%% Information and debug functions.
-%%
-
--module(diameter_dbg).
-
--export([table/1,
- tables/0,
- fields/1,
- help/0,
- modules/0,
- versions/0,
- version_info/0,
- compiled/0,
- procs/0,
- latest/0,
- nl/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").
--include("diameter_internal.hrl").
-
-
--define(INFO, diameter_info).
--define(SEP(), ?INFO:sep()).
-
--define(LOCAL, [diameter_config,
- diameter_peer,
- diameter_reg,
- diameter_request,
- diameter_sequence,
- diameter_service,
- diameter_stats]).
-
--define(VALUES(Rec), tl(tuple_to_list(Rec))).
-
-%%% ----------------------------------------------------------
-%%% # help()
-%%% ----------------------------------------------------------
-
-help() ->
- not_yet_implemented.
-
-%%% ----------------------------------------------------------
-%%% # table(TableName)
-%%%
-%%% Input: TableName = diameter table containing record entries.
-%%%
-%%% Output: Count | undefined
-%%% ----------------------------------------------------------
-
-table(T)
- when (T == diameter_peer) orelse (T == diameter_reg) ->
- ?INFO:format(collect(T), fields(T), fun ?INFO:split/2);
-
-table(Table)
- when is_atom(Table) ->
- case fields(Table) of
- undefined = No ->
- No;
- Fields ->
- ?INFO: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()
-%%% ----------------------------------------------------------
-
--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()
-%%%
-%%% Output: Number of records output.
-%%%
-%%% Description: Pretty-print records in diameter tables from all nodes.
-%%% ----------------------------------------------------------
-
-tables() ->
- ?INFO: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()
-%%% ----------------------------------------------------------
-
-modules() ->
- Path = filename:join([appdir(), atom_to_list(?APPLICATION) ++ ".app"]),
- {ok, [{application, ?APPLICATION, Attrs}]} = file:consult(Path),
- {modules, Mods} = lists:keyfind(modules, 1, Attrs),
- Mods.
-
-appdir() ->
- [_|_] = code:lib_dir(?APPLICATION, ebin).
-
-%%% ----------------------------------------------------------
-%%% # versions()
-%%% ----------------------------------------------------------
-
-versions() ->
- ?INFO:versions(modules()).
-
-%%% ----------------------------------------------------------
-%%% # versions()
-%%% ----------------------------------------------------------
-
-version_info() ->
- ?INFO:version_info(modules()).
-
-%%% ----------------------------------------------------------
-%%% # compiled()
-%%% ----------------------------------------------------------
-
-compiled() ->
- ?INFO:compiled(modules()).
-
-%%% ----------------------------------------------------------
-%%% procs()
-%%% ----------------------------------------------------------
-
-procs() ->
- ?INFO:procs(?APPLICATION).
-
-%%% ----------------------------------------------------------
-%%% # latest()
-%%% ----------------------------------------------------------
-
-latest() ->
- ?INFO:latest(modules()).
-
-%%% ----------------------------------------------------------
-%%% # nl()
-%%% ----------------------------------------------------------
-
-nl() ->
- lists:foreach(fun(M) -> abcast = c:nl(M) end, modules()).
-
-%%% ----------------------------------------------------------
-%%% # pp(Bin)
-%%%
-%%% 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>>) ->
- ?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),
- ?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()
-%%%
-%%% Output: list of {SvcName, Pid}
-%%% ----------------------------------------------------------
-
-subscriptions() ->
- diameter_service:subscriptions().
-
-%%% ----------------------------------------------------------
-%%% # children()
-%%% ----------------------------------------------------------
-
-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),
- peerT,
- connT,
- share_peers,
- use_shared_peers,
- shared_peers,
- local_peers,
- monitor];
-
-?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(_) ->
- [].
diff --git a/lib/diameter/src/base/diameter_info.erl b/lib/diameter/src/base/diameter_info.erl
deleted file mode 100644
index 10972f3231..0000000000
--- a/lib/diameter/src/base/diameter_info.erl
+++ /dev/null
@@ -1,869 +0,0 @@
-%%
-%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2010-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
-%% 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%
-%%
-
-%%
-%% Generic functions for formatting table listings and more. Used by
-%% diameter_dbg.
-%%
-
--module(diameter_info).
-
--export([usage/1,
- format/1,
- format/2,
- format/3,
- format/4,
- table/2,
- tables/1,
- tables/2,
- split/2,
- split/3,
- tab2list/1,
- modules/1,
- versions/1,
- version_info/1,
- attrs/2,
- compiled/1,
- procs/1,
- latest/1,
- list/1]).
-
-%% Support for rolling your own.
--export([sep/0,
- sep/1,
- widest/1,
- p/1,
- p/3]).
-
--compile({no_auto_import,[max/2]}).
-
--export([collect/2]).
-
--define(LONG_TIMEOUT, 30000).
--define(VALUES(Rec), tl(tuple_to_list(Rec))).
-
-%%% ----------------------------------------------------------
-%%% # usage(String)
-%%% ----------------------------------------------------------
-
-usage(Usage) ->
- sep($+),
- io:format("+ ~p~n", [?MODULE]),
- io:format("~n~s~n~n", [compact(Usage)]),
- sep($+).
-
-%%%
-%%% The function format/3, for pretty-printing tables, comes in
-%%% several flavours.
-%%%
-
-%%% ----------------------------------------------------------
-%%% # format(TableName, Fields, SplitFun)
-%%%
-%%% Input: TableName = atom() name of table.
-%%%
-%%% Fields = List of field names for the records maintained
-%%% in the specified table. Can be empty, in which
-%%% case entries are listed unadorned of field names
-%%% and SplitFun is unused.
-%%% | Integer, equivalent to a list with this many '' atoms.
-%%% | Arity 1 fun mapping a table entry to a Fields list
-%%% or a tuple {Fields, Values} of lists of the same
-%%% length.
-%%%
-%%% If Fields is a list then its length must be the same
-%%% as or one less than the size of the tuples contained
-%%% in the table. (The values printed then being those
-%%% in the tuple or record in question.)
-%%%
-%%% SplitFun = Arity 3 fun applied as
-%%%
-%%% SplitFun(TableName, Fields, Values)
-%%%
-%%% in order to obtain a tuple
-%%%
-%%% {Field, RestFields, Value, RestValues}
-%%%
-%%% for which Field/Value will be formatted on
-%%% STDOUT. (This is to allow a value to be
-%%% transformed before being output by returning a
-%%% new value and/or replacing the remainder of
-%%% the list.) The returned lists must have the
-%%% same length and Field here is an atom, '' causing
-%%% a value to be listed unadorned of the field name.
-%%%
-%%% Field can also be list of field names, in
-%%% which case Value must be a record of the
-%%% corresponding type.
-%%%
-%%% | Arity 2 fun applied as SplitFun(Fields, Values).
-%%%
-%%% Output: Count | undefined
-%%%
-%%% Count = Number of entries output.
-%%%
-%%% Description: Pretty-print records in a named table.
-%%% ----------------------------------------------------------
-
-format(Table, Fields, SFun)
- when is_atom(Table), is_function(SFun, 2) ->
- ft(ets:info(Table), Table, SFun, Fields);
-
-format(Table, Fields, SFun)
- when is_atom(Table), is_function(SFun, 3) ->
- format(Table, Fields, fun(Fs,Vs) -> SFun(Table, Fs, Vs) end);
-
-%%% ----------------------------------------------------------
-%%% # format(Recs, Fields, SplitFun)
-%%%
-%%% Input: Recs = list of records/tuples
-%%% Fields = As for format(Table, Fields, SplitFun), a table
-%%% entry there being a member of Recs.
-%%% SplitFun = Arity 3 fun applied as above but with the TableName
-%%% replaced by the first element of the records in
-%%% question.
-%%% | Arity 2 fun as for format/3.
-%%%
-%%% Output: length(Recs)
-%%%
-%%% Description: Pretty print records/tuples.
-%%% ----------------------------------------------------------
-
-format(Recs, Fields, SFun)
- when is_list(Recs), is_function(SFun, 3) ->
- lists:foldl(fun(R,A) -> f(recsplit(SFun, R), 0, Fields, R, A) end,
- 0,
- Recs);
-
-format(Recs, Fields, SFun)
- when is_list(Recs), is_function(SFun, 2) ->
- lists:foldl(fun(R,A) -> f(SFun, 0, Fields, R, A) end,
- 0,
- Recs);
-
-%%% ----------------------------------------------------------
-%%% # format(Tables, SplitFun, CollectFun)
-%%%
-%%% Input: Tables = list of {TableName, Fields}.
-%%% SplitFun = As for format(Table, Fields, SplitFun).
-%%% CollectFun = arity 1 fun mapping a table name to a list
-%%% of elements. A non-list can be returned to indicate
-%%% that the table in question doesn't exist.
-%%%
-%%% Output: Number of entries output.
-%%%
-%%% Description: Pretty-print records in a named tables as collected
-%%% from known nodes. Each table listing is preceeded by
-%%% a banner.
-%%% ----------------------------------------------------------
-
-format(Tables, SFun, CFun)
- when is_list(Tables), is_function(CFun, 1) ->
- format_remote(Tables,
- SFun,
- rpc:multicall(nodes(known),
- ?MODULE,
- collect,
- [CFun, lists:map(fun({T,_}) -> T end, Tables)],
- ?LONG_TIMEOUT));
-
-%%% ----------------------------------------------------------
-%%% # format(LocalTables, RemoteTables, SplitFun, CollectFun)
-%%% # format(LocalTables, RemoteTables, SplitFun)
-%%%
-%%% Input: LocalTables = list of {TableName, Fields}.
-%%% | list of {TableName, Recs, Fields}
-%%% RemoteTable = list of {TableName, Fields}.
-%%% SplitFun, CollectFun = As for format(Table, CollectFun, SplitFun).
-%%%
-%%% Output: Number of entries output.
-%%%
-%%% Description: Pretty-print records in a named tables as collected
-%%% from local and remote nodes. Each table listing is
-%%% preceeded by a banner.
-%%% ----------------------------------------------------------
-
-format(Local, Remote, SFun) ->
- format(Local, Remote, SFun, fun tab2list/1).
-
-format(Local, Remote, SFun, CFun)
- when is_list(Local), is_list(Remote), is_function(CFun, 1) ->
- format_local(Local, SFun) + format(Remote, SFun, CFun).
-
-%%% ----------------------------------------------------------
-%%% # format(Tables, SplitFun)
-%%% ----------------------------------------------------------
-
-format(Tables, SFun)
- when is_list(Tables), (is_function(SFun, 2) or is_function(SFun, 3)) ->
- format(Tables, SFun, fun tab2list/1);
-
-format(Tables, CFun)
- when is_list(Tables), is_function(CFun, 1) ->
- format(Tables, fun split/2, CFun).
-
-%%% ----------------------------------------------------------
-%%% # format(Table|Tables)
-%%% ----------------------------------------------------------
-
-format(Table)
- when is_atom(Table) ->
- format(Table, [], fun split/2);
-
-format(Tables)
- when is_list(Tables) ->
- format(Tables, fun split/2, fun tab2list/1).
-
-%%% ----------------------------------------------------------
-%%% # split(TableName, Fields, Values)
-%%%
-%%% Description: format/3 SplitFun that does nothing special.
-%%% ----------------------------------------------------------
-
-split([F|FT], [V|VT]) ->
- {F, FT, V, VT}.
-
-split(_, Fs, Vs) ->
- split(Fs, Vs).
-
-%%% ----------------------------------------------------------
-%%% # tab2list(TableName)
-%%%
-%%% Description: format/4 CollectFun that extracts records from an
-%%% existing ets table.
-%%% ----------------------------------------------------------
-
-tab2list(Table) ->
- case ets:info(Table) of
- undefined = No ->
- No;
- _ ->
- ets:tab2list(Table)
- end.
-
-list(Table) ->
- l(tab2list(Table)).
-
-l(undefined = No) ->
- No;
-l(List)
- when is_list(List) ->
- io:format("~p~n", [List]),
- length(List).
-
-%%% ----------------------------------------------------------
-%%% # table(TableName, Fields)
-%%% ----------------------------------------------------------
-
-table(Table, Fields) ->
- format(Table, Fields, fun split/2).
-
-%%% ----------------------------------------------------------
-%%% # tables(LocalTables, RemoteTables)
-%%% ----------------------------------------------------------
-
-tables(Local, Remote) ->
- format(Local, Remote, fun split/2).
-
-%%% ----------------------------------------------------------
-%%% # tables(Tables)
-%%% ----------------------------------------------------------
-
-tables(Tables) ->
- format(Tables, fun split/2).
-
-%%% ----------------------------------------------------------
-%%% # modules(Prefix|Prefixes)
-%%%
-%%% Input: Prefix = atom()
-%%%
-%%% Description: Return the list of all loaded modules with the
-%%% specified prefix.
-%%% ----------------------------------------------------------
-
-modules(Prefix)
- when is_atom(Prefix) ->
- lists:sort(mods(Prefix));
-
-modules(Prefixes)
- when is_list(Prefixes) ->
- lists:sort(lists:flatmap(fun modules/1, Prefixes)).
-
-mods(Prefix) ->
- P = atom_to_list(Prefix),
- lists:filter(fun(M) ->
- lists:prefix(P, atom_to_list(M))
- end,
- erlang:loaded()).
-
-%%% ----------------------------------------------------------
-%%% # versions(Modules|Prefix)
-%%%
-%%% Output: Number of modules listed.
-%%%
-%%% Description: List the versions of the specified modules.
-%%% ----------------------------------------------------------
-
-versions(Modules) ->
- {SysInfo, OsInfo, ModInfo} = version_info(Modules),
- sep(),
- print_sys_info(SysInfo),
- sep(),
- print_os_info(OsInfo),
- sep(),
- print_mod_info(ModInfo),
- sep().
-
-%%% ----------------------------------------------------------
-%%% # attrs(Modules|Prefix, Attr|FormatFun)
-%%%
-%%% Output: Number of modules listed.
-%%%
-%%% Description: List an attribute from module_info.
-%%% ----------------------------------------------------------
-
-attrs(Modules, Attr)
- when is_atom(Attr) ->
- attrs(Modules, fun(W,M) -> attr(W, M, Attr, fun attr/1) end);
-
-attrs(Modules, Fun)
- when is_list(Modules) ->
- sep(),
- W = 2 + widest(Modules),
- N = lists:foldl(fun(M,A) -> Fun(W,M), A+1 end, 0, Modules),
- sep(),
- N;
-
-attrs(Prefix, Fun) ->
- attrs(modules(Prefix), Fun).
-
-%% attr/1
-
-attr(T) when is_atom(T) ->
- atom_to_list(T);
-attr(N) when is_integer(N) ->
- integer_to_list(N);
-attr(V) ->
- case is_list(V) andalso lists:all(fun is_char/1, V) of
- true -> %% string
- V;
- false ->
- io_lib:format("~p", [V])
- end.
-
-is_char(C) ->
- 0 =< C andalso C < 256.
-
-%% attr/4
-
-attr(Width, Mod, Attr, VFun) ->
- io:format(": ~*s~s~n", [-Width, Mod, attr(Mod, Attr, VFun)]).
-
-attr(Mod, Attr, VFun) ->
- Key = key(Attr),
- try
- VFun(val(Attr, keyfetch(Attr, Mod:module_info(Key))))
- catch
- _:_ ->
- "-"
- end.
-
-attr(Mod, Attr) ->
- attr(Mod, Attr, fun attr/1).
-
-key(time) -> compile;
-key(_) -> attributes.
-
-val(time, {_,_,_,_,_,_} = T) ->
- lists:flatten(io_lib:format("~p-~2..0B-~2..0B ~2..0B:~2..0B:~2..0B",
- tuple_to_list(T)));
-val(_, [V]) ->
- V.
-
-%%% ----------------------------------------------------------
-%%% # compiled(Modules|Prefix)
-%%%
-%%% Output: Number of modules listed.
-%%%
-%%% Description: List the compile times of the specified modules.
-%%% ----------------------------------------------------------
-
-compiled(Modules)
- when is_list(Modules) ->
- attrs(Modules, fun compiled/2);
-
-compiled(Prefix) ->
- compiled(modules(Prefix)).
-
-compiled(Width, Mod) ->
- io:format(": ~*s~19s ~s~n", [-Width,
- Mod,
- attr(Mod, time),
- opt(attr(Mod, date))]).
-
-opt("-") ->
- "";
-opt(D) ->
- "(" ++ D ++ ")".
-
-%%% ----------------------------------------------------------
-%%% # procs(Pred|Prefix|Prefixes|Pid|Pids)
-%%%
-%%% Input: Pred = arity 2 fun returning true|false when applied to a
-%%% pid and its process info.
-%%%
-%%% Output: Number of processes listed.
-%%%
-%%% Description: List process info for all local processes that test
-%%% true with the specified predicate. With the prefix
-%%% form, those processes that are either currently
-%%% executing in, started executing in, or have a
-%%% registered name with a specified prefix are listed.
-%%% With the pid forms, only those process that are local
-%%% are listed and those that are dead list only the pid
-%%% itself.
-%%% ----------------------------------------------------------
-
-procs(Pred)
- when is_function(Pred, 2) ->
- procs(Pred, erlang:processes());
-
-procs([]) ->
- 0;
-
-procs(Prefix)
- when is_atom(Prefix) ->
- procs(fun(_,I) -> info(fun pre1/2, I, atom_to_list(Prefix)) end);
-
-procs(Prefixes)
- when is_atom(hd(Prefixes)) ->
- procs(fun(_,I) -> info(fun pre/2, I, Prefixes) end);
-
-procs(Pid)
- when is_pid(Pid) ->
- procs(fun true2/2, [Pid]);
-
-procs(Pids)
- when is_list(Pids) ->
- procs(fun true2/2, Pids).
-
-true2(_,_) ->
- true.
-
-%% procs/2
-
-procs(Pred, Pids) ->
- Procs = lists:foldl(fun(P,A) ->
- procs_acc(Pred, P, catch process_info(P), A)
- end,
- [],
- Pids),
- sep(0 < length(Procs)),
- lists:foldl(fun(T,N) -> p(T), sep(), N+1 end, 0, Procs).
-
-procs_acc(_, Pid, undefined, Acc) -> %% dead
- [[{pid, Pid}] | Acc];
-procs_acc(Pred, Pid, Info, Acc)
- when is_list(Info) ->
- p_acc(Pred(Pid, Info), Pid, Info, Acc);
-procs_acc(_, _, _, Acc) ->
- Acc.
-
-p_acc(true, Pid, Info, Acc) ->
- [[{pid, Pid} | Info] | Acc];
-p_acc(false, _, _, Acc) ->
- Acc.
-
-%% info/3
-
-info(Pred, Info, T) ->
- lists:any(fun(I) -> i(Pred, I, T) end, Info).
-
-i(Pred, {K, {M,_,_}}, T)
- when K == current_function;
- K == initial_call ->
- Pred(M,T);
-i(Pred, {registered_name, N}, T) ->
- Pred(N,T);
-i(_,_,_) ->
- false.
-
-pre1(A, Pre) ->
- lists:prefix(Pre, atom_to_list(A)).
-
-pre(A, Prefixes) ->
- lists:any(fun(P) -> pre1(A, atom_to_list(P)) end, Prefixes).
-
-%%% ----------------------------------------------------------
-%%% # latest(Modules|Prefix)
-%%%
-%%% Output: {Mod, {Y,M,D,HH,MM,SS}, Version}
-%%%
-%%% Description: Return the compile time of the most recently compiled
-%%% module from the specified non-empty list. The modules
-%%% are assumed to exist.
-%%% ----------------------------------------------------------
-
-latest(Prefix)
- when is_atom(Prefix) ->
- latest(modules(Prefix));
-
-latest([_|_] = Modules) ->
- {Mod, T}
- = hd(lists:sort(fun latest/2, lists:map(fun compile_time/1, Modules))),
- {Mod, T, app_vsn(Mod)}.
-
-app_vsn(Mod) ->
- keyfetch(app_vsn, Mod:module_info(attributes)).
-
-compile_time(Mod) ->
- T = keyfetch(time, Mod:module_info(compile)),
- {Mod, T}.
-
-latest({_,T1},{_,T2}) ->
- T1 > T2.
-
-%%% ----------------------------------------------------------
-%%% version_info(Modules|Prefix)
-%%%
-%%% Output: {SysInfo, OSInfo, [ModInfo]}
-%%%
-%%% SysInfo = {Arch, Vers}
-%%% OSInfo = {Vers, {Fam, Name}}
-%%% ModInfo = {Vsn, AppVsn, Time, CompilerVsn}
-%%% ----------------------------------------------------------
-
-version_info(Prefix)
- when is_atom(Prefix) ->
- version_info(modules(Prefix));
-
-version_info(Mods)
- when is_list(Mods) ->
- {sys_info(), os_info(), [{M, mod_version_info(M)} || M <- Mods]}.
-
-mod_version_info(Mod) ->
- try
- Info = Mod:module_info(),
- [[Vsn], AppVsn] = get_values(attributes, [vsn, app_vsn], Info),
- [Ver, Time] = get_values(compile, [version, time], Info),
- [Vsn, AppVsn, Ver, Time]
- catch
- _:_ ->
- []
- end.
-
-get_values(Attr, Keys, Info) ->
- As = proplists:get_value(Attr, Info),
- [proplists:get_value(K, As, "?") || K <- Keys].
-
-sys_info() ->
- [A,V] = [chomp(erlang:system_info(K)) || K <- [system_architecture,
- system_version]],
- {A,V}.
-
-os_info() ->
- {os:version(), os:type()}.
-
-chomp(S) ->
- string:strip(S, right, $\n).
-
-print_sys_info({Arch, Ver}) ->
- io:format("System info:~n"
- " architecture : ~s~n"
- " version : ~s~n",
- [Arch, Ver]).
-
-print_os_info({Vsn, {Fam, Name}}) ->
- io:format("OS info:~n"
- " family : ~s ~s~n"
- " version : ~s~n",
- [str(Fam), bkt(str(Name)), vsn(Vsn)]).
-
-print_mod_info(Mods) ->
- io:format("Module info:~n", []),
- lists:foreach(fun print_mod/1, Mods).
-
-print_mod({Mod, []}) ->
- io:format(" ~w:~n", [Mod]);
-print_mod({Mod, [Vsn, AppVsn, Ver, {Year, Month, Day, Hour, Min, Sec}]}) ->
- Time = io_lib:format("~w-~2..0w-~2..0w ~2..0w:~2..0w:~2..0w",
- [Year, Month, Day, Hour, Min, Sec]),
- io:format(" ~w:~n"
- " vsn : ~s~n"
- " app_vsn : ~s~n"
- " compiled : ~s~n"
- " compiler : ~s~n",
- [Mod, str(Vsn), str(AppVsn), Time, Ver]).
-
-str(A)
- when is_atom(A) ->
- atom_to_list(A);
-str(S)
- when is_list(S) ->
- S;
-str(T) ->
- io_lib:format("~p", [T]).
-
-bkt("" = S) ->
- S;
-bkt(S) ->
- [$[, S, $]].
-
-vsn(T) when is_tuple(T) ->
- case [[$., integer_to_list(N)] || N <- tuple_to_list(T)] of
- [[$.,S] | Rest] ->
- [S | Rest];
- [] = S ->
- S
- end;
-vsn(T) ->
- str(T).
-
-%%% ----------------------------------------------------------
-%%% ----------------------------------------------------------
-
-%% p/1
-
-p(Info) ->
- W = 2 + widest([K || {K,_} <- Info]),
- lists:foreach(fun({K,V}) -> p(W,K,V) end, Info).
-
-p(Width, Key, Value) ->
- io:format(": ~*s: ~p~n", [-Width, Key, Value]).
-
-%% sep/[01]
-
-sep() ->
- sep($#).
-
-sep(true) ->
- sep();
-sep(false) ->
- ok;
-
-sep(Ch) ->
- io:format("~c~65c~n", [Ch, $-]).
-
-%% widest/1
-
-widest(List) ->
- lists:foldl(fun widest/2, 0, List).
-
-widest(T, Max)
- when is_atom(T) ->
- widest(atom_to_list(T), Max);
-
-widest(T, Max)
- when is_integer(T) ->
- widest(integer_to_list(T), Max);
-
-widest(T, Max)
- when is_list(T) -> %% string
- max(length(T), Max).
-
-pt(T) ->
- io:format(": ~p~n", [T]).
-
-recsplit(SFun, Rec) ->
- fun(Fs,Vs) -> SFun(element(1, Rec), Fs, Vs) end.
-
-max(A, B) ->
- if A > B -> A; true -> B end.
-
-keyfetch(Key, List) ->
- {Key,V} = lists:keyfind(Key, 1, List),
- V.
-
-%% ft/4
-
-ft(undefined = No, _, _, _) ->
- No;
-
-ft(_, Table, SFun, Fields) ->
- ets:foldl(fun(R,A) ->
- f(SFun, 0, Fields, R, A)
- end,
- 0,
- Table).
-
-%% f/5
-
-f(SFun, Width, Fields, Rec, Count) ->
- ff(SFun, Width, fields(Fields, Rec), Rec, Count).
-
-ff(SFun, Width, Fields, Rec, Count) ->
- sep(0 == Count),
- f(SFun, Width, Fields, Rec),
- sep(),
- Count+1.
-
-fields(N, _)
- when is_integer(N), N >= 0 ->
- lists:duplicate(N, ''); %% list values unadorned
-fields(Fields, R)
- when is_function(Fields, 1) ->
- fields(Fields(R), R);
-fields({Fields, Values} = T, _)
- when length(Fields) == length(Values) ->
- T;
-fields(Fields, _)
- when is_list(Fields) ->
- Fields. %% list field/value pairs, or tuples if []
-
-%% f/4
-
-%% Empty fields list: just print the entry.
-f(_, _, [], Rec)
- when is_tuple(Rec) ->
- pt(Rec);
-
-%% Otherwise list field names/values.
-f(SFun, Width, {Fields, Values}, _) ->
- f(SFun, Width, Fields, Values);
-
-f(SFun, Width, Fields, Rec)
- when is_tuple(Rec) ->
- f(SFun, Width, Fields, values(Fields, Rec));
-
-f(_, _, [], []) ->
- ok;
-
-f(SFun, Width, [HF | _] = Fields, Values) ->
- {F, FT, V, VT} = SFun(Fields, Values),
- if is_list(F) -> %% V is a record
- break($>, HF),
- f(SFun, Width, F, values(F,V)),
- break($<, HF),
- f(SFun, Width, FT, VT);
- F == '' -> %% no field name: just list value
- pt(V),
- f(SFun, Width, FT, VT);
- true -> %% list field/value.
- W = max(Width, 1 + widest(Fields)),
- p(W, F, V),
- f(SFun, W, FT, VT)
- end.
-
-values(Fields, Rec)
- when length(Fields) == size(Rec) - 1 ->
- ?VALUES(Rec);
-values(Fields, T)
- when length(Fields) == size(T) ->
- tuple_to_list(T).
-
-%% format_local/2
-
-format_local(Tables, SFun) ->
- lists:foldl(fun(T,A) -> fl(SFun, T, A) end, 0, Tables).
-
-fl(SFun, {Table, Recs, Fields}, Count) ->
- sep(),
- io:format("# ~p~n", [Table]),
- N = fmt(Recs, Fields, SFun),
- sep(0 == N),
- Count + N;
-
-fl(SFun, {Table, Fields}, Count) ->
- fl(SFun, {Table, Table, Fields}, Count).
-
-%% fmt/3
-
-fmt(T, Fields, SFun) ->
- case format(T, Fields, SFun) of
- undefined ->
- 0;
- N ->
- N
- end.
-
-%% break/2
-
-break(C, T) ->
- io:format("~c ~p~n", [C, T]).
-
-%% collect/2
-%%
-%% Output: {[{TableName, Recs}, ...], node()}
-
-collect(CFun, TableNames) ->
- {lists:foldl(fun(N,A) -> c(CFun, N, A) end, [], TableNames), node()}.
-
-c(CFun, TableName, Acc) ->
- case CFun(TableName) of
- Recs when is_list(Recs) ->
- [{TableName, Recs} | Acc];
- _ ->
- Acc
- end.
-
-%% format_remote/3
-
-format_remote(Tables, SFun, {Replies, BadNodes}) ->
- N = lists:foldl(fun(T,A) -> fr(Tables, SFun, T, A) end,
- 0,
- Replies),
- sep(0 == N andalso [] /= BadNodes),
- lists:foreach(fun(Node) -> io:format("# no reply from ~p~n", [Node]) end,
- BadNodes),
- sep([] /= BadNodes),
- N.
-
-fr(Tables, SFun, {List, Node}, Count)
- when is_list(List) -> %% guard against {badrpc, Reason}
- lists:foldl(fun({T,Recs}, C) -> fr(Tables, SFun, Node, T, Recs,C) end,
- Count,
- List);
-fr(_, _, _, Count) ->
- Count.
-
-fr(Tables, SFun, Node, Table, Recs, Count) ->
- Fields = keyfetch(Table, Tables),
- sep(),
- io:format("# ~p@~p~n", [Table, Node]),
- N = format(Recs, Fields, tblsplit(SFun, Table)),
- sep(0 == N),
- Count + N.
-
-tblsplit(SFun, Table)
- when is_function(SFun, 3) ->
- fun(Fs,Vs) -> SFun(Table, Fs, Vs) end;
-tblsplit(SFun, _)
- when is_function(SFun, 2) ->
- SFun.
-
-%% compact/1
-%%
-%% Strip whitespace from both ends of a string.
-
-compact(Str) ->
- compact(Str, true).
-
-compact([Ch|Rest], B)
- when Ch == $\n;
- Ch == $ ;
- Ch == $\t;
- Ch == $\v;
- Ch == $\r ->
- compact(Rest, B);
-
-compact(Str, false) ->
- Str;
-
-compact(Str, true) ->
- lists:reverse(compact(lists:reverse(Str), false)).