1%%% @author zhongwen <zhongwencool@gmail.com>
2-module(observer_cli_mnesia).
3
4%% API
5-export([start/1]).
6-export([clean/1]).
7
8-include("observer_cli.hrl").
9
10-define(LAST_LINE,
11    "q(quit) s(sort by size) m(sort by memory) pd/pu(page:down/up) F/B(forward/back)"
12    " hide(swith between hide and display system table)"
13).
14
15-spec start(#view_opts{}) -> any().
16start(
17    #view_opts{
18        db = #db{
19            interval = MillSecond,
20            hide_sys = HideSys,
21            cur_page = CurPage,
22            attr = Attr
23        },
24        auto_row = AutoRow
25    } = HomeOpts
26) ->
27    Pid = spawn_link(fun() ->
28        ?output(?CLEAR),
29        render_worker(MillSecond, ?INIT_TIME_REF, HideSys, AutoRow, Attr, CurPage)
30    end),
31    manager(Pid, HomeOpts).
32
33-spec clean(list()) -> ok.
34clean(Pids) -> observer_cli_lib:exit_processes(Pids).
35
36%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
37%%% Private
38%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
39
40manager(ChildPid, #view_opts{db = DBOpts = #db{cur_page = CurPage, hide_sys = Hide}} = HomeOpts) ->
41    case observer_cli_lib:parse_cmd(HomeOpts, ?MODULE, [ChildPid]) of
42        quit ->
43            erlang:send(ChildPid, quit);
44        {new_interval, NewMs} = Msg ->
45            erlang:send(ChildPid, Msg),
46            manager(ChildPid, HomeOpts#view_opts{db = DBOpts#db{interval = NewMs}});
47        hide ->
48            NewHide = not Hide,
49            erlang:send(ChildPid, {system_table, NewHide}),
50            manager(ChildPid, HomeOpts#view_opts{db = DBOpts#db{hide_sys = NewHide}});
51        size ->
52            clean([ChildPid]),
53            start(HomeOpts#view_opts{db = DBOpts#db{attr = size}});
54        %% Home
55        {func, proc_count, memory} ->
56            clean([ChildPid]),
57            start(HomeOpts#view_opts{db = DBOpts#db{attr = memory}});
58        page_down_top_n ->
59            NewPage = max(CurPage + 1, 1),
60            clean([ChildPid]),
61            start(HomeOpts#view_opts{db = DBOpts#db{cur_page = NewPage}});
62        page_up_top_n ->
63            NewPage = max(CurPage - 1, 1),
64            clean([ChildPid]),
65            start(HomeOpts#view_opts{db = DBOpts#db{cur_page = NewPage}});
66        _ ->
67            manager(ChildPid, HomeOpts)
68    end.
69
70render_worker(Interval, LastTimeRef, HideSystemTable, AutoRow, Attr, CurPage) ->
71    TerminalRow = observer_cli_lib:get_terminal_rows(AutoRow),
72    Rows = erlang:max(TerminalRow - 5, 0),
73    Text =
74        "Interval: " ++
75            integer_to_list(Interval) ++
76            "ms" ++
77            " HideSystemTable:" ++ atom_to_list(HideSystemTable),
78    Menu = observer_cli_lib:render_menu(mnesia, Text),
79    LastLine = observer_cli_lib:render_last_line(?LAST_LINE),
80    case get_table_list(HideSystemTable, Attr) of
81        {error, Reason} ->
82            ErrInfo = io_lib:format("Mnesia Error   ~p~n", [Reason]),
83            ?output([?CURSOR_TOP, Menu, ErrInfo, LastLine]);
84        MnesiaList ->
85            Info = render_mnesia(MnesiaList, Attr, Rows, CurPage),
86            ?output([?CURSOR_TOP, Menu, Info, LastLine])
87    end,
88    TimeRef = observer_cli_lib:next_redraw(LastTimeRef, Interval),
89    receive
90        quit ->
91            quit;
92        {new_interval, NewInterval} ->
93            render_worker(NewInterval, TimeRef, HideSystemTable, AutoRow, Attr, CurPage);
94        {system_table, NewHideSystemTable} ->
95            render_worker(Interval, TimeRef, NewHideSystemTable, AutoRow, Attr, CurPage);
96        _ ->
97            render_worker(Interval, TimeRef, HideSystemTable, AutoRow, Attr, CurPage)
98    end.
99
100render_mnesia(MnesiaList, Attr, Rows, CurPage) ->
101    {_StartPos, SortMnesia} = observer_cli_lib:sublist(MnesiaList, Rows, CurPage),
102    {MemColor, SizeColor} =
103        case Attr of
104            memory -> {?RED_BG, ?GRAY_BG};
105            _ -> {?GRAY_BG, ?RED_BG}
106        end,
107    Title = ?render([
108        ?UNDERLINE,
109        ?W2(?GRAY_BG, "Name", 25),
110        ?UNDERLINE,
111        ?W2(MemColor, "    Memory    ", 16),
112        ?UNDERLINE,
113        ?W2(SizeColor, "Size", 16),
114        ?UNDERLINE,
115        ?W2(?GRAY_BG, "Type", 12),
116        ?UNDERLINE,
117        ?W2(?GRAY_BG, "Storage", 15),
118        ?UNDERLINE,
119        ?W2(?GRAY_BG, "Owner", 14),
120        ?UNDERLINE,
121        ?W2(?GRAY_BG, "Index", 11),
122        ?UNDERLINE,
123        ?W2(?GRAY_BG, "Reg_name", 20)
124    ]),
125    View = [
126        begin
127            Name = proplists:get_value(name, Mnesia),
128            Memory = proplists:get_value(memory, Mnesia),
129            Size = proplists:get_value(size, Mnesia),
130            Type = proplists:get_value(type, Mnesia),
131            RegName = proplists:get_value(reg_name, Mnesia),
132            Index = proplists:get_value(index, Mnesia),
133            Owner = proplists:get_value(owner, Mnesia),
134            Storage = proplists:get_value(storage, Mnesia),
135            ?render([
136                ?W(Name, 24),
137                ?W({byte, Memory}, 14),
138                ?W(Size, 14),
139                ?W(Type, 10),
140                ?W(Storage, 13),
141                ?W(Owner, 12),
142                ?W(Index, 9),
143                ?W(RegName, 19)
144            ])
145        end
146        || {_, _, Mnesia} <- SortMnesia
147    ],
148    [Title | View].
149
150mnesia_tables() ->
151    [
152        ir_AliasDef,
153        ir_ArrayDef,
154        ir_AttributeDef,
155        ir_ConstantDef,
156        ir_Contained,
157        ir_Container,
158        ir_EnumDef,
159        ir_ExceptionDef,
160        ir_IDLType,
161        ir_IRObject,
162        ir_InterfaceDef,
163        ir_ModuleDef,
164        ir_ORB,
165        ir_OperationDef,
166        ir_PrimitiveDef,
167        ir_Repository,
168        ir_SequenceDef,
169        ir_StringDef,
170        ir_StructDef,
171        ir_TypedefDef,
172        ir_UnionDef,
173        logTable,
174        logTransferTable,
175        mesh_meas,
176        mesh_type,
177        mnesia_clist,
178        orber_CosNaming,
179        orber_objkeys,
180        user
181    ].
182
183get_table_list(HideSys, Attr) ->
184    Owner = ets:info(schema, owner),
185    case Owner of
186        undefined -> {error, "Mnesia is not running on: " ++ atom_to_list(node())};
187        _ -> get_table_list2(Owner, HideSys, Attr)
188    end.
189
190get_table_list2(Owner, HideSys, Attr) ->
191    {registered_name, RegName} = process_info(Owner, registered_name),
192    WordSize = erlang:system_info(wordsize),
193    CollectFun = fun(Id, Acc) ->
194        case HideSys andalso ordsets:is_element(Id, mnesia_tables()) orelse Id =:= schema of
195            %% ignore system table
196            true ->
197                Acc;
198            false ->
199                Storage = mnesia:table_info(Id, storage_type),
200                Size = mnesia:table_info(Id, size),
201                Memory = mnesia:table_info(Id, memory) * WordSize,
202                Tab0 = [
203                    {name, Id},
204                    {owner, Owner},
205                    {size, Size},
206                    {reg_name, RegName},
207                    {type, mnesia:table_info(Id, type)},
208                    {memory, Memory},
209                    {storage, Storage},
210                    {index, mnesia:table_info(Id, index)}
211                ],
212                Tab =
213                    case Storage of
214                        _ when Storage =:= ram_copies orelse Storage =:= disc_copies ->
215                            [
216                                {fixed, ets:info(Id, fixed)},
217                                {compressed, ets:info(Id, compressed)}
218                                | Tab0
219                            ];
220                        disc_only_copies ->
221                            [{fixed, dets:info(Id, safe_fixed)} | Tab0];
222                        _ ->
223                            Tab0
224                    end,
225                [{0, proplists:get_value(Attr, Tab), Tab} | Acc]
226        end
227    end,
228    lists:foldl(CollectFun, [], mnesia:system_info(tables)).
229