File 0850-observer-etop-Calculate-field-width-with-available-c.patch of Package erlang
From 1a4bfb2153b9bfcd9c923e25ed697ebf64000d0e Mon Sep 17 00:00:00 2001
From: Per Andersson <avtobiff@foo.nu>
Date: Wed, 29 Dec 2021 16:14:00 +0100
Subject: [PATCH] observer/etop: Calculate field width with available columns
The field widths are calculated depending on what the underlying io
driver reports as max column width.
The fields are allowed to use the column width they need if they column
budget allows it. The field widths are deducted in priority order and if
there is space left the calculated width is used otherwise the default
field width is used.
If the underlying io driver can't give any column width, the default
column width budget of 89 characters is used. This is the legacy hard
coded width, plus two spaces between reductions, memory, and msgq
fields.
Fixes #5080
Signed-off-by: Per Andersson <avtobiff@foo.nu>
---
lib/observer/src/etop_txt.erl | 161 ++++++++++++++++++++++++++++++----
1 file 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 -> "";
--
2.31.1