Current section
Files
Jump to
Current section
Files
src/observer_cli_process.erl
-module(observer_cli_process).
-include("observer_cli.hrl").
-export([start/2]).
-spec start(pid(), view_opts()) -> no_return.
start(ProcessPid, Opts) ->
#view_opts{process = #process{interval = RefreshMillSecond}} = Opts,
ChildPid = spawn(fun() ->
?output(?CLEAR),
InitQ = lists:foldl(fun(_X, Acc) -> queue:in('NaN', Acc) end, queue:new(), lists:seq(1, 5)),
render_worker(RefreshMillSecond, ProcessPid, undefined, InitQ, InitQ)
end),
manager(ChildPid, Opts).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%% Private
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
manager(ChildPid, #view_opts{process = ProcOpts} = Opts) ->
case observer_cli_lib:parse_cmd(Opts, ChildPid) of
quit -> erlang:send(ChildPid, quit);
{proc_count, binary_memory, no_change} -> %% shared with home 'b'
erlang:exit(ChildPid, stop),
observer_cli:start(Opts);
{new_interval, NewInterval} ->
erlang:send(ChildPid, {new_interval, NewInterval}),
manager(ChildPid, Opts#view_opts{process = ProcOpts#process{interval = NewInterval}});
_ -> manager(ChildPid, Opts)
end.
render_worker(Interval, ProcessPid, TimeRef, OldRedus, OldMems) ->
ProcessInfo = recon:info(ProcessPid),
Meta = proplists:get_value(meta, ProcessInfo),
RegisteredName = proplists:get_value(registered_name, Meta),
Dictionary = proplists:get_value(dictionary, Meta),
GroupLeader = proplists:get_value(group_leader, Meta),
Status = proplists:get_value(status, Meta),
Signals = proplists:get_value(signals, ProcessInfo),
Link = proplists:get_value(links, Signals),
Monitors = proplists:get_value(monitors, Signals),
MonitoredBy = proplists:get_value(monitored_by, Signals),
TrapExit = proplists:get_value(trap_exit, Signals),
Location = proplists:get_value(location, ProcessInfo),
InitialCall = proplists:get_value(initial_call, Location),
CurrentStacktrace = proplists:get_value(current_stacktrace, Location),
MemoryUsed = proplists:get_value(memory_used, ProcessInfo),
Memory = proplists:get_value(memory, MemoryUsed),
MessageQueueLen = proplists:get_value(message_queue_len, MemoryUsed),
HeapSize = proplists:get_value(heap_size, MemoryUsed),
TotalHeapSize = proplists:get_value(total_heap_size, MemoryUsed),
GarbageCollection = proplists:get_value(garbage_collection, MemoryUsed),
Work = proplists:get_value(work, ProcessInfo),
Reductions = proplists:get_value(reductions, Work),
Line1 = render_line(RegisteredName, GroupLeader, Status, TrapExit, InitialCall,
MessageQueueLen, HeapSize, TotalHeapSize, GarbageCollection),
{NewRedus, NewMems, Line2} = render_reductions_memory(Reductions, Memory, OldRedus, OldMems),
Line3 = render_pid_info(ProcessPid, Dictionary, Link, Monitors, MonitoredBy, CurrentStacktrace),
Line4 = render_last_line(Interval),
redraw_screen_to_blank(),
?output([?CURSOR_TOP, Line1, Line2, Line3, Line4]),
NewTimeRef = observer_cli_lib:next_redraw(TimeRef, Interval),
receive
redraw -> render_worker(Interval, ProcessPid, NewTimeRef, NewRedus, NewMems);
{new_interval, NewInterval} -> render_worker(NewInterval, ProcessPid, NewTimeRef, NewRedus, NewMems);
quit -> quit
end.
redraw_screen_to_blank() ->
?output(?CURSOR_TOP),
BlankLine = lists:concat(lists:duplicate(?COLUMN, " ")),
[begin io:format(BlankLine) end || _Q <- lists:seq(1, 35)].
render_line(RegisteredName, GroupLeader, Status, TrapExit, InitialCall,
MessageQueueLen, HeapSize, TotalHeapSize, GarbageCollection) ->
MinBinVHeapSize = integer_to_list(proplists:get_value(min_bin_vheap_size, GarbageCollection)),
MinHeapSize = integer_to_list(proplists:get_value(min_heap_size, GarbageCollection)),
FullsweepAfter = integer_to_list(proplists:get_value(fullsweep_after, GarbageCollection)),
MinorGcs = integer_to_list(proplists:get_value(minor_gcs, GarbageCollection)),
InitialCallStr = observer_cli_lib:mfa_to_list(InitialCall),
MessageQueueLenStr = integer_to_list(MessageQueueLen),
HeapSizeStr = integer_to_list(HeapSize),
TotalHeapSizeStr = integer_to_list(TotalHeapSize),
GroupLeaderStr = erlang:pid_to_list(GroupLeader),
Format = "|~-15.15s|~52.52s|~17.17s|~12.12s|~18.18s|~12.12s|~n",
[io_lib:format("|\e[0m\e[7m~-15.15s|~-52.52s|~-17.17s|~-12.12s|~-18.18s|~-12.12s\e[0m|~n",
["Meta", "Value", "Memory Used", "Value", "Garbage Collection", "Value"]),
io_lib:format(Format, ["registered_name", RegisteredName, "message_queue_len",
MessageQueueLenStr, "min_bin_vheap_size", MinBinVHeapSize]),
io_lib:format(Format, ["initial_call", InitialCallStr, "heap_size", HeapSizeStr, "min_heap_size", MinHeapSize]),
io_lib:format(Format, ["group_leader", GroupLeaderStr, "total_heap_size", TotalHeapSizeStr, "fullsweep_after", FullsweepAfter]),
io_lib:format(Format, ["status", Status, "trap_exit", TrapExit, "minor_gcs", MinorGcs])].
render_pid_info(Pid, Dictionary, Link, Monitors, MonitoredBy, CurrentStacktrace) ->
PidStr = pid_to_list(Pid),
DictionaryStr = dict_to_str(Dictionary, 4),
LinkStr = pids_to_str(Link, 8),
MonitorsStr = pids_to_str(Monitors, 8),
MonitoredByStr = pids_to_str(MonitoredBy, 8),
CurrentStacktraceStr = stacktrace_to_str(CurrentStacktrace),
[io_lib:format("|Pid: ~-125s |~n", [PidStr]),
io_lib:format("|Dictionary: ~-119.119s|~n", [DictionaryStr]),
io_lib:format("|Link: ~-125.125s|~n", [LinkStr]),
io_lib:format("|Monitors: ~-121.121s|~n", [MonitorsStr]),
io_lib:format("|MonitoredBy: ~-118.118s|~n", [MonitoredByStr]),
io_lib:format("|CurrentStackTraces: ~p~n", [CurrentStacktraceStr])].
render_reductions_memory(Reduction, Memory, Reductions, Memorys) ->
{NewRedus, NewMems}
= case queue:len(Reductions) >= 20 of
true ->
RestRedus = queue:tail(Reductions),
RestMems = queue:tail(Memorys),
{queue:in(Reduction, RestRedus), queue:in(Memory, RestMems)};
false -> {queue:in(Reduction, Reductions), queue:in(Memory, Memorys)}
end,
View =
[
io_lib:format("|Reductions: ~119.119s|~n", [get_chart_format(NewRedus)]),
io_lib:format("|Memorys: ~122.122s|~n", [get_chart_format(NewMems)])
],
{NewRedus, NewMems, View}.
render_last_line(Interval) ->
Format =
case Interval >= 10000 of
true -> "|\e[31;1mINPUT: \e[0m\e[7mq(quit) b(back) i~w(refresh every ~wms) ~78.78s\e[0m~n";
false -> "|\e[31;1mINPUT: \e[0m\e[7mq(quit) b(back) i~w(refresh every ~wms) ~80.80s\e[0m~n"
end,
io_lib:format(Format, [Interval, Interval, ?render(observer_cli_lib:uptime())]).
get_chart_format(Queue) ->
List = queue:to_list(Queue),
chart_format(List, "").
chart_format([_R], Lines) -> Lines;
chart_format([R, R | RestRedus], Lines) ->
chart_format([R | RestRedus], Lines ++ observer_cli_lib:to_list(R) ++ "==>");
chart_format([R1, R2 | RestRedus], Lines) when R1 > R2 ->
chart_format([R2 | RestRedus], Lines ++ observer_cli_lib:to_list(R1) ++ "==>");
chart_format([R1, R2 | RestRedus], Lines) when R1 < R2 ->
chart_format([R2 | RestRedus], Lines ++ observer_cli_lib:to_list(R1) ++ "==>").
dict_to_str(Dicts, 10) -> lists:flatten(io_lib:format("~P", [Dicts, 10]));
dict_to_str(Dicts, Len) ->
Str = lists:flatten(io_lib:format("~P", [Dicts, Len])),
case length(Str) >= ?COLUMN of
true -> string:sub_string(Str, 1, ?COLUMN - 7) ++ ".....";
false -> dict_to_str(Dicts, Len + 1)
end.
pids_to_str(Link, 13) -> lists:flatten(io_lib:format("~P", [Link, 13]));
pids_to_str(Link, Len) ->
Str = lists:flatten(io_lib:format("~P", [Link, Len])),
case length(Str) >= ?COLUMN of
true -> string:sub_string(Str, 1, ?COLUMN - 7) ++ ".....";
false -> dict_to_str(Link, Len + 1)
end.
stacktrace_to_str(CurrentStacktrace) ->
CurrentStacktrace.