src/edbg_utils.erl

%%%-------------------------------------------------------------------
%%% @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}.