]> granicus.if.org Git - procps-ng/commitdiff
top: add graphs modes for cpu and memory, program code
authorJim Warner <james.warner@comcast.net>
Wed, 18 Jun 2014 05:00:00 +0000 (00:00 -0500)
committerCraig Small <csmall@enc.com.au>
Sun, 22 Jun 2014 11:39:55 +0000 (21:39 +1000)
This patch makes 't' (View_STATES) & 'm' (View_MEMORY)
commands into 4-way toggles. The two new modes provide
for two different graphs of the cpu and/or memory use.

These new capabilities are similar to those offered by
the 'htop' program. However they're aesthetically more
pleasing (to me) plus the scalings are more authentic.

Poor ol' top has long been troubled by the comparisons
offered up by the 'htop' program. Many of those things
were only true of the original redhat top while others
are no longer true of this current top program. So let
me use this commit msg to begin to correct the record.

Corrected comparisons between 'htop' & 'top' programs:
------------------------------------------------------
+ htop does not start faster, actually reverse is true
+ top offers scrolling vertically and horizontally too
. (and top offers better <Home> and <End> key support)
+ unassigned keystrokes don't subject top to any delay
. (but htop suffers that annoying ncurses <Esc> delay)
+ in top one need not type the PID to kill the process
+ in top one need not type the PID to renice a process

Some things the 'htop' program was not bragging about:
------------------------------------------------------
+ top can outperform the htop program by a wide margin
+ htop + SIGWINCH = corrupted display + restart likely
+ htop cannot preserve its screen data at suspend/exit
+ the htop column management scheme is very cumbersome
+ htop allows columns to be duplicated again and again
+ htop displays only full command lines, not pgm names
. (and that 'Command' column must always be displayed)
. (and it must always remain as the last column shown)
+ htop does not provide for any sort of command recall
+ htop's search feature does not highlight any matches
+ there is no 'find next' outside of htop search modes
+ htop does not allow Header or Process memory scaling
+ htop provides no flexibility on column justification
+ htop does not provide the means to change col widths
+ htop provides less control over colors configuration
+ htop always overwrites the rcfile with any UI change

Someday, maybe we'll provide a better comparison as an
addendum for (or replacement of) that README.top file.

Signed-off-by: Jim Warner <james.warner@comcast.net>
top/top.c
top/top.h

index 0fece2d10129468fe9f4f4ccd141e8e29ec2f6c4..83dad82a9f0f0f15bfc7e41d49fa5427c8badffa 100644 (file)
--- a/top/top.c
+++ b/top/top.c
@@ -235,6 +235,11 @@ static int (*Numa_max_node)(void);
 static int (*Numa_node_of_cpu)(int num);
 #endif
 #endif
+
+        /* Support for Graphing of the View_STATES ('t') and View_MEMORY ('m')
+           commands -- which are now both 4-way toggles */
+static const char Graph_blks[] = "                                                                                                    ";
+static const char Graph_bars[] = "||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||";
 \f
 /*######  Sort callbacks  ################################################*/
 
