1-module(observer_cli_port). 2 3-include("observer_cli.hrl"). 4 5-export([start/2]). 6 7-spec start(pid(), view_opts()) -> no_return. 8start(Port, Opts) -> 9 #view_opts{port = RefreshMs} = Opts, 10 RenderPid = spawn_link(fun() -> 11 ?output(?CLEAR), 12 render_worker(Port, RefreshMs, ?INIT_TIME_REF) 13 end), 14 manager(RenderPid, Opts). 15 16%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 17%%% Private 18%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 19manager(RenderPid, Opts) -> 20 case parse_cmd(Opts, RenderPid) of 21 quit -> 22 erlang:send(RenderPid, quit); 23 {new_interval, NewInterval} -> 24 erlang:send(RenderPid, {new_interval, NewInterval}), 25 manager(RenderPid, Opts#view_opts{port = NewInterval}); 26 ViewAction -> 27 erlang:send(RenderPid, ViewAction), 28 manager(RenderPid, Opts) 29 end. 30 31render_worker(Port, Interval, TimeRef) -> 32 PortInfo = recon:port_info(Port), 33 Meta = proplists:get_value(meta, PortInfo), 34 case lists:member(undefined, Meta) of 35 true -> 36 output_die_view(Port, Interval), 37 next_draw_view(TimeRef, Interval, Port); 38 false -> 39 Id = proplists:get_value(id, Meta), 40 Name = proplists:get_value(name, Meta), 41 OsPid = proplists:get_value(os_pid, Meta), 42 43 Signals = proplists:get_value(signals, PortInfo), 44 Link = proplists:get_value(links, Signals), 45 Monitors = proplists:get_value(monitors, Signals), 46 Connected = proplists:get_value(connected, Signals), 47 48 IO = proplists:get_value(io, PortInfo), 49 Input = proplists:get_value(input, IO), 50 Output = proplists:get_value(output, IO), 51 52 MemoryUsed = proplists:get_value(memory_used, PortInfo), 53 Memory = proplists:get_value(memory, MemoryUsed), 54 QueueSize = proplists:get_value(queue_size, MemoryUsed), 55 Menu = render_menu(info, Interval), 56 57 Line1 = render_port_info( 58 Port, 59 Id, 60 Name, 61 OsPid, 62 Input, 63 Output, 64 Memory, 65 QueueSize, 66 Connected 67 ), 68 Line2 = render_link_monitor(Link, Monitors), 69 Line3 = render_type_line(proplists:get_value(type, PortInfo)), 70 LastLine = render_last_line(), 71 72 ?output([?CURSOR_TOP, Menu, Line1, Line2, Line3, LastLine]), 73 next_draw_view(TimeRef, Interval, Port) 74 end. 75 76next_draw_view(TimeRef, Interval, Port) -> 77 NewTimeRef = observer_cli_lib:next_redraw(TimeRef, Interval), 78 next_draw_view_2(NewTimeRef, Interval, Port). 79 80next_draw_view_2(TimeRef, Interval, Port) -> 81 receive 82 quit -> 83 quit; 84 {new_interval, NewInterval} -> 85 ?output(?CLEAR), 86 render_worker(Port, NewInterval, TimeRef); 87 _ -> 88 ?output(?CLEAR), 89 render_worker(Port, Interval, TimeRef) 90 end. 91 92render_port_info( 93 Port, 94 Id, 95 Name, 96 OsPid, 97 Input, 98 Output, 99 Memory, 100 QueueSize, 101 Connected 102) -> 103 QueueSizeColor = 104 case QueueSize > 0 of 105 true -> ?RED; 106 false -> ?GREEN 107 end, 108 Title = 109 ?render([ 110 ?GRAY_BG, 111 ?W("Attr", 18), 112 ?W("Value", 20), 113 ?W("Attr", 18), 114 ?W("Value", 20), 115 ?W("Attr", 19), 116 ?W("Value", 21) 117 ]), 118 Rows = 119 ?render([ 120 ?W("port", 18), 121 ?W(Port, 20), 122 ?W("id", 18), 123 ?W(Id, 20), 124 ?W("name", 19), 125 ?W(Name, 21), 126 ?NEW_LINE, 127 ?W("queue_size", 18), 128 ?W2(QueueSizeColor, QueueSize, 21), 129 ?W(" input", 19), 130 ?W({byte, Input}, 20), 131 ?W("output", 19), 132 ?W({byte, Output}, 21), 133 ?NEW_LINE, 134 ?W("connected", 18), 135 ?W(Connected, 20), 136 ?W("memory", 18), 137 ?W({byte, Memory}, 20), 138 ?W("os_pid", 19), 139 ?W(OsPid, 21) 140 ]), 141 [Title, Rows]. 142 143render_link_monitor(Link, Monitors) -> 144 LinkStr = [ 145 begin 146 observer_cli_lib:to_list(P) 147 end 148 || P <- lists:sublist(Link, 30) 149 ], 150 MonitorsStr = [ 151 begin 152 case P of 153 {process, Pid} -> 154 observer_cli_lib:to_list(Pid); 155 {RegName, Node} -> 156 observer_cli_lib:to_list(RegName) ++ "/" ++ observer_cli_lib:to_list(Node) 157 end 158 end 159 || P <- lists:sublist(Monitors, 30) 160 ], 161 LinkInfo = "Links(" ++ erlang:integer_to_list(erlang:length(Link)) ++ ")", 162 MonitorInfo = "Monitors(" ++ erlang:integer_to_list(erlang:length(Monitors)) ++ ")", 163 ?render([ 164 ?W(LinkInfo, 18), 165 ?W(LinkStr, 110), 166 ?NEW_LINE, 167 ?W2(?UNDERLINE, MonitorInfo, 19), 168 ?W2(?UNDERLINE, MonitorsStr, 111) 169 ]). 170 171render_type_line(List) -> 172 PeerName = 173 case lists:keyfind(peername, 1, List) of 174 {_, Peer} -> addr_to_str(Peer); 175 false -> "undefined" 176 end, 177 SockName = 178 case lists:keyfind(sockname, 1, List) of 179 {_, Sock} -> addr_to_str(Sock); 180 false -> "undefined" 181 end, 182 Line1 = 183 ?render([ 184 ?UNDERLINE, 185 ?W(" " ++ SockName ++ "(sockname)", 55), 186 ?W("<=============>", 15), 187 ?W(" " ++ PeerName ++ "(peername)", 55) 188 ]), 189 Line2 = 190 case lists:keyfind(statistics, 1, List) of 191 {_, Stats} -> [Line1, render_stats(Stats)]; 192 false -> Line1 193 end, 194 case lists:keyfind(options, 1, List) of 195 {_, Opts} -> Line2 ++ [render_opts(Opts)]; 196 false -> Line2 197 end. 198 199render_stats(Stats) -> 200 RecvOct = proplists:get_value(recv_oct, Stats), 201 RecvCnt = proplists:get_value(recv_cnt, Stats), 202 RecvMax = proplists:get_value(recv_max, Stats), 203 RecvAvg = proplists:get_value(recv_avg, Stats), 204 RecvDvi = proplists:get_value(recv_dvi, Stats), 205 SendOct = proplists:get_value(send_oct, Stats), 206 SendCnt = proplists:get_value(send_cnt, Stats), 207 SendMax = proplists:get_value(send_max, Stats), 208 SendAvg = proplists:get_value(send_avg, Stats), 209 SendPend = proplists:get_value(send_pend, Stats), 210 ?render([ 211 ?W("recv_cnt", 9), 212 ?W(RecvCnt, 12), 213 ?W("recv_oct", 8), 214 ?W({byte, RecvOct}, 12), 215 ?W("recv_max", 9), 216 ?W({byte, RecvMax}, 12), 217 ?W("recv_avg", 9), 218 ?W({byte, RecvAvg}, 12), 219 ?W("recv_dvi", 9), 220 ?W({byte, RecvDvi}, 12), 221 ?NEW_LINE, 222 ?W("send_cnt", 9), 223 ?W(SendCnt, 12), 224 ?W("send_oct", 8), 225 ?W({byte, SendOct}, 12), 226 ?W("send_max", 9), 227 ?W({byte, SendMax}, 12), 228 ?W("send_avg", 9), 229 ?W({byte, SendAvg}, 12), 230 ?W("send_pend", 9), 231 ?W(SendPend, 12) 232 ]). 233 234render_opts(Opts) -> 235 Active = proplists:get_value(active, Opts), 236 Broadcast = proplists:get_value(broadcast, Opts), 237 Buffer = proplists:get_value(buffer, Opts), 238 DelaySend = proplists:get_value(delay_send, Opts), 239 DontRoute = proplists:get_value(dontroute, Opts), 240 241 ExitOnClose = proplists:get_value(exit_on_close, Opts), 242 Header = proplists:get_value(header, Opts), 243 HighWatermark = proplists:get_value(high_watermark, Opts), 244 KeepAlive = proplists:get_value(keepalive, Opts), 245 Linger = io_lib:format("~p", [proplists:get_value(linger, Opts)]), 246 247 LowWatermark = proplists:get_value(low_watermark, Opts), 248 Mode = proplists:get_value(mode, Opts), 249 NoDelay = proplists:get_value(nodelay, Opts), 250 Packet = proplists:get_value(packet, Opts), 251 PacketSize = proplists:get_value(packet_size, Opts), 252 253 Priority = proplists:get_value(priority, Opts), 254 RecBuf = proplists:get_value(recbuf, Opts), 255 ReuseAddr = proplists:get_value(reuseaddr, Opts), 256 SendTimeout = proplists:get_value(send_timeout, Opts), 257 SndBuf = proplists:get_value(sndbuf, Opts), 258 Title = 259 ?render([ 260 ?GRAY_BG, 261 ?W("Option", 9), 262 ?W("Value", 6), 263 ?W("Option", 14), 264 ?W("Value", 12), 265 ?W("Option", 9), 266 ?W("Value", 12), 267 ?W("Option", 13), 268 ?W("Value", 8), 269 ?W("Option", 9), 270 ?W("Value", 12) 271 ]), 272 Rows = 273 ?render([ 274 ?W("mode", 9), 275 ?W(Mode, 6), 276 ?W("recbuf", 14), 277 ?W({byte, RecBuf}, 12), 278 ?W("sndbuf", 9), 279 ?W({byte, SndBuf}, 12), 280 ?W("delay_send", 13), 281 ?W(DelaySend, 8), 282 ?W("dontroute", 9), 283 ?W(DontRoute, 12), 284 ?NEW_LINE, 285 ?W("reuseaddr", 9), 286 ?W(ReuseAddr, 6), 287 ?W("packet_size", 14), 288 ?W({byte, PacketSize}, 12), 289 ?W("buffer", 9), 290 ?W({byte, Buffer}, 12), 291 ?W("exit_on_close", 13), 292 ?W(ExitOnClose, 8), 293 ?W("priority", 9), 294 ?W(Priority, 12), 295 ?NEW_LINE, 296 ?W("active", 9), 297 ?W(Active, 6), 298 ?W("low_watermark", 14), 299 ?W({byte, LowWatermark}, 12), 300 ?W("header", 9), 301 ?W(Header, 12), 302 ?W("keepalive", 13), 303 ?W(KeepAlive, 8), 304 ?W("linger", 9), 305 ?W(Linger, 12), 306 ?NEW_LINE, 307 ?W("nodelay", 9), 308 ?W(NoDelay, 6), 309 ?W("high_watermark", 14), 310 ?W({byte, HighWatermark}, 12), 311 ?W("broadcast", 9), 312 ?W(Broadcast, 12), 313 ?W("send_timeout", 13), 314 ?W(SendTimeout, 8), 315 ?W("packet", 9), 316 ?W(Packet, 12) 317 ]), 318 [Title, Rows]. 319 320render_last_line() -> 321 io_lib:format("|\e[7mq(quit) ~124.124s\e[0m|~n", [" "]). 322 323render_menu(Type, Interval) -> 324 Text = "Interval: " ++ integer_to_list(Interval) ++ "ms", 325 Title = get_menu_title(Type), 326 UpTime = observer_cli_lib:uptime(), 327 TitleWidth = ?COLUMN + 41 - erlang:length(UpTime), 328 ?render([?W([Title | Text], TitleWidth) | UpTime]). 329 330get_menu_title(Type) -> 331 [Home, Net, Port] = get_menu_title2(Type), 332 [Home, "|", Net, "|", Port]. 333 334get_menu_title2(info) -> 335 [?UNSELECT("Home(H)"), ?UNSELECT("Network(N)"), ?SELECT("Port Info(P)")]. 336 337parse_cmd(ViewOpts, Pid) -> 338 case observer_cli_lib:to_list(io:get_line("")) of 339 "q\n" -> 340 quit; 341 "Q\n" -> 342 quit; 343 "P\n" -> 344 info_view; 345 "H\n" -> 346 erlang:exit(Pid, stop), 347 observer_cli:start(ViewOpts); 348 "N\n" -> 349 erlang:exit(Pid, stop), 350 observer_cli_inet:start(ViewOpts); 351 Number -> 352 observer_cli_lib:parse_integer(Number) 353 end. 354 355output_die_view(Port, Interval) -> 356 Menu = render_menu(info, Interval), 357 Line = io_lib:format("\e[31mPort(~p) has already die.\e[0m~n", [Port]), 358 LastLine = render_last_line(), 359 ?output([?CURSOR_TOP, Menu, Line, LastLine]). 360 361addr_to_str({Addr, Port}) -> 362 AddrList = [ 363 begin 364 erlang:integer_to_list(A) 365 end 366 || A <- erlang:tuple_to_list(Addr) 367 ], 368 string:join(AddrList, ".") ++ ":" ++ erlang:integer_to_list(Port). 369