1%%% @author zhongwen <zhongwencool@gmail.com> 2-module(observer_cli_ets). 3 4-include("observer_cli.hrl"). 5 6%% API 7-export([start/1]). 8-export([clean/1]). 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). 13 14-spec start(ViewOpts) -> no_return() when ViewOpts :: view_opts(). 15start( 16 #view_opts{ 17 ets = #ets{interval = Interval, attr = Attr, cur_page = CurPage}, 18 auto_row = AutoRow 19 } = ViewOpts 20) -> 21 Pid = spawn_link(fun() -> 22 ?output(?CLEAR), 23 render_worker(Interval, ?INIT_TIME_REF, Attr, CurPage, AutoRow) 24 end), 25 manager(Pid, ViewOpts). 26 27-spec clean(list()) -> ok. 28clean(Pids) -> observer_cli_lib:exit_processes(Pids). 29 30%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 31%%% Private 32%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 33manager(ChildPid, #view_opts{ets = EtsOpts = #ets{cur_page = CurPage}} = ViewOpts) -> 34 case observer_cli_lib:parse_cmd(ViewOpts, ?MODULE, [ChildPid]) of 35 quit -> 36 erlang:send(ChildPid, quit); 37 {new_interval, NewMs} -> 38 clean([ChildPid]), 39 start(ViewOpts#view_opts{ets = EtsOpts#ets{interval = NewMs}}); 40 size -> 41 clean([ChildPid]), 42 start(ViewOpts#view_opts{ets = EtsOpts#ets{attr = size}}); 43 %% Home 44 {func, proc_count, memory} -> 45 clean([ChildPid]), 46 start(ViewOpts#view_opts{ets = EtsOpts#ets{attr = memory}}); 47 page_down_top_n -> 48 NewPage = max(CurPage + 1, 1), 49 clean([ChildPid]), 50 start(ViewOpts#view_opts{ets = EtsOpts#ets{cur_page = NewPage}}); 51 page_up_top_n -> 52 NewPage = max(CurPage - 1, 1), 53 clean([ChildPid]), 54 start(ViewOpts#view_opts{ets = EtsOpts#ets{cur_page = NewPage}}); 55 _ -> 56 manager(ChildPid, ViewOpts) 57 end. 58 59render_worker(Interval, LastTimeRef, Attr, CurPage, AutoRow) -> 60 TerminalRow = observer_cli_lib:get_terminal_rows(AutoRow), 61 Text = "Interval: " ++ integer_to_list(Interval) ++ "ms", 62 Menu = observer_cli_lib:render_menu(ets, Text), 63 Ets = render_ets_info(erlang:max(0, TerminalRow - 4), CurPage, Attr), 64 LastLine = observer_cli_lib:render_last_line(?LAST_LINE), 65 ?output([?CURSOR_TOP, Menu, Ets, LastLine]), 66 NextTimeRef = observer_cli_lib:next_redraw(LastTimeRef, Interval), 67 receive 68 quit -> quit; 69 _ -> render_worker(Interval, NextTimeRef, Attr, CurPage, AutoRow) 70 end. 71 72render_ets_info(Rows, CurPage, Attr) -> 73 AllEts = [ 74 begin 75 get_ets_info(Tab, Attr) 76 end 77 || Tab <- ets:all() 78 ], 79 WordSize = erlang:system_info(wordsize), 80 {_StartPos, SortEts} = observer_cli_lib:sublist(AllEts, Rows, CurPage), 81 {MemColor, SizeColor} = 82 case Attr of 83 memory -> {?RED_BG, ?GRAY_BG}; 84 _ -> {?GRAY_BG, ?RED_BG} 85 end, 86 Title = ?render([ 87 ?UNDERLINE, 88 ?W2(?GRAY_BG, "Table Name", 37), 89 ?UNDERLINE, 90 ?W2(SizeColor, "Size", 14), 91 ?UNDERLINE, 92 ?W2(MemColor, " Memory ", 14), 93 ?UNDERLINE, 94 ?W2(?GRAY_BG, "Type", 15), 95 ?UNDERLINE, 96 ?W2(?GRAY_BG, "Protection", 12), 97 ?UNDERLINE, 98 ?W2(?GRAY_BG, "KeyPos", 8), 99 ?UNDERLINE, 100 ?W2(?GRAY_BG, "Write/Read", 14), 101 ?UNDERLINE, 102 ?W2(?GRAY_BG, "Owner Pid", 15) 103 ]), 104 RowView = [ 105 begin 106 Name = proplists:get_value(name, Ets), 107 Memory = proplists:get_value(memory, Ets), 108 Size = proplists:get_value(size, Ets), 109 Type = proplists:get_value(type, Ets), 110 Protect = proplists:get_value(protection, Ets), 111 KeyPos = proplists:get_value(keypos, Ets), 112 Write = observer_cli_lib:to_list(proplists:get_value(write_concurrency, Ets)), 113 Read = observer_cli_lib:to_list(proplists:get_value(read_concurrency, Ets)), 114 Owner = proplists:get_value(owner, Ets), 115 ?render([ 116 ?W(Name, 36), 117 ?W(Size, 12), 118 ?W({byte, Memory * WordSize}, 12), 119 ?W(Type, 13), 120 ?W(Protect, 10), 121 ?W(KeyPos, 6), 122 ?W(Write ++ "/" ++ Read, 12), 123 ?W(Owner, 14) 124 ]) 125 end 126 || {_, _, Ets} <- SortEts 127 ], 128 [Title | RowView]. 129 130get_ets_info(Tab, Attr) -> 131 case catch ets:info(Tab) of 132 {'EXIT', _} -> 133 { 134 0, 135 0, 136 [ 137 %%it maybe die 138 {name, unread}, 139 {write_concurrency, unread}, 140 {read_concurrency, unread}, 141 {compressed, unread}, 142 {memory, unread}, 143 {owner, unread}, 144 {heir, unread}, 145 {size, unread}, 146 {node, unread}, 147 {named_table, unread}, 148 {type, unread}, 149 {keypos, unread}, 150 {protection, unread} 151 ] 152 }; 153 Info when is_list(Info) -> 154 Owner = proplists:get_value(owner, Info), 155 NewInfo = 156 case is_reg(Owner) of 157 Owner -> Info; 158 Reg -> lists:keyreplace(Owner, 1, Info, {owner, Reg}) 159 end, 160 { 161 0, 162 proplists:get_value(Attr, NewInfo), 163 NewInfo 164 } 165 end. 166 167is_reg(Owner) -> 168 case process_info(Owner, registered_name) of 169 {registered_name, Name} -> Name; 170 _ -> Owner 171 end. 172