@@ -3423,7 +3428,8 @@ static int config_cvt (WIN_t *q) {
          *     line a: contains w->winname, fieldscur
          *     line b: contains w->winflags, sortindx, maxtasks
          *     line c: contains w->summclr, msgsclr, headclr, taskclr
-         *   line 15 : Fixed_widest, Summ_mscale, Task_mscale, Zero_suppress */
+         *   line 15 : miscellaneous additional global settings
+         *   Any remaining lines are devoted to the 'Inspect Other' feature */
 static void configs_read (void) {
    float tmp_delay = DEF_DELAY;
    char fbuf[LRGBUFSIZ];
@@ -3505,8 +3511,9 @@ static void configs_read (void) {
       } // end: for (GROUPSMAX)
 
       // any new addition(s) last, for older rcfiles compatibility...
-      if (fscanf(fp, "Fixed_widest=%d, Summ_mscale=%d, Task_mscale=%d, Zero_suppress=%d\n"
-         , &Rc.fixed_widest, &Rc.summ_mscale, &Rc.task_mscale, &Rc.zero_suppress))
+      if (fscanf(fp, "Fixed_widest=%d, Summ_mscale=%d, Task_mscale=%d, Zero_suppress=%d, Graph_cpus=%d, Graph_mems=%d\n"
+         , &Rc.fixed_widest, &Rc.summ_mscale, &Rc.task_mscale, &Rc.zero_suppress
+         , &Rc.graph_cpus, &Rc.graph_mems))
             ;                                  // avoid -Wunused-result
 
 try_inspect_entries:
@@ -4317,8 +4324,9 @@ static void write_rcfile (void) {
    }
 
    // any new addition(s) last, for older rcfiles compatibility...
-   fprintf(fp, "Fixed_widest=%d, Summ_mscale=%d, Task_mscale=%d, Zero_suppress=%d\n"
-      , Rc.fixed_widest, Rc.summ_mscale, Rc.task_mscale, Rc.zero_suppress);
+   fprintf(fp, "Fixed_widest=%d, Summ_mscale=%d, Task_mscale=%d, Zero_suppress=%d, Graph_cpus=%d, Graph_mems=%d\n"
+      , Rc.fixed_widest, Rc.summ_mscale, Rc.task_mscale, Rc.zero_suppress
+      , Rc.graph_cpus, Rc.graph_mems);
 
    if (Inspect.raw)
       fputs(Inspect.raw, fp);
@@ -4500,10 +4508,20 @@ static void keys_summary (int ch) {
          TOGw(w, View_LOADAV);
          break;
       case 'm':
-         TOGw(w, View_MEMORY);
+         if (!CHKw(w, View_MEMORY))
+            SETw(w, View_MEMORY);
+         else if (++Rc.graph_mems > 2) {
+            Rc.graph_mems = 0;;
+            OFFw(w, View_MEMORY);
+         }
          break;
       case 't':
-         TOGw(w, View_STATES);
+         if (!CHKw(w, View_STATES))
+            SETw(w, View_STATES);
+         else if (++Rc.graph_cpus > 2) {
+            Rc.graph_cpus = 0;;
+            OFFw(w, View_STATES);
+         }
          break;
       default:                    // keep gcc happy
          break;
@@ -5038,11 +5056,33 @@ static void summary_hlp (CPU_t *cpu, const char *pfx) {
 
    /* display some kinda' cpu state percentages
       (who or what is explained by the passed prefix) */
-   show_special(0, fmtmk(Cpu_States_fmts, pfx
-      , (float)u_frme * scale, (float)s_frme * scale
-      , (float)n_frme * scale, (float)i_frme * scale
-      , (float)w_frme * scale, (float)x_frme * scale
-      , (float)y_frme * scale, (float)z_frme * scale));
+   if (Rc.graph_cpus) {
+      static struct {
+         const char *user; const char *syst; const char *type;
+      } gtab[] = {
+         { "%-.*s~7", "%-.*s~8", Graph_bars },
+         { "%-.*s~4", "%-.*s~6", Graph_blks }
+      };
+      char graph_user[SMLBUFSIZ], graph_syst[SMLBUFSIZ], graph_dual[MEDBUFSIZ];
+      int ix = Rc.graph_cpus - 1;
+      float percent_user = (float)(u_frme + n_frme) * scale,
+            percent_syst = (float)s_frme * scale;
+      snprintf(graph_user, sizeof(graph_user), gtab[ix].user, (int)(percent_user + .5), gtab[ix].type);
+      snprintf(graph_syst, sizeof(graph_syst), gtab[ix].syst, (int)(percent_syst + .5), gtab[ix].type);
+      snprintf(graph_dual, sizeof(graph_dual), "%s%s", graph_user, graph_syst);
+#ifdef GRAPHS_ALIGN
+      show_special(0, fmtmk("%%%s ~3%#5.1f~2/%-#8.1f~3 [~1%-104.104s]~1\n"
+#else
+      show_special(0, fmtmk("%%%s ~3%#5.1f~2/%-#5.1f~3 [~1%-104.104s]~1\n"
+#endif
+         , pfx, percent_user, percent_syst, graph_dual));
+   } else {
+      show_special(0, fmtmk(Cpu_States_fmts, pfx
+         , (float)u_frme * scale, (float)s_frme * scale
+         , (float)n_frme * scale, (float)i_frme * scale
+         , (float)w_frme * scale, (float)x_frme * scale
+         , (float)y_frme * scale, (float)z_frme * scale));
+   }
  #undef TRIMz
 } // end: summary_hlp
 
@@ -5147,7 +5187,7 @@ numa_nope:
          const char *fmts;
          const char *label;
       } scaletab[] = {
-         { 1, "%8.0f ", NULL },                            // kibibytes
+         { 1, "%1.0f ", NULL },                            // kibibytes
          { 1024.0, "%#4.3f ", NULL },                      // mebibytes
          { 1024.0*1024, "%#4.3f ", NULL },                 // gibibytes
          { 1024.0*1024*1024, "%#4.3f ", NULL },            // tebibytes
@@ -5171,15 +5211,43 @@ numa_nope:
       }
       kb_main_my_used = kb_main_used - kb_main_buffers - kb_main_cached;
 
-      prT(bfT(0), mkM(total));   prT(bfT(1), mkM(free));
-      prT(bfT(2), mkM(my_used)); prT(bfT(3), mkM(buffers));
-      prT(bfT(4), mkS(total));   prT(bfT(5), mkS(free));
-      prT(bfT(6), mkS(used));    prT(bfT(7), mkM(cached));
-
-      show_special(0, fmtmk(N_unq(MEMORY_lines_fmt)
-         , scT(label), N_txt(WORD_abv_mem_txt), bfT(0), bfT(1), bfT(2), bfT(3)
-         , scT(label), N_txt(WORD_abv_swp_txt), bfT(4), bfT(5), bfT(6), bfT(7)
-         , N_txt(WORD_abv_mem_txt)));
+      if (Rc.graph_mems) {
+         static struct {
+            const char *used; const char *misc; const char *swap; const char *type;
+         } gtab[] = {
+            { "%-.*s~7", "%-.*s~8", "%-.*s~8", Graph_bars },
+            { "%-.*s~4", "%-.*s~6", "%-.*s~6", Graph_blks }
+         };
+         char graph_used[SMLBUFSIZ], graph_util[SMLBUFSIZ], graph_dual[MEDBUFSIZ];
+         int ix = Rc.graph_mems - 1;
+         float percent_used = (float)kb_main_my_used * (100.0 / (float)kb_main_total),
+               percent_misc = (float)(kb_main_buffers + kb_main_cached) * (100.0 / (float)kb_main_total),
+               percent_swap = (float)kb_swap_used * (100.0 / (float)kb_swap_total);
+         snprintf(graph_used, sizeof(graph_used), gtab[ix].used, (int)(percent_used + .5), gtab[ix].type);
+         snprintf(graph_util, sizeof(graph_util), gtab[ix].misc, (int)(percent_misc + .5), gtab[ix].type);
+         snprintf(graph_dual, sizeof(graph_dual), "%s%s", graph_used, graph_util);
+         snprintf(graph_util, sizeof(graph_util), gtab[ix].swap, (int)(percent_swap + .5), gtab[ix].type);
+         prT(bfT(0), mkM(total)); prT(bfT(1), mkS(total));
+         show_special(0, fmtmk(
+#ifdef GRAPHS_ALIGN
+         /* note: without this define, cpu and memory graphs can usually be aligned via scaling
+                  (the 'E' command) and without any waisted space preceeding the cpu graphs */
+            "%s %s:~3%#5.1f~2/%-9.9s~3[~1%-104.104s]~1\n%s %s:~3%#5.1f~2/%-9.9s~3[~1%-102.102s]~1\n"
+#else
+            "%s %s:~3%#5.1f~2/%-.9s~3[~1%-104.104s]~1\n%s %s:~3%#5.1f~2/%-.9s~3[~1%-102.102s]~1\n"
+#endif
+            , scT(label), N_txt(WORD_abv_mem_txt), percent_used + percent_misc, bfT(0), graph_dual
+            , scT(label), N_txt(WORD_abv_swp_txt), percent_swap, bfT(1), graph_util));
+      } else {
+         prT(bfT(0), mkM(total));   prT(bfT(1), mkM(free));
+         prT(bfT(2), mkM(my_used)); prT(bfT(3), mkM(buffers));
+         prT(bfT(4), mkS(total));   prT(bfT(5), mkS(free));
+         prT(bfT(6), mkS(used));    prT(bfT(7), mkM(cached));
+         show_special(0, fmtmk(N_unq(MEMORY_lines_fmt)
+            , scT(label), N_txt(WORD_abv_mem_txt), bfT(0), bfT(1), bfT(2), bfT(3)
+            , scT(label), N_txt(WORD_abv_swp_txt), bfT(4), bfT(5), bfT(6), bfT(7)
+            , N_txt(WORD_abv_mem_txt)));
+      }
       Msg_row += 2;
     #undef bfT
     #undef scT
index 836b9fb9035534533a0b49ee8b49a0c209703b46..5b96263cf1b2699c9a233280e687c587bbc0ad21 100644 (file)
--- a/top/top.h
+++ b/top/top.h
@@ -36,6 +36,7 @@
 //#define CASEUP_SUFIX            /* show time/mem/cnts suffix in upper case */
 //#define CPU_ZEROTICS            /* tolerate few tics when cpu off vs. idle */
 //#define EQUCOLHDRYES            /* yes, do equalize column header lengths  */
+//#define GRAPHS_ALIGN            /* force cpu & memory graphs to be aligned */
 //#define INSP_JUSTNOT            /* don't smooth unprintable right margins  */
 //#define INSP_OFFDEMO            /* disable demo screens, issue msg instead */
 //#define INSP_SAVEBUF            /* preserve 'Insp_buf' contents in a file  */
@@ -208,7 +209,7 @@ enum pflag {
 
         /* The scaling 'target' used with memory fields */
 enum scale_enum {
-   SK_Kb, SK_Mb, SK_Gb, SK_Tb, SK_Pb, SK_Eb, SK_SENTINEL
+   SK_Kb, SK_Mb, SK_Gb, SK_Tb, SK_Pb, SK_Eb
 };
 
         /* Used to manipulate (and document) the Frames_signal states */
@@ -366,6 +367,8 @@ typedef struct RCF_t {
    int    summ_mscale;          // 'E' - scaling of summary memory values
    int    task_mscale;          // 'e' - scaling of process memory values
    int    zero_suppress;        // '0' - suppress scaled zeros toggle
+   int    graph_cpus;           // 't' - View_STATES supplementary values
+   int    graph_mems;           // 'm' - View_MEMORY supplememtary values
 } RCF_t;
 
         /* This structure stores configurable information for each window.
@@ -610,7 +613,7 @@ typedef struct WIN_t {
    { EU_UEN, DEF_WINFLGS, 0, \
       COLOR_YELLOW, COLOR_YELLOW, COLOR_GREEN, COLOR_YELLOW, \
       "Usr", USR_FIELDS } \
-   }, 0, SK_Kb, SK_Kb, 0 }
+   }, 0, SK_Kb, SK_Kb, 0, 0, 0 }
 
         /* Summary Lines specially formatted string(s) --
            see 'show_special' for syntax details + other cautions. */