1%%% @author zhongwen <zhongwencool@gmail.com> 2-module(observer_cli_inet). 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) ic(inet_count) iw(inet_window) rc(recv_cnt) ro(recv_oct)" 12 " sc(send_cnt) so(send_oct) cnt oct 9(port 9 info) pd/pu(page:down/up)" 13). 14 15-spec start(view_opts()) -> no_return. 16start(#view_opts{inet = InetOpt, auto_row = AutoRow} = ViewOpts) -> 17 StorePid = observer_cli_store:start(), 18 RenderPid = spawn_link( 19 fun() -> 20 ?output(?CLEAR), 21 {{input, In}, {output, Out}} = erlang:statistics(io), 22 render_worker(StorePid, InetOpt, ?INIT_TIME_REF, 0, {In, Out}, AutoRow) 23 end 24 ), 25 manager(StorePid, RenderPid, ViewOpts). 26 27-spec clean(list()) -> ok. 28clean(Pids) -> observer_cli_lib:exit_processes(Pids). 29 30%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 31%%% Private 32%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 33manager(StorePid, RenderPid, ViewOpts = #view_opts{inet = InetOpts}) -> 34 #inet{cur_page = CurPage, pages = Pages} = InetOpts, 35 case observer_cli_lib:parse_cmd(ViewOpts, ?MODULE, [RenderPid, StorePid]) of 36 quit -> 37 observer_cli_lib:exit_processes([StorePid]), 38 erlang:send(RenderPid, quit); 39 {new_interval, NewInterval} -> 40 erlang:send(RenderPid, {new_interval, NewInterval}), 41 NewInet = InetOpts#inet{interval = NewInterval}, 42 manager(StorePid, RenderPid, ViewOpts#view_opts{inet = NewInet}); 43 Func when Func =:= inet_count; Func =:= inet_window -> 44 clean([StorePid, RenderPid]), 45 start(ViewOpts#view_opts{inet = InetOpts#inet{func = Func}}); 46 Type when 47 Type =:= recv_cnt; 48 Type =:= recv_oct; 49 Type =:= send_cnt; 50 Type =:= send_oct; 51 Type =:= cnt; 52 Type =:= oct 53 -> 54 clean([StorePid, RenderPid]), 55 start(ViewOpts#view_opts{inet = InetOpts#inet{type = Type}}); 56 {jump, NewPos} -> 57 NewPages = observer_cli_lib:update_page_pos(CurPage, NewPos, Pages), 58 NewInetOpts = InetOpts#inet{pages = NewPages}, 59 start_port_view(StorePid, RenderPid, ViewOpts#view_opts{inet = NewInetOpts}, false); 60 jump -> 61 start_port_view(StorePid, RenderPid, ViewOpts, true); 62 page_down_top_n -> 63 NewPage = max(CurPage + 1, 1), 64 NewPages = observer_cli_lib:update_page_pos(StorePid, NewPage, Pages), 65 clean([StorePid, RenderPid]), 66 start(ViewOpts#view_opts{inet = InetOpts#inet{cur_page = NewPage, pages = NewPages}}); 67 page_up_top_n -> 68 NewPage = max(CurPage - 1, 1), 69 NewPages = observer_cli_lib:update_page_pos(StorePid, NewPage, Pages), 70 clean([StorePid, RenderPid]), 71 start(ViewOpts#view_opts{inet = InetOpts#inet{cur_page = NewPage, pages = NewPages}}); 72 _ -> 73 manager(StorePid, RenderPid, ViewOpts) 74 end. 75 76render_worker(StorePid, InetOpt, LastTimeRef, Count, LastIO, AutoRow) -> 77 #inet{func = Function, type = Type, interval = Interval, cur_page = CurPage} = InetOpt, 78 TerminalRow = observer_cli_lib:get_terminal_rows(AutoRow), 79 Row = erlang:max(TerminalRow - 5, 0), 80 Text = get_menu_str(Function, Type, Interval, Row), 81 Menu = observer_cli_lib:render_menu(inet, Text), 82 TopLen = Row * CurPage, 83 InetList = inet_info(Function, Type, TopLen, Interval, Count), 84 {IORows, NewIO} = render_io_rows(LastIO), 85 {PortList, InetRows} = render_inet_rows(InetList, TopLen, InetOpt), 86 LastLine = observer_cli_lib:render_last_line(?LAST_LINE), 87 ?output([?CURSOR_TOP, Menu, IORows, InetRows, LastLine]), 88 observer_cli_store:update(StorePid, Row, PortList), 89 NewInterval = 90 case Function of 91 inet_count -> Interval; 92 inet_window -> 10 93 end, 94 TimeRef = observer_cli_lib:next_redraw(LastTimeRef, NewInterval), 95 receive 96 {new_interval, NewInterval} -> 97 ?output(?CLEAR), 98 render_worker( 99 StorePid, 100 InetOpt#inet{interval = NewInterval}, 101 TimeRef, 102 Count + 1, 103 NewIO, 104 AutoRow 105 ); 106 quit -> 107 quit; 108 _ -> 109 render_worker(StorePid, InetOpt, TimeRef, Count + 1, NewIO, AutoRow) 110 end. 111 112render_io_rows({LastIn, LastOut}) -> 113 {{input, In}, {output, Out}} = erlang:statistics(io), 114 { 115 ?render([ 116 ?YELLOW, 117 ?W("Byte Input", 14), 118 ?W({byte, In - LastIn}, 12), 119 ?W("Byte Output", 13), 120 ?W({byte, Out - LastOut}, 12), 121 ?W("Total Input", 15), 122 ?W({byte, In}, 17), 123 ?W("Total Output", 15), 124 ?W({byte, Out}, 17) 125 ]), 126 {In, Out} 127 }. 128 129render_inet_rows([], Rows, #inet{func = inet_count, type = Type}) -> 130 {[], io_lib:format("\e[32;1mGet nothing for recon:inet_count(~p, ~p)\e[0m~n", [Type, Rows])}; 131render_inet_rows([], Rows, #inet{func = inet_window, type = Type, interval = Interval}) -> 132 {[], 133 io_lib:format("\e[32;1mGet nothing for recon:inet_window(~p, ~p, ~p)\e[0m~n", [ 134 Type, 135 Rows, 136 Interval 137 ])}; 138render_inet_rows(InetList, Num, #inet{ 139 type = Type, 140 pages = Pages, 141 cur_page = Page 142}) when Type =:= cnt orelse Type =:= oct -> 143 {Unit, RecvType, SendType} = trans_type(Type), 144 Title = title(Type, RecvType, SendType), 145 {Start, ChoosePos} = observer_cli_lib:get_pos(Page, Num, Pages, erlang:length(InetList)), 146 FormatFunc = fun(Item, {Acc, Acc1, Pos}) -> 147 {Port, Value, [{_, Recv}, {_, Send}]} = Item, 148 {memory_used, MemoryUsed} = recon:port_info(Port, memory_used), 149 {io, IO} = recon:port_info(Port, io), 150 Input = proplists:get_value(input, IO), 151 Output = proplists:get_value(output, IO), 152 QueueSize = proplists:get_value(queue_size, MemoryUsed), 153 Memory = proplists:get_value(memory, MemoryUsed), 154 IP = get_remote_ip(Port), 155 {ValueFormat, RecvFormat, SendFormat} = trans_format(Unit, Value, Recv, Send), 156 R = [ 157 ?W(Pos, 2), 158 ?W(Port, 16), 159 ValueFormat, 160 RecvFormat, 161 SendFormat, 162 ?W({byte, Output}, 12), 163 ?W({byte, Input}, 12), 164 ?W(QueueSize, 6), 165 ?W({byte, Memory}, 12), 166 ?W(IP, 19) 167 ], 168 Rows = add_choose_color(ChoosePos, Pos, R), 169 {[?render(Rows) | Acc], [{Pos, Port} | Acc1], Pos + 1} 170 end, 171 {Rows, PortList, _} = lists:foldl( 172 FormatFunc, 173 {[], [], Start}, 174 lists:sublist(InetList, Start, Num) 175 ), 176 {PortList, [Title | lists:reverse(Rows)]}; 177render_inet_rows(InetList, Num, #inet{type = Type, pages = Pages, cur_page = Page}) -> 178 {Unit, Type1, Type2} = trans_type(Type), 179 Title = title(Type, Type1, Type2), 180 {Start, ChoosePos} = observer_cli_lib:get_pos(Page, Num, Pages, erlang:length(InetList)), 181 FormatFunc = fun(Item, {Acc, Acc1, Pos}) -> 182 {Port, Value, _} = Item, 183 {memory_used, MemoryUsed} = recon:port_info(Port, memory_used), 184 {io, IO} = recon:port_info(Port, io), 185 Input = proplists:get_value(input, IO), 186 Output = proplists:get_value(output, IO), 187 QueueSize = proplists:get_value(queue_size, MemoryUsed), 188 Memory = proplists:get_value(memory, MemoryUsed), 189 IP = get_remote_ip(Port), 190 Packet1 = getstat(Port, erlang:list_to_existing_atom(Type1)), 191 AllPacket = 192 case is_integer(Packet1) of 193 true -> Value + Packet1; 194 false -> Value 195 end, 196 {ValueFormat, Packet1Format, AllFormat} = trans_format(Unit, Value, Packet1, AllPacket), 197 R = [ 198 ?W(Pos, 2), 199 ?W(Port, 16), 200 ValueFormat, 201 Packet1Format, 202 AllFormat, 203 ?W({byte, Input}, 12), 204 ?W({byte, Output}, 12), 205 ?W(QueueSize, 6), 206 ?W({byte, Memory}, 12), 207 ?W(IP, 19) 208 ], 209 Rows = add_choose_color(ChoosePos, Pos, R), 210 {[?render(Rows) | Acc], [{Pos, Port} | Acc1], Pos + 1} 211 end, 212 {Rows, PortList, _} = lists:foldl( 213 FormatFunc, 214 {[], [], Start}, 215 lists:sublist(InetList, Start, Num) 216 ), 217 {PortList, [Title | lists:reverse(Rows)]}. 218 219get_menu_str(inet_count, Type, Interval, Rows) -> 220 io_lib:format("recon:inet_count(~p, ~w) Interval:~wms", [Type, Rows, Interval]); 221get_menu_str(inet_window, Type, Interval, Rows) -> 222 io_lib:format("recon:inet_window(~p, ~w, ~w) Interval:~wms", [Type, Rows, Interval, Interval]). 223 224inet_info(inet_count, Type, Num, _, _) -> recon:inet_count(Type, Num); 225inet_info(inet_window, Type, Num, _, 0) -> recon:inet_count(Type, Num); 226inet_info(inet_window, Type, Num, Ms, _) -> recon:inet_window(Type, Num, Ms). 227 228getstat(Port, Attr) -> 229 case inet:getstat(Port, [Attr]) of 230 {ok, [{_, Value}]} -> Value; 231 {error, Err} -> inet:format_error(Err) 232 end. 233 234start_port_view(StorePid, RenderPid, Opts = #view_opts{inet = InetOpt}, AutoJump) -> 235 #inet{cur_page = CurPage, pages = Pages} = InetOpt, 236 {_, CurPos} = lists:keyfind(CurPage, 1, Pages), 237 case observer_cli_store:lookup_pos(StorePid, CurPos) of 238 {CurPos, ChoosePort} -> 239 clean([StorePid, RenderPid]), 240 observer_cli_port:start(ChoosePort, Opts); 241 {_, ChoosePort} when AutoJump -> 242 clean([StorePid, RenderPid]), 243 observer_cli_port:start(ChoosePort, Opts); 244 _ -> 245 manager(StorePid, RenderPid, Opts) 246 end. 247 248trans_type(cnt) -> 249 {number, "recv_cnt", "send_cnt"}; 250trans_type(oct) -> 251 {byte, "recv_oct", "send_oct"}; 252trans_type(send_cnt) -> 253 {number, "recv_cnt", "cnt"}; 254trans_type(recv_cnt) -> 255 {number, "send_cnt", "cnt"}; 256trans_type(send_oct) -> 257 {byte, "recv_oct", "oct"}; 258trans_type(recv_oct) -> 259 {byte, "send_oct", "oct"}. 260 261trans_format(byte, Val, Val1, Val2) -> 262 { 263 ?W({byte, Val}, 10), 264 ?W({byte, Val1}, 10), 265 ?W({byte, Val2}, 10) 266 }; 267trans_format(number, Val, Val1, Val2) -> 268 { 269 ?W(Val, 10), 270 ?W(Val1, 10), 271 ?W(Val2, 10) 272 }. 273 274title(Type, Type1, Type2) -> 275 ?render([ 276 ?UNDERLINE, 277 ?GRAY_BG, 278 ?W("NO", 2), 279 ?W("Port", 16), 280 ?W(erlang:atom_to_list(Type), 10), 281 ?W(Type1, 10), 282 ?W(Type2, 10), 283 ?W("output", 12), 284 ?W("input", 12), 285 ?W("queuesize", 6), 286 ?W("memory", 12), 287 ?W("Peername(ip:port)", 19) 288 ]). 289 290get_remote_ip(P) -> 291 case inet:peername(P) of 292 {ok, {Addr, Port}} -> 293 AddrList = [ 294 begin 295 erlang:integer_to_list(A) 296 end 297 || A <- erlang:tuple_to_list(Addr) 298 ], 299 string:join(AddrList, ".") ++ ":" ++ erlang:integer_to_list(Port); 300 {error, Err} -> 301 inet:format_error(Err) 302 end. 303 304add_choose_color(ChoosePos, ChoosePos, R) -> [?CHOOSE_BG | R]; 305add_choose_color(_ChoosePos, _CurPos, R) -> R. 306