diff options
author | Dan Gudmundsson <dgud@erlang.org> | 2022-01-21 09:15:24 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2022-01-21 09:15:24 +0100 |
commit | 32ac4b182d098515338cbc60ff96954c34e6356a (patch) | |
tree | 582ee46de7430fbd016649503dcc519ef397a26b /lib | |
parent | 57261ba34b97190a6a75707c596881b0f2aeed1e (diff) | |
parent | 1a4bfb2153b9bfcd9c923e25ed697ebf64000d0e (diff) | |
download | erlang-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.erl | 161 |
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 -> ""; |