Current section

Files

Jump to
observer_cli src observer_cli_system.erl
Raw

src/observer_cli_system.erl

%%% @author zhongwen <zhongwencool@gmail.com>
-module(observer_cli_system).
-include("observer_cli.hrl").
%% API
-export([start/2]).
%%for rpc call
-export([get_system_info/1]).
%% @doc List System and Architecture, CPU's and Threads metrics in observer's system
-spec start(Node, ViewOpts) -> no_return() when
Node:: atom(),
ViewOpts:: view_opts().
start(Node, #view_opts{sys = #system{interval = RefreshMillSecond}, incr_rows = IncrRows} = ViewOpts) ->
ParentPid = self(),
Pid = spawn(fun() ->
observer_cli_lib:clear_screen(),
loop(Node, RefreshMillSecond, IncrRows, erlang:make_ref(), ParentPid) end),
waiting(Node, Pid, ViewOpts).
%%for fetching data from remote data by rpc:call/4
-spec get_system_info(Node) -> [tuple()] when
Node:: atom().
get_system_info(local_node) -> sys_info();
get_system_info(Node) -> rpc:call(Node, ?MODULE, get_system_info, [local_node]).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%% Private
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
waiting(Node, ChildPid, #view_opts{sys = SysOpts} = ViewOpts) ->
Input = observer_cli_lib:get_line(""),
case Input of
"q\n" -> erlang:send(ChildPid, quit);
"o\n" ->
erlang:exit(ChildPid, stop),
observer_cli:start_node(Node, ViewOpts);
"a\n" ->
erlang:exit(ChildPid, stop),
observer_cli_allocator:start(Node, ViewOpts);
"h\n" ->
erlang:exit(ChildPid, stop),
observer_cli_help:start(Node, ViewOpts);
"db\n" ->
erlang:exit(ChildPid, stop),
observer_cli_mnesia:start(Node, ViewOpts);
[$r| RefreshInterval] ->
case string:to_integer(RefreshInterval) of
{error, no_integer} -> waiting(Node, ChildPid, ViewOpts);
{NewInterval, _} when NewInterval >= ?SYSTEM_MIN_INTERVAL ->
erlang:send(ChildPid, {new_interval, NewInterval}),
waiting(Node, ChildPid, ViewOpts#view_opts{sys = SysOpts#system{interval = NewInterval}});
{_Interval, _} -> waiting(Node, ChildPid, ViewOpts)
end;
[$i| IncrRows] ->
case string:to_integer(IncrRows) of
{error, no_integer} -> waiting(Node, ChildPid, ViewOpts);
{NewIncrRows, _} ->
erlang:send(ChildPid, {incr_rows, NewIncrRows}),
waiting(Node, ChildPid, ViewOpts#view_opts{incr_rows = NewIncrRows})
end;
_ -> waiting(Node, ChildPid, ViewOpts)
end.
loop(Node, Interval, IncrRows, LastTimeRef, ParentPid) ->
observer_cli_lib:move_cursor_to_top_line(),
refresh(Node, Interval),
observer_cli_ets:draw_ets_info(Node, IncrRows),
draw_last_line(Interval),
erlang:cancel_timer(LastTimeRef),
TimeRef = erlang:send_after(Interval, self(), refresh),
receive
quit -> quit;
{new_interval, NewInterval} -> loop(Node, NewInterval, IncrRows, TimeRef, ParentPid);
{incr_rows, NewIncrRows} -> loop(Node, Interval, NewIncrRows, TimeRef, ParentPid);
_ -> loop(Node, Interval, IncrRows, TimeRef, ParentPid)
end.
refresh(Node, Interval) ->
SysInfo = get_system_info(Node),
{Info, Stat} = info_fields(),
SystemAndCPU = observer_lib:fill_info(Info, SysInfo),
MemAndStatistics = observer_lib:fill_info(Stat, SysInfo),
System = proplists:get_value("System and Architecture", SystemAndCPU),
CPU = proplists:get_value("CPU's and Threads", SystemAndCPU),
{_, _, Memory} = lists:keyfind("Memory Usage", 1, MemAndStatistics),
{_, _, Statistics} = lists:keyfind("Statistics", 1, MemAndStatistics),
draw_menu(Node, Interval),
draw(System, CPU, Memory, Statistics).
draw(System, CPU, Memory, Statistics) ->
io:format("|\e[46m~-22.22s| ~-8.8s|~-23.23s| ~-7.7s|~-15.15s|~18.18s|~-13.13s|~15.15s \e[49m|~n", %%cyan background
["System and Architecture", "State", "CPU's and Threads", "State", "Memory Usage", "State", "Statistics", "State"]),
NewSystem = [begin {Key, Value} end|| {Key, Value}<- System, Key =/= "Compiled for" andalso Key =/= "smp Support"],
Format = "|~-22.22s| ~-8.8s|~-23.23s| ~-7.7s|~-15.15s|~18.18s|~-13.13s|~15.15s |~n",
[{_, TotalMem}|_R] = Memory,
[begin
{SysKey, SysVal} = lists:nth(Pos, NewSystem),
{CpuKey, CpuVal} = lists:nth(Pos, CPU),
{MemKey, MemVal} = lists:nth(Pos, Memory),
{StatisKey, StatisVal} =
case lists:nth(Pos, Statistics) of
{"Up time", _} -> {"Smp Support", to_list(proplists:get_value("Smp Support", System))};
Value -> Value
end,
io:format(Format, [SysKey, to_list(SysVal), CpuKey, to_list(CpuVal),
MemKey, byte_to_megabyte(MemVal, TotalMem), StatisKey, to_list(StatisVal)])
end|| Pos<- lists:seq(1, 6)],
io:format("|~-22.22s| ~-106.106s |~n", ["Compiled for", to_list(proplists:get_value("Compiled for", System))]).
draw_menu(Node, Interval) ->
[Home, Ets, Alloc, Mnesia, Help] = observer_cli_lib:get_menu_title(ets),
Title = lists:flatten(["|", Home, "|", Ets, "|", Alloc, "|", Mnesia, "|", Help, "|"]),
UpTime = observer_cli_lib:green(" Uptime:" ++ observer_cli_lib:uptime(Node)) ++ "|",
RefreshStr = "Refresh: " ++ integer_to_list(Interval) ++ "ms",
SpaceLen = ?COLUMN_WIDTH - erlang:length(Title) - erlang:length(RefreshStr) - erlang:length(UpTime)+ 110,
Space = case SpaceLen > 0 of true -> lists:duplicate(SpaceLen, " "); false -> [] end,
io:format("~s~n", [Title ++ RefreshStr ++ Space ++ UpTime]).
draw_last_line(Interval) ->
Text = io_lib:format("r~w(refresh every ~wms)", [Interval, Interval]),
io:format("|\e[31;1mINPUT: \e[0m\e[44mq(quit) ~-111.111s\e[49m|~n", [Text]).
to_list(Val) when is_integer(Val) -> integer_to_list(Val);
to_list(Val) when is_atom(Val) -> atom_to_list(Val);
to_list({bytes, Val}) ->
M = trunc(Val/(1024*1024)*1000),
Integer = M div 1000,
Decimal = M - Integer * 1000,
lists:flatten(io_lib:format("~w.~4..0wM", [Integer, Decimal]));
to_list(Val) -> Val.
byte_to_megabyte({bytes, Val}, {bytes, Total}) when is_integer(Val) ->
M = trunc(Val/(1024*1024)*1000),
Integer = M div 1000,
Decmial = M - Integer * 1000,
Perc = observer_cli_lib:float_to_percent_with_two_digit(Val/Total),
lists:flatten(io_lib:format("~w.~4..0wM ~s", [Integer, Decmial, Perc])).
info_fields() ->
Info = [{"System and Architecture",
[{"System Version", otp_release},
{"Erts Version", version},
{"Compiled for", system_architecture},
{"Emulator Wordsize", wordsize_external},
{"Process Wordsize", wordsize_internal},
{"Smp Support", smp_support},
{"Thread Support", threads},
{"Async thread pool size", thread_pool_size}
]},
{"CPU's and Threads",
[{"Logical CPU's", logical_processors},
{"Online Logical CPU's", logical_processors_online},
{"Available Logical CPU's", logical_processors_available},
{"Schedulers", schedulers},
{"Online schedulers", schedulers_online},
{"Available schedulers", schedulers_available}
]}
],
Stat = [{"Memory Usage", right,
[{"Total", {bytes, total}},
{"Processes", {bytes, processes}},
{"Atoms", {bytes, atom}},
{"Binaries", {bytes, binary}},
{"Code", {bytes, code}},
{"Ets", {bytes, ets}}
]},
{"Statistics", right,
[{"Up time", {time_ms, uptime}},
{"Max Processes", process_limit},
{"Processes", process_count},
{"Run Queue", run_queue},
{"Total IO In", {bytes, io_input}},
{"Total IO Out", {bytes, io_output}}
]}
],
{Info, Stat}.
sys_info() ->
MemInfo = try erlang:memory() of
Mem -> Mem
catch _:_ -> []
end,
SchedulersOnline = erlang:system_info(schedulers_online),
SchedulersAvailable = case erlang:system_info(multi_scheduling) of
enabled -> SchedulersOnline;
_ -> 1
end,
{{_, Input}, {_, Output}} = erlang:statistics(io),
[{process_count, erlang:system_info(process_count)},
{process_limit, erlang:system_info(process_limit)},
{uptime, element(1, erlang:statistics(wall_clock))},
{run_queue, erlang:statistics(run_queue)},
{io_input, Input},
{io_output, Output},
{logical_processors, erlang:system_info(logical_processors)},
{logical_processors_online, erlang:system_info(logical_processors_online)},
{logical_processors_available, erlang:system_info(logical_processors_available)},
{schedulers, erlang:system_info(schedulers)},
{schedulers_online, SchedulersOnline},
{schedulers_available, SchedulersAvailable},
{otp_release, erlang:system_info(otp_release)},
{version, erlang:system_info(version)},
{system_architecture, erlang:system_info(system_architecture)},
{kernel_poll, erlang:system_info(kernel_poll)},
{smp_support, erlang:system_info(smp_support)},
{threads, erlang:system_info(threads)},
{thread_pool_size, erlang:system_info(thread_pool_size)},
{wordsize_internal, erlang:system_info({wordsize, internal})},
{wordsize_external, erlang:system_info({wordsize, external})},
{alloc_info, alloc_info()}
| MemInfo].
alloc_info() ->
AlcuAllocs = erlang:system_info(alloc_util_allocators),
try erlang:system_info({allocator_sizes, AlcuAllocs}) of
Allocators -> Allocators
catch _:_ -> []
end.