summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
authorDan Gudmundsson <dgud@erlang.org>2022-01-21 09:15:24 +0100
committerGitHub <noreply@github.com>2022-01-21 09:15:24 +0100
commit32ac4b182d098515338cbc60ff96954c34e6356a (patch)
tree582ee46de7430fbd016649503dcc519ef397a26b /lib
parent57261ba34b97190a6a75707c596881b0f2aeed1e (diff)
parent1a4bfb2153b9bfcd9c923e25ed697ebf64000d0e (diff)
downloaderlang-32ac4b182d098515338cbc60ff96954c34e6356a.tar.gz
Merge pull request #5557 from avtobiff/etop-dynamic-width
observer/etop: Calculate field width with available columns OTP-17880
Diffstat (limited to 'lib')
-rw-r--r--lib/observer/src/etop_txt.erl161
1 files changed, 143 insertions, 18 deletions
diff --git a/lib/observer/src/etop_txt.erl b/lib/observer/src/etop_txt.erl
index cd3ec62c13..81680e9df8 100644
--- a/lib/observer/src/etop_txt.erl
+++ b/lib/observer/src/etop_txt.erl
@@ -27,6 +27,16 @@
-include("etop.hrl").
-include("etop_defs.hrl").
+-define(DEFAULT_WIDTH, 89).
+
+-record(field_widths, {cols :: pos_integer(),
+ used_cols :: pos_integer(),
+ init_func :: pos_integer(),
+ reds :: pos_integer(),
+ mem :: pos_integer(),
+ msgq :: pos_integer(),
+ curr_func :: pos_integer()}).
+
-import(etop,[loadinfo/2,meminfo/2]).
stop(Pid) -> Pid ! stop.
@@ -49,8 +59,9 @@ do_update(Prev,Config) ->
do_update(Fd,Info,Prev,Config) ->
{Cpu,NProcs,RQ,Clock} = loadinfo(Info,Prev),
+ FieldWidths = calc_field_widths(Info#etop_info.procinfo),
io:nl(Fd),
- writedoubleline(Fd),
+ writedoubleline(Fd, FieldWidths),
case Info#etop_info.memi of
undefined ->
io:fwrite(Fd, " ~-72w~10s~n"
@@ -69,21 +80,124 @@ do_update(Fd,Info,Prev,Config) ->
RQ,Atom,Ets])
end,
io:nl(Fd),
- writepinfo_header(Fd),
- writesingleline(Fd),
- writepinfo(Fd,Info#etop_info.procinfo,modifier(Fd)),
- writedoubleline(Fd),
+ writepinfo_header(Fd, FieldWidths),
+ writesingleline(Fd, FieldWidths),
+ writepinfo(Fd, Info#etop_info.procinfo, modifier(Fd), FieldWidths),
+ writedoubleline(Fd, FieldWidths),
io:nl(Fd),
Info.
-writepinfo_header(Fd) ->
- io:fwrite(Fd,"Pid Name or Initial Func Time Reds Memory MsgQ Current Function~n",[]).
-writesingleline(Fd) ->
- io:fwrite(Fd,"----------------------------------------------------------------------------------------~n",[]).
-writedoubleline(Fd) ->
- io:fwrite(Fd,"========================================================================================~n",[]).
-
+calc_field_widths(ProcInfoL) ->
+ Cols = case io:columns() of
+ {ok, IoCols} -> max(IoCols, ?DEFAULT_WIDTH);
+ {error, enotsup} -> ?DEFAULT_WIDTH
+ end,
+
+ %% There is a priority with the field calculation since it's done
+ %% sequential with all fields deducted with their default values from
+ %% start. If any field eventually exceed the column budget, the rest of the
+ %% columns will get their default widths.
+
+ %% Calculate columns left. NOTE: The extra spaces between reds/mem/msgq.
+ %% Subsequent calculations adds default width then deducts calculated width.
+ %% See proc_format/2 for fields format.
+ %% Columns: pid, init_func, time, reds, mem, msgq, curr_func.
+ ColsLeft0 = Cols - 15 - 20 - 8 - 8 - 1 - 8 - 1 - 8 - 20,
+
+ RedsWidth = get_width(reds, ProcInfoL, ColsLeft0),
+
+ ColsLeft1 = ColsLeft0 + 8 - RedsWidth,
+
+ MemWidth = get_width(mem, ProcInfoL, ColsLeft1),
+
+ ColsLeft2 = ColsLeft1 + 8 - MemWidth,
+
+ MsgQWidth= get_width(msgq, ProcInfoL, ColsLeft2),
+
+ ColsLeft3 = ColsLeft2 + 8 - MsgQWidth,
+
+ %% Use the rest for initial and current function fields
+ if ColsLeft3 > 0 ->
+ %% compensate field start width for rounding
+ FieldSize = 19 + round((ColsLeft3 - 1) / 2),
+ InitFuncWidth = FieldSize,
+ CurrFuncWidth = FieldSize;
+ true ->
+ InitFuncWidth = 20,
+ CurrFuncWidth = 20
+ end,
+
+ %% Extra space between reds/mem, extra space due to rounding
+ UsedCols =
+ 15 + InitFuncWidth + 8 + RedsWidth + 1 + MemWidth + 1
+ + MsgQWidth + CurrFuncWidth + 1,
+
+ #field_widths{cols = Cols,
+ used_cols = UsedCols,
+ init_func = InitFuncWidth,
+ reds = RedsWidth,
+ mem = MemWidth,
+ msgq = MsgQWidth,
+ curr_func = CurrFuncWidth}.
+
+
+get_width(reds, ProcInfoL, ColsLeft) ->
+ get_width(4, ProcInfoL, ColsLeft);
+get_width(mem, ProcInfoL, ColsLeft) ->
+ get_width(3, ProcInfoL, ColsLeft);
+get_width(msgq, ProcInfoL, ColsLeft) ->
+ get_width(8, ProcInfoL, ColsLeft);
+
+%% Get the maximum width of the field at place N in #sysinfo{}.
+%% Calculate the maximum width by taking the maximum width (by taking common
+%% logarithm of largest number) then check if is larger than 8 and that it
+%% fits the column budget, if so return that value; otherwise return 8.
+get_width(N, ProcInfoL, ColsLeft) ->
+ MaxNum = lists:foldr(fun(Info, Acc)
+ when element(N, Info) > Acc ->
+ element(N, Info);
+ (_, Acc) ->
+ Acc
+ end,
+ 0,
+ ProcInfoL),
+
+ MaxWidth =
+ if MaxNum > 0 -> round(math:log10(MaxNum)) + 1;
+ true -> 1 %% logarithm defined in R_n, n > 0.
+ end,
+
+ if MaxWidth > 8 andalso ColsLeft - MaxWidth > 0 ->
+ MaxWidth;
+ true ->
+ 8
+ end.
+
+
+writepinfo_header(Fd, #field_widths{init_func = InitFunc, reds = Reds,
+ mem = Mem, msgq = MsgQ}) ->
+ %% Add spaces between variable width columns.
+ Header =
+ "Pid Name or Initial Func"
+ ++ lists:duplicate(max(InitFunc - 16, 4), $\s) ++
+ "Time"
+ ++ lists:duplicate(max(Reds - 4, 4), $\s) ++
+ "Reds"
+ ++ lists:duplicate(max(Mem - 5, 3), $\s) ++
+ "Memory"
+ ++ lists:duplicate(max(MsgQ - 3, 5), $\s) ++
+ "MsgQ Current Function\n",
+
+ io:fwrite(Fd, Header, []).
+
+writesingleline(Fd, FieldWidths) -> writedupline(Fd, $-, FieldWidths).
+writedoubleline(Fd, FieldWidths) -> writedupline(Fd, $=, FieldWidths).
+
+writedupline(Fd, Char, #field_widths{used_cols = UsedCols}) ->
+ Line = lists:duplicate(UsedCols, Char) ++ "\n",
+ io:fwrite(Fd, Line, []).
+
writepinfo(Fd,[#etop_proc_info{pid=Pid,
mem=Mem,
reds=Reds,
@@ -92,22 +206,33 @@ writepinfo(Fd,[#etop_proc_info{pid=Pid,
cf=MFA,
mq=MQ}
|T],
- Modifier) ->
- io:fwrite(Fd,proc_format(Modifier),
+ Modifier, FieldWidths) ->
+ io:fwrite(Fd,proc_format(Modifier, FieldWidths),
[Pid,to_string(Name,Modifier),Time,Reds,Mem,MQ,
to_string(MFA,Modifier)]),
- writepinfo(Fd,T,Modifier);
-writepinfo(_Fd,[],_) ->
+ writepinfo(Fd,T,Modifier, FieldWidths);
+writepinfo(_Fd,[],_,_) ->
ok.
-proc_format(Modifier) ->
- "~-15w~-20"++Modifier++"s~8w~8w~8w~8w ~-20"++Modifier++"s~n".
+proc_format(Modifier, #field_widths{init_func = InitFunc, reds = Reds,
+ mem = Mem, msgq = MsgQ,
+ curr_func = CurrFunc}) ->
+ "~-15w"
+ "~-" ++ i2l(InitFunc) ++ Modifier ++ "s"
+ "~8w"
+ "~" ++ i2l(Reds) ++ "w "
+ "~" ++ i2l(Mem) ++"w "
+ "~" ++ i2l(MsgQ) ++ "w "
+ "~-" ++ i2l(CurrFunc) ++ Modifier ++ "s~n".
+
to_string({M,F,A},Modifier) ->
io_lib:format("~w:~"++Modifier++"w/~w",[M,F,A]);
to_string(Other,Modifier) ->
io_lib:format("~"++Modifier++"w",[Other]).
+i2l(I) -> integer_to_list(I).
+
modifier(Device) ->
case encoding(Device) of
latin1 -> "";