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      TIME    CPU 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 -0000      1.61
+++ display.c   14 Apr 2020 19:55:33 -0000
@@ -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();
                }
@@ -438,18 +472,22 @@ 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);
+                       char *cputag = cpustates_tag(cpu);
+                       addstrp(cputag);
+                       if (!cpu_bars) {
+                               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 {
+                               draw_cpubar(states, screen_width - 
strlen(cputag), 1);
                        }
                        putn();
                        cpu_line++;
Index: display.h
===================================================================
RCS file: /cvs/src/usr.bin/top/display.h,v
retrieving revision 1.15
diff -u -p -r1.15 display.h
--- display.h   17 Nov 2018 23:10:08 -0000      1.15
+++ display.h   14 Apr 2020 19:55:33 -0000
@@ -40,6 +40,7 @@ void u_loadave(int, double *);
 void i_timeofday(time_t *);
 void i_procstates(int, int *, int);
 void u_procstates(int, int *);
+void draw_cpubar(long long[], int, int);
 void i_cpustates(int64_t *, int *);
 void u_cpustates(int64_t *);
 void i_memory(int *);
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 -0000       1.73
+++ top.1       14 Apr 2020 19:55:33 -0000
@@ -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
 Show command line arguments
 as well as the process itself.
@@ -295,6 +297,10 @@ Toggle the display of per CPU or combine
 Scroll up/down the process list by one line.
 .It \&( | \&)
 Scroll up/down the process list by screen half.
+.It B
+Toggle the display of CPU utilisation bars.
+Bars are displayed in the same format as the vmstat view in
+.Xr systat 1 .
 .It C
 Toggle the display of process command line arguments.
 .It d Ar count
Index: top.c
===================================================================
RCS file: /cvs/src/usr.bin/top/top.c,v
retrieving revision 1.102
diff -u -p -r1.102 top.c
--- top.c       6 Jan 2020 20:05:10 -0000       1.102
+++ top.c       14 Apr 2020 19:55:33 -0000
@@ -72,6 +72,7 @@ static int    skip;           /* how many processes 
 
 extern int ncpu;
 extern int ncpuonline;
+extern int cpu_bars;
 
 extern int     (*proc_compares[])(const void *, const void *);
 int order_index;
@@ -131,6 +132,7 @@ struct statics  statics;
 #define CMD_up         25
 #define CMD_pagedown   26
 #define CMD_pageup     27
+#define CMD_bars       28
 
 static void
 usage(void)
@@ -212,11 +214,14 @@ parseargs(int ac, char **av)
        char *endp;
        int i;
 
-       while ((i = getopt(ac, av, "1SHICbinqus:d:p:U:o:g:")) != -1) {
+       while ((i = getopt(ac, av, "1SHICbinqus:d:p:U:o:g:B")) != -1) {
                switch (i) {
                case '1':
                        combine_cpus = 1;
                        break;
+               case 'B':
+                       cpu_bars = true;
+                       break;
                case 'C':
                        show_args = true;
                        break;
@@ -632,7 +637,7 @@ rundisplay(void)
        char ch, *iptr;
        int change, i;
        struct pollfd pfd[1];
-       static char command_chars[] = "\f qh?en#sdkriIuSopCHg+P109)(";
+       static char command_chars[] = "\f qh?en#sdkriIuSopCHg+P109)(B";
 
        /*
         * assume valid command unless told
@@ -995,6 +1000,9 @@ rundisplay(void)
                        skip -= max_topn / 2;
                        if (skip < 0)
                                skip = 0;
+                       break;
+               case CMD_bars:
+                       cpu_bars = !cpu_bars;
                        break;
                default:
                        new_message(MT_standout, " BAD CASE IN SWITCH!");


-- 
Best Regards
Edd Barrett

http://www.theunixzoo.co.uk

Reply via email to