Re: cpu utilisation bars for top(1)
Hi, On Mon, Apr 13, 2020 at 07:18:34PM +0100, Stuart Henderson wrote: > It would make better use of the screen space if it worked like systat vm - > multiple characters | (interrupt), @ (spin), = (sys), > (user) ... this fits > a lot more information into the same space than just using a single char to > represent any type of cpu use. Ah, gotcha. Here's what I've done: - Use different symbols for different CPU states, like systat(1) does. - Made "combined" mode work. In doing so, put the code that draws the bars into a function. - There are now some long lines that are hard to wrap. I was tempted to factor out the code that writes the textual CPU states into its own function, as that'd reduce nesting and duplication. However, I've refrained fow now. Example output (with system under load -- compiling LLVM): ``` load averages: 5.49, 5.35, 5.06 arrakis.home 21:04:53 153 processes: 6 running, 142 idle, 1 dead, 4 on processor up 2 days, 7:49 CPU0: @>>> CPU1: @@@ CPU2: @>>> CPU3: @>>> Memory: Real: 2972M/16G act/tot Free: 7053M Cache: 11G Swap: 0K/1028M PID USERNAME PRI NICE SIZE RES STATE WAIT TIMECPU COMMAND ... ``` Diff follows, thanks. Index: display.c === RCS file: /cvs/src/usr.bin/top/display.c,v retrieving revision 1.61 diff -u -p -r1.61 display.c --- display.c 27 Oct 2019 13:52:26 - 1.61 +++ display.c 14 Apr 2020 19:55:33 - @@ -106,6 +106,11 @@ extern struct process_select ps; int header_status = true; +/* stuff for graphical display of CPU utilisation */ +int cpu_bars = false; +static char cpubar_chars[] = { '|', '@', '=', '>' }; +static char cpubar_order[] = { CP_INTR, CP_SPIN, CP_SYS, CP_USER }; + static int empty(void) { @@ -376,6 +381,30 @@ cpustates_tag(int cpu) } void +draw_cpubar(long long states[], int width, int num_cpus) +{ +#define NUM_CPUBAR_CHARS(V, NCPUS, PP) V / NCPUS / 10 * PP + int i, st, num_chars; + double pperc; + + width = width < 0 ? 0 : width; + pperc = width / 100.0; /* characters per-percent */ + + for (i = 0; i < sizeof(cpubar_order) / sizeof(cpubar_order[0]); i++) { + st = cpubar_order[i]; + num_chars = NUM_CPUBAR_CHARS(states[st], num_cpus, pperc); + + /* CP_USER is merged with CP_NICE, like in systat(1) */ + if (st == CP_USER) + num_chars += NUM_CPUBAR_CHARS(states[CP_NICE], num_cpus, pperc); + + while (num_chars-- > 0) { + addch(cpubar_chars[i]); + } + } +} + +void i_cpustates(int64_t *ostates, int *online) { int i, first, cpu, cpu_line; @@ -384,7 +413,7 @@ i_cpustates(int64_t *ostates, int *onlin char **names, *thisname; if (combine_cpus) { - static double *values; + static long long *values; if (!values) { values = calloc(num_cpustates, sizeof(*values)); if (!values) @@ -412,14 +441,19 @@ i_cpustates(int64_t *ostates, int *onlin clrtoeol(); printwp("%-3d CPUs: ", ncpuonline); - while ((thisname = *names++) != NULL) { - if (*thisname != '\0') { - value = values[i++] / ncpuonline; - /* if percentage is >= 1000, print it as 100% */ - printwp((value >= 1000 ? "%s%4.0f%% %s" : - "%s%4.1f%% %s"), first++ == 0 ? "" : ", ", - value / 10., thisname); + if (!cpu_bars) { + while ((thisname = *names++) != NULL) { + if (*thisname != '\0') { + value = (double) values[i++] / ncpuonline; + /* if percentage is >= 1000, print it as 100% */ + printwp((value >= 1000 ? "%s%4.0f%% %s" : + "%s%4.1f%% %s"), first++ == 0 ? "" : ", ", + value / 10., thisname); + } } + } else { + /* "NNN CPUs: " is 10 characters */ + draw_cpubar(values, screen_width - 10, ncpuonline); } putn();
Re: cpu utilisation bars for top(1)
Hey, thanks for the diff. I gave it a try and i really like it. On a server with 32 cores this can be usefull to get a quick look at what is going on. As long as this is not the default and just a toggle it is very usefull for my usecase. Your diff works well on my system. Thanks and greeings Leo Am 13.04.2020 um 16:47 schrieb Edd Barrett: Hi, One thing I miss from our top(1) is the ability to see overall CPU utilisation at a glance (I usually scan for the idle percentage and invert it in my head). This diff adds a way to toggle (using `B`) CPU utilisation bars, like this: ``` load averages: 0.92, 0.52, 1.26 arrakis.home 15:31:14 110 processes: 108 idle, 1 stopped, 1 on processorup 1 day, 2:15 [] 37.1% [# ] 45.0% [] 42.8% [### ] 47.2% Memory: Real: 4399M/12G act/tot Free: 11G Cache: 6204M Swap: 0K/1028M ``` The value reported is the inverse of the idle percentage and the bars replace the 'user, nice, sys, ...' lines. Can also be enabled from the command line with `-B`. What do people think? (I know htop from ports does utilisation bars, but I prefer pretty much everything else about our in-base top.) Index: display.c === RCS file: /cvs/src/usr.bin/top/display.c,v retrieving revision 1.61 diff -u -p -r1.61 display.c --- display.c 27 Oct 2019 13:52:26 - 1.61 +++ display.c 13 Apr 2020 14:43:10 - @@ -105,6 +105,7 @@ extern int combine_cpus; extern struct process_select ps; int header_status = true; +int cpu_bars = false; static int empty(void) @@ -382,6 +383,13 @@ i_cpustates(int64_t *ostates, int *onlin double value; int64_t *states; char **names, *thisname; + char cpu_bar[MAX_COLS]; + int hash_space = screen_width - 8; /* space for hash chars in bars */ + + /* this is used as an offset into cpu_bar and must never be negative */ + if (hash_space < 0) { + hash_space = 0; + } if (combine_cpus) { static double *values; @@ -438,18 +446,30 @@ i_cpustates(int64_t *ostates, int *onlin if (screen_length > 2 + cpu_line || !smart_terminal) { move(2 + cpu_line, 0); clrtoeol(); - addstrp(cpustates_tag(cpu)); - - while ((thisname = *names++) != NULL) { - if (*thisname != '\0') { - /* retrieve the value and remember it */ - value = *states++; - - /* if percentage is >= 1000, print it as 100% */ - printwp((value >= 1000 ? "%s%4.0f%% %s" : - "%s%4.1f%% %s"), first++ == 0 ? "" : ", ", - value / 10., thisname); + if (!cpu_bars) { + addstrp(cpustates_tag(cpu)); + while ((thisname = *names++) != NULL) { + if (*thisname != '\0') { + /* retrieve the value and remember it */ + value = *states++; + + /* if percentage is >= 1000, print it as 100% */ + printwp((value >= 1000 ? "%s%4.0f%% %s" : +"%s%4.1f%% %s"), first++ == 0 ? "" : ", ", + value / 10., thisname); + } } + } else { + float idle_ratio = (states[5] >= 1000 ? 1000 : states[5]) / 1000.; + int n_idle_chars = hash_space * idle_ratio; + int n_busy_chars = hash_space - n_idle_chars; + + cpu_bar[0] = '['; + memset(cpu_bar + 1, '#', n_busy_chars); + memset(cpu_bar + 1 + n_busy_chars, ' ', n_idle_chars); + snprintf(cpu_bar + hash_space, 9, "] %5.1f%%", + 100 - idle_ratio * 100); + printwp("%s", cpu_bar); } putn(); cpu_line++; Index: top.1 === RCS file: /cvs/src/usr.bin/top/top
Re: cpu utilisation bars for top(1)
On 2020/04/13 18:27, Edd Barrett wrote: > On Mon, Apr 13, 2020 at 11:12:46AM -0600, Theo de Raadt wrote: > > > If this is going to be done, it should look the same. I'm not talking > > about the markers above it, but about your arbitrary use of #. > > So you mean the first proposal is good, just change # to >? > > Note that systat(1) uses both chevrons (first screen) and hashes (second > screen) for CPU bar graphs. It would make better use of the screen space if it worked like systat vm - multiple characters | (interrupt), @ (spin), = (sys), > (user) ... this fits a lot more information into the same space than just using a single char to represent any type of cpu use.
Re: cpu utilisation bars for top(1)
Edd Barrett wrote: > On Mon, Apr 13, 2020 at 11:40:21AM -0600, Theo de Raadt wrote: > > I don't like it. > > > > CPU00 states: 0.0% user, 0.0% nice, 0.0% sys, 0.0% spin, 18.2% intr, > > 81.8% idle > > CPU01 states: 0.0% user, 0.0% nice, 0.0% sys, 0.0% spin, 0.0% intr, > > 100% idle > > > > versus > > > > [] > > 37.1% > > [# ] > > 45.0% > > > > You have thrown information away. > > Just to be clear, I'm not proposing we change the default display. > > The bar graphs I'm proposing are only displayed when toggled on by > pressing `B` (or by invoking top(1) with `-B`). > > Otherwise the display remains exactly as it was before. An option which I believe approximately noone will use.
Re: cpu utilisation bars for top(1)
On Mon, Apr 13, 2020 at 11:40:21AM -0600, Theo de Raadt wrote: > I don't like it. > > CPU00 states: 0.0% user, 0.0% nice, 0.0% sys, 0.0% spin, 18.2% intr, > 81.8% idle > CPU01 states: 0.0% user, 0.0% nice, 0.0% sys, 0.0% spin, 0.0% intr, > 100% idle > > versus > > [] > 37.1% > [# ] > 45.0% > > You have thrown information away. Just to be clear, I'm not proposing we change the default display. The bar graphs I'm proposing are only displayed when toggled on by pressing `B` (or by invoking top(1) with `-B`). Otherwise the display remains exactly as it was before. -- Best Regards Edd Barrett http://www.theunixzoo.co.uk
Re: cpu utilisation bars for top(1)
Edd Barrett wrote: > On Mon, Apr 13, 2020 at 11:12:46AM -0600, Theo de Raadt wrote: > > > If this is going to be done, it should look the same. I'm not talking > > about the markers above it, but about your arbitrary use of #. > > So you mean the first proposal is good, just change # to >? I don't like it. CPU00 states: 0.0% user, 0.0% nice, 0.0% sys, 0.0% spin, 18.2% intr, 81.8% idle CPU01 states: 0.0% user, 0.0% nice, 0.0% sys, 0.0% spin, 0.0% intr, 100% idle versus [] 37.1% [# ] 45.0% You have thrown information away. I don't think any of this makes sense.
Re: cpu utilisation bars for top(1)
On Mon, Apr 13, 2020 at 11:15:59AM -0600, Theo de Raadt wrote: > I think your bar-graph removes detailed information and replaces it > with a visual which wastes screen real-estate. I agree, that's why I made it a toggle. -- Best Regards Edd Barrett http://www.theunixzoo.co.uk
Re: cpu utilisation bars for top(1)
On Mon, Apr 13, 2020 at 11:12:46AM -0600, Theo de Raadt wrote: > If this is going to be done, it should look the same. I'm not talking > about the markers above it, but about your arbitrary use of #. So you mean the first proposal is good, just change # to >? Note that systat(1) uses both chevrons (first screen) and hashes (second screen) for CPU bar graphs. -- Best Regards Edd Barrett http://www.theunixzoo.co.uk
Re: cpu utilisation bars for top(1)
Theo de Raadt wrote: > Edd Barrett wrote: > > > On Mon, Apr 13, 2020 at 06:26:53PM +0200, Sebastian Benoit wrote: > > > If you make them look like the display in systat, you still get the user, > > > ... nice, sys information. Also i like my bikeshed green. > > > > So looking at the bar in `systat vmstat`, we have: > > ``` > >0.0%Int 0.0%Spn 0.1%Sys 25.5%Usr 74.4%Idle > > ||||||||||| > > > > > ``` > > > > To replicate that exactly per-processor, we'd need 3 lines per-cpu. > > That is not what I am talking about. > > I am pointing out you were not aware of a previous display format, and > chose to make your own. > > If this is going to be done, it should look the same. I'm not talking > about the markers above it, but about your arbitrary use of #. > > Frankly I don't see any value in a bar-graph when the number is still > present. And I don't see value in a bar-graph without numbers. I think your bar-graph removes detailed information and replaces it with a visual which wastes screen real-estate.
Re: cpu utilisation bars for top(1)
Edd Barrett wrote: > On Mon, Apr 13, 2020 at 06:26:53PM +0200, Sebastian Benoit wrote: > > If you make them look like the display in systat, you still get the user, > > ... nice, sys information. Also i like my bikeshed green. > > So looking at the bar in `systat vmstat`, we have: > ``` >0.0%Int 0.0%Spn 0.1%Sys 25.5%Usr 74.4%Idle > ||||||||||| > > > ``` > > To replicate that exactly per-processor, we'd need 3 lines per-cpu. That is not what I am talking about. I am pointing out you were not aware of a previous display format, and chose to make your own. If this is going to be done, it should look the same. I'm not talking about the markers above it, but about your arbitrary use of #. Frankly I don't see any value in a bar-graph when the number is still present. And I don't see value in a bar-graph without numbers.
Re: cpu utilisation bars for top(1)
On Mon, Apr 13, 2020 at 06:26:53PM +0200, Sebastian Benoit wrote: > If you make them look like the display in systat, you still get the user, > ... nice, sys information. Also i like my bikeshed green. So looking at the bar in `systat vmstat`, we have: ``` 0.0%Int 0.0%Spn 0.1%Sys 25.5%Usr 74.4%Idle ||||||||||| > ``` To replicate that exactly per-processor, we'd need 3 lines per-cpu. I don't think we want to steal too much screen real-estate from the actual process listing, but we could try some middle-ground like: - No 'int, spn, sys, ...' line. You can toggle the bars off to see those. - One shared line for the hatchings, either an additional line or perhaps replacing the process counts line. - No enclosing square braces on the bars. - No numeric percentage display. - Use > instead of #. So that might look like: ``` load averages: 1.13, 1.03, 0.65 arrakis.home 17:51:51 118 processes: 114 idle, 1 stopped, 3 on processorup 1 day, 4:36 | | | | | | | | | | | >> >>> >>> > Memory: Real: 3931M/12G act/tot Free: 11G Cache: 6507M Swap: 0K/1028M PID USERNAME PRI NICE SIZE RES STATE WAIT TIMECPU COMMAND ... ``` Something like that? -- Best Regards Edd Barrett http://www.theunixzoo.co.uk
Re: cpu utilisation bars for top(1)
Sebastian Benoit wrote: > Edd Barrett(e...@theunixzoo.co.uk) on 2020.04.13 15:47:03 +0100: > > Hi, > > > > One thing I miss from our top(1) is the ability to see overall CPU > > utilisation at a glance (I usually scan for the idle percentage and > > invert it in my head). > > > > This diff adds a way to toggle (using `B`) CPU utilisation bars, like this: > > > > ``` > > load averages: 0.92, 0.52, 1.26 arrakis.home > > 15:31:14 > > 110 processes: 108 idle, 1 stopped, 1 on processorup 1 day, > > 2:15 > > [] > > 37.1% > > [# ] > > 45.0% > > [] > > 42.8% > > [### ] > > 47.2% > > Memory: Real: 4399M/12G act/tot Free: 11G Cache: 6204M Swap: 0K/1028M > > ``` > > > > The value reported is the inverse of the idle percentage and the bars > > replace the 'user, nice, sys, ...' lines. > > > > Can also be enabled from the command line with `-B`. > > > > What do people think? > > If you make them look like the display in systat, you still get the user, > ... nice, sys information. Also i like my bikeshed green. But it is different from the display in systat vm 1. If this is going to be done, it should be as close as possible. And this isn't.
Re: cpu utilisation bars for top(1)
Edd Barrett(e...@theunixzoo.co.uk) on 2020.04.13 15:47:03 +0100: > Hi, > > One thing I miss from our top(1) is the ability to see overall CPU > utilisation at a glance (I usually scan for the idle percentage and > invert it in my head). > > This diff adds a way to toggle (using `B`) CPU utilisation bars, like this: > > ``` > load averages: 0.92, 0.52, 1.26 arrakis.home > 15:31:14 > 110 processes: 108 idle, 1 stopped, 1 on processorup 1 day, > 2:15 > [] > 37.1% > [# ] > 45.0% > [] > 42.8% > [### ] > 47.2% > Memory: Real: 4399M/12G act/tot Free: 11G Cache: 6204M Swap: 0K/1028M > ``` > > The value reported is the inverse of the idle percentage and the bars > replace the 'user, nice, sys, ...' lines. > > Can also be enabled from the command line with `-B`. > > What do people think? If you make them look like the display in systat, you still get the user, ... nice, sys information. Also i like my bikeshed green.
cpu utilisation bars for top(1)
Hi, One thing I miss from our top(1) is the ability to see overall CPU utilisation at a glance (I usually scan for the idle percentage and invert it in my head). This diff adds a way to toggle (using `B`) CPU utilisation bars, like this: ``` load averages: 0.92, 0.52, 1.26 arrakis.home 15:31:14 110 processes: 108 idle, 1 stopped, 1 on processorup 1 day, 2:15 [] 37.1% [# ] 45.0% [] 42.8% [### ] 47.2% Memory: Real: 4399M/12G act/tot Free: 11G Cache: 6204M Swap: 0K/1028M ``` The value reported is the inverse of the idle percentage and the bars replace the 'user, nice, sys, ...' lines. Can also be enabled from the command line with `-B`. What do people think? (I know htop from ports does utilisation bars, but I prefer pretty much everything else about our in-base top.) Index: display.c === RCS file: /cvs/src/usr.bin/top/display.c,v retrieving revision 1.61 diff -u -p -r1.61 display.c --- display.c 27 Oct 2019 13:52:26 - 1.61 +++ display.c 13 Apr 2020 14:43:10 - @@ -105,6 +105,7 @@ extern int combine_cpus; extern struct process_select ps; int header_status = true; +int cpu_bars = false; static int empty(void) @@ -382,6 +383,13 @@ i_cpustates(int64_t *ostates, int *onlin double value; int64_t *states; char **names, *thisname; + char cpu_bar[MAX_COLS]; + int hash_space = screen_width - 8; /* space for hash chars in bars */ + + /* this is used as an offset into cpu_bar and must never be negative */ + if (hash_space < 0) { + hash_space = 0; + } if (combine_cpus) { static double *values; @@ -438,18 +446,30 @@ i_cpustates(int64_t *ostates, int *onlin if (screen_length > 2 + cpu_line || !smart_terminal) { move(2 + cpu_line, 0); clrtoeol(); - addstrp(cpustates_tag(cpu)); - - while ((thisname = *names++) != NULL) { - if (*thisname != '\0') { - /* retrieve the value and remember it */ - value = *states++; - - /* if percentage is >= 1000, print it as 100% */ - printwp((value >= 1000 ? "%s%4.0f%% %s" : - "%s%4.1f%% %s"), first++ == 0 ? "" : ", ", - value / 10., thisname); + if (!cpu_bars) { + addstrp(cpustates_tag(cpu)); + while ((thisname = *names++) != NULL) { + if (*thisname != '\0') { + /* retrieve the value and remember it */ + value = *states++; + + /* if percentage is >= 1000, print it as 100% */ + printwp((value >= 1000 ? "%s%4.0f%% %s" : +"%s%4.1f%% %s"), first++ == 0 ? "" : ", ", + value / 10., thisname); + } } + } else { + float idle_ratio = (states[5] >= 1000 ? 1000 : states[5]) / 1000.; + int n_idle_chars = hash_space * idle_ratio; + int n_busy_chars = hash_space - n_idle_chars; + + cpu_bar[0] = '['; + memset(cpu_bar + 1, '#', n_busy_chars); + memset(cpu_bar + 1 + n_busy_chars, ' ', n_idle_chars); + snprintf(cpu_bar + hash_space, 9, "] %5.1f%%", + 100 - idle_ratio * 100); + printwp("%s", cpu_bar); } putn(); cpu_line++; Index: top.1 === RCS file: /cvs/src/usr.bin/top/top.1,v retrieving revision 1.73 diff -u -p -r1.73 top.1 --- top.1 7 Jan 2020 13:30:43 - 1.73 +++ top.1 13 Apr 2020 14:43:10 - @@ -90,6 +90,8 @@ and .Ql ^\e ) still have an effect. This is the default on a dumb terminal, or when the output is not a terminal. +.It Fl B +Show CPU utilisation bars instead of CPU states. .It Fl C