%%%-------------------------------------------------------------------
%%% @author Torbjorn Tornkvist <ttornkvi@cisco.com>
%%% @copyright (C) 2022, Torbjorn Tornkvist
%%% Good to have utilities when debugging code.
%%%
%%% Created : 13 Dec 2022 by Torbjorn Tornkvist <ttornkvi@cisco.com>
%%%-------------------------------------------------------------------
-module(edbg_utils).
-export([all_sup_trees/0
, start/0
, supervisors/0
, supervisors/1
]).
-define(SERVER, ?MODULE).
-ifdef(USE_COLORS).
-define(info_msg(Fmt,Args), edbg_color_srv:info_msg(Fmt,Args)).
-define(att_msg(Fmt,Args), edbg_color_srv:att_msg(Fmt,Args)).
-define(warn_msg(Fmt,Args), edbg_color_srv:warn_msg(Fmt,Args)).
-define(err_msg(Fmt,Args), edbg_color_srv:err_msg(Fmt,Args)).
-define(cur_line_msg(Fmt,Args), edbg_color_srv:cur_line_msg(Fmt,Args)).
-define(c_hi(Str), edbg_color_srv:c_hi(Str)).
-define(c_warn(Str), edbg_color_srv:c_warn(Str)).
-define(c_err(Str), edbg_color_srv:c_err(Str)).
-define(help_hi(Str), edbg_color_srv:help_hi(Str)).
-define(edbg_color_srv_init(), edbg_color_srv:init()).
-else.
-define(info_msg(Fmt,Args), io:format(Fmt,Args)).
-define(att_msg(Fmt,Args), io:format(Fmt,Args)).
-define(warn_msg(Fmt,Args), io:format(Fmt,Args)).
-define(err_msg(Fmt,Args), io:format(Fmt,Args)).
-define(cur_line_msg(Fmt,Args), io:format(Fmt,Args)).
-define(c_hi(Str), Str).
-define(c_warn(Str), Str).
-define(c_err(Str), Str).
-define(help_hi(Str), Str).
-define(edbg_color_srv_init(), ok).
-endif.
-record(state,
{
expand_levels = [],
sup_trees = []
}).
-record(sup_tree,
{id,
pid,
children = []
}).
%% @private
start() ->
Self = self(),
Prompt = spawn_link(fun() -> prompt(Self) end),
Prompt ! show,
ploop(Prompt).
ploop(Prompt) ->
receive
{'EXIT', Prompt, _} ->
true;
quit ->
true;
_ ->
?MODULE:ploop(Prompt)
end.
prompt(Pid) when is_pid(Pid) ->
process_flag(trap_exit, true),
State = #state{sup_trees = all_sup_trees()},
Prompt = "suptrees> ",
print_help(),
loop(Pid, Prompt, State).
loop(Pid, Prompt, State0) ->
State =
case string:tokens(io:get_line(Prompt), "\n") of
["h"++X] -> help(X, State0);
["s"++X] -> show(X, State0);
["x"++X] -> expand(X, State0);
["q"++_] -> Pid ! quit, exit(normal);
_X ->
?info_msg("prompt got: ~p~n",[_X]),
State0
end,
?MODULE:loop(Pid, Prompt, State).
show(Chars, State) ->
show(Chars, State, 1).
show(Chars, [#sup_tree{id = Id, pid = Pid}|Tail], N) ->
io:format("~p:s ~p ~p~n", [N, Id, Pid]),
show(Chars, Tail, N+1);
show(Chars, [{Name,Pid,worker,_Mods}|Tail], N) ->
io:format("~p:w ~p ~p~n", [N, Name, Pid]),
show(Chars, Tail, N+1);
show(_, [], _) ->
ok.
help(_,_) ->
tbd.
expand(_,_) ->
tbd.
print_help() ->
S1 = " (h)elp e(x)pand [<N>] (s)how (q)uit",
S = io_lib:format("~n~s~n",[S1]),
?info_msg(?help_hi(S), []).
%% @private
supervisors() ->
supervisors(kernel_sup).
%% @private
supervisors(Supervisor) when is_atom(Supervisor) ->
supervisors(Supervisor, new_sup_tree(Supervisor)).
%% supervisors(user = Supervisor, SupTree) -> % eternal loop?
%% SupTree;
%% supervisors(standard_error = Supervisor, SupTree) -> % eternal loop?
%% SupTree;
supervisors(Supervisor, SupTree) ->
io:format(">>> Supervisor=~p~n",[Supervisor]),
case supervisor:which_children(SupTree#sup_tree.pid) of
Children when is_list(Children) ->
lists:foldl(fun({Id, Pid, Type, _Modules}, AccTree)
when (Type == supervisor) andalso is_pid(Pid) ->
Cs = AccTree#sup_tree.children,
AccTree#sup_tree{children =
Cs ++
[supervisors(Id, new_sup_tree(Id, Pid))]};
(Entry, AccTree) ->
Cs = AccTree#sup_tree.children,
AccTree#sup_tree{children = Cs ++ [Entry]}
end, SupTree, Children);
_ ->
SupTree
end.
%% @private
all_sup_trees() ->
Sups = lists:foldr(
fun(Id,Acc) ->
case process_info(whereis(Id), dictionary) of
{dictionary,L} ->
case lists:keyfind('$initial_call', 1, L) of
{'$initial_call',{supervisor,_,_}} ->
[Id|Acc];
_ ->
Acc
end;
_ ->
Acc
end
end, [], erlang:registered()),
[supervisors(Id) || Id <- Sups].
%% SupTree#sup_tree{children =
%% [supervisors(Id, new_sup_tree(Id, Pid)) ||
%% {Id, Pid, Type, _Modules}
%% <- supervisor:which_children(SupTree#sup_tree.pid),
%% Type == supervisor,
%% is_pid(Pid) == true]}.
new_sup_tree(SupervisorId) ->
new_sup_tree(SupervisorId, erlang:whereis(SupervisorId)).
new_sup_tree(SupervisorId, Pid) ->
#sup_tree{id = SupervisorId,
pid = Pid}.