Re: cpu utilisation bars for top(1)

2020-04-14 Thread Edd Barrett
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)

2020-04-13 Thread Leo Unglaub

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)

2020-04-13 Thread Stuart Henderson
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)

2020-04-13 Thread Theo de Raadt
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)

2020-04-13 Thread Edd Barrett
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)

2020-04-13 Thread Theo de Raadt
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)

2020-04-13 Thread Edd Barrett
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)

2020-04-13 Thread Edd Barrett
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)

2020-04-13 Thread Theo de Raadt
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)

2020-04-13 Thread Theo de Raadt
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)

2020-04-13 Thread Edd Barrett
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)

2020-04-13 Thread Theo de Raadt
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)

2020-04-13 Thread Sebastian Benoit
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)

2020-04-13 Thread 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.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