]> granicus.if.org Git - procps-ng/commitdiff
top: parent total cpu includes collapsed children, pgm
authorJim Warner <james.warner@comcast.net>
Mon, 25 Jun 2018 05:00:00 +0000 (00:00 -0500)
committerCraig Small <csmall@enc.com.au>
Tue, 17 Jul 2018 10:58:32 +0000 (20:58 +1000)
Now, when a parent's children have been collapsed, the
cpu used by those unseen tasks will disappear no more.
Instead such tics will be added to the parent's total.

[ if one wished a return to the 'land of lost tics', ]
[ the '#define TREE_VCPUOFF' directive is available. ]

------------------------------------------------------
Note: With collapsible parents now displaying children
cpu usage, it will eventually be noticed the cpu stats
for the summary area and task areas often vary widely.

It's worth a reminder that for top's summary area each
individual cpu and the cpu summary is limited to 100%,
regardless of how many tics a linux kernel may export.

An individual task is limited to 100% times the number
of threads. But, in no case will cpu usage ever exceed
100% times total number of processors. Such limits are
further reduced under 'Solaris' mode ('I' toggle off).
In this mode, a task cpu usage will never exceed 100%.
These limits will now also apply to collapsed parents.

In addition to those influences, results are subjected
to kernel timer sampling anomalies and the distortions
inherent in a small sample size, made worse by smaller
delay intervals. Often there is just 1 or 2 tics for a
few tasks at smaller intervals such as: 1/10th second.

Anyway, should questions on this subject arise, a good
starting point, beyond the reminders above, is the 1st
link listed below. Those other links were derivatives.

Reference(s):
. from the kernel documentation
https://www.kernel.org/doc/Documentation/cpu-load.txt
. as mentioned in the above kernel documentation
https://lkml.org/lkml/2007/2/12/6
. from above, with many more links on the subject
https://www.boblycat.org/~malc/apc/

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

index 848ef4aac15117b19b41137fec16a70636574b22..bc9536ed3c39076f19ec6b59ec1581fa1b76f653 100644 (file)
--- a/top/top.c
+++ b/top/top.c
@@ -1659,12 +1659,14 @@ static struct {
 #define eu_TIME_START  eu_LAST +3
 #define eu_ID_FUID     eu_LAST +4
 #define eu_LVL         eu_LAST +5
-#define eu_HID         eu_LAST +6
+#define eu_ADD         eu_LAST +6
+#define eu_HID         eu_LAST +7
    {          -1, -1, -1, -1,         PIDS_CMDLINE        },  // str      ( if Show_CMDLIN )
    {          -1, -1, -1, -1,         PIDS_TICS_ALL_C     },  // ull_int  ( if Show_CTIMES )
    {          -1, -1, -1, -1,         PIDS_TIME_START     },  // ull_int  ( if Show_FOREST )
    {          -1, -1, -1, -1,         PIDS_ID_FUID        },  // u_int    ( if a usrseltyp )
    {          -1, -1, -1, -1,         PIDS_extra          },  // u_int    ( if Show_FOREST )
+   {          -1, -1, -1, -1,         PIDS_extra          },  // u_int    ( if Show_FOREST )
    {          -1, -1, -1, -1,         PIDS_extra          }   // s_ch     ( if Show_FOREST )
  #undef A_left
  #undef A_right
@@ -1822,7 +1824,7 @@ static void build_headers (void) {
          // for 'busy' only processes, we'll need elapsed tics
          if (!CHKw(w, Show_IDLEPS)) ckITEM(EU_CPU);
          // with forest view mode, we'll need pid, tgid, ppid & start_time...
-         if (CHKw(w, Show_FOREST)) { ckITEM(EU_PPD); ckITEM(EU_TGD); ckITEM(eu_TIME_START); ckITEM(eu_LVL); ckITEM(eu_HID);}
+         if (CHKw(w, Show_FOREST)) { ckITEM(EU_PPD); ckITEM(EU_TGD); ckITEM(eu_TIME_START); ckITEM(eu_LVL); ckITEM(eu_ADD); ckITEM(eu_HID); }
          // for 'cumulative' times, we'll need equivalent of cutime & cstime
          if (Fieldstab[EU_TME].erel > -1 && CHKw(w, Show_CTIMES)) ckITEM(eu_TICS_ALL_C);
          if (Fieldstab[EU_TM2].erel > -1 && CHKw(w, Show_CTIMES)) ckITEM(eu_TICS_ALL_C);
@@ -4199,9 +4201,8 @@ static void wins_stage_2 (void) {
 
 
         /*
-         * Determine if this task matches the 'u/U' selection criteria
-         * for a given window -- it is called from only one place, and
-         * will likely be inlined even without the following directive */
+         * Determine if this task matches the 'u/U' selection
+         * criteria for a given window */
 static inline int wins_usrselect (const WIN_t *q, struct pids_stack *p) {
  // a tailored 'results stack value' extractor macro
  #define rSv(E)  PID_VAL(E, u_int, p)
@@ -4238,8 +4239,8 @@ static int Tree_idx;                        // frame_make resets to zero
            positive pid values represent parents with collapsed children
            while a negative pid value means children have been expanded.
            ( both of these are managed under the 'keys_task()' routine ) */
-static int     *Hide_pid;                   // collapsible process array
-static int      Hide_tot;                   // total used in above array
+static int *Hide_pid;                       // collapsible process array
+static int  Hide_tot;                       // total used in above array
 
         /*
          * This little recursive guy is the real forest view workhorse.
@@ -4300,34 +4301,45 @@ static void forest_begin (WIN_t *q) {
             forest_adds(i, 0);                 // add a parent with its children
       }
 
-      /* we're employing a couple of 'PIDS_extra' results in our stacks
-            eu_LVL (u_int): where level number is stored (0 - 100)
+      /* we're employing 3 additional 'PIDS_extra' results in our stacks
+            eu_LVL (u_int): where a level number is stored (0 - 100)
+            eu_ADD (u_int): where children's accumulated tics stored
             eu_HID (s_ch) : where 'x' == collapsed and 'z' == unseen */
       for (i = 0; i < Hide_tot; i++) {
        #define rSv_Pid(X)  PID_VAL(EU_PID, s_int, Tree_ppt[X])
        // if xtra-procps-debug.h active, can't use PID_VAL with assignment
        #define rSv_Lvl(X)  Tree_ppt[X]->head[Fieldstab[eu_LVL].erel].result.u_int
+       #define rSv_Add(X)  Tree_ppt[X]->head[Fieldstab[eu_ADD].erel].result.u_int
        #define rSv_Hid(X)  Tree_ppt[X]->head[Fieldstab[eu_HID].erel].result.s_ch
+       /* next isn't needed if TREE_VCPUOFF was defined, but it costs us nothing
+          yet we must never assume that PIDS_CPU result struct is always present */
+       #define rSv_Cpu(X)  (Fieldstab[EU_CPU].erel < 0) ? 0 : PID_VAL(EU_CPU, s_int, Tree_ppt[X])
 
          if (Hide_pid[i] > 0) {
             for (j = 0; j < PIDSmaxt; j++) {
                if (rSv_Pid(j) == Hide_pid[i]) {
-                  int  idx = j;
-                  unsigned lvl = rSv_Lvl(idx);
-                  rSv_Hid(idx) = 'x';
-                  while (j+1 < PIDSmaxt && rSv_Lvl(j+1) > lvl) {
+                  int parent = j;
+                  int children = 0;
+                  unsigned level = rSv_Lvl(parent);
+
+                  while (j+1 < PIDSmaxt && rSv_Lvl(j+1) > level) {
+#ifndef TREE_VCPUOFF
+                     rSv_Add(parent) += rSv_Cpu(j+1);
+#endif
                      rSv_Hid(j+1) = 'z';
-                     idx = 0;
+                     children = 1;
                      ++j;
                   }
-                  // no children found, so unmark this puppy
-                  if (idx) rSv_Hid(idx) = '\0';
+                  // children found (and collapsed), so mark that puppy
+                  if (children) rSv_Hid(parent) = 'x';
                }
             }
          }
        #undef rSv_Pid
        #undef rSv_Lvl
+       #undef rSv_Add
        #undef rSv_Hid
+       #undef rSv_Cpu
       }
    } // end: !Tree_idx
    memcpy(Seed_ppt, Tree_ppt, sizeof(void*) * PIDSmaxt);
@@ -5480,8 +5492,9 @@ static const char *task_show (const WIN_t *q, struct pids_stack *p) {
    char *rp;
    int x;
 
-   /* we're employing a couple of 'PIDS_extra' results in our stacks
-         eu_LVL (u_int): where level number is stored (0 - 100)
+   /* we're employing 3 additional 'PIDS_extra' results in our stacks
+         eu_LVL (u_int): where a level number is stored (0 - 100)
+         eu_ADD (u_int): where children's accumulated tics stored
          eu_HID (s_ch) : where 'x' == collapsed and 'z' == unseen */
 #ifndef TREE_VWINALL
    if (q == Curwin)            // note: the following is NOT indented
@@ -5558,11 +5571,19 @@ static const char *task_show (const WIN_t *q, struct pids_stack *p) {
             break;
    /* s_int, scale_pcnt with special handling */
          case EU_CPU:
-         {  float u = (float)rSv(EU_CPU, s_int) * Frame_etscale;
+         {  float u = (float)rSv(EU_CPU, s_int);
             int n = rSv(EU_THD, s_int);
+#ifndef TREE_VCPUOFF
+            // this eu_ADD is always zero, unless we're a collapsed parent
+            u += rSv(eu_ADD, u_int);
+            u *= Frame_etscale;
+            if (rSv(eu_HID, s_ch) != 'x' && u > 100.0 * n) u = 100.0 * n;
+#else
+            u *= Frame_etscale;
             /* process can't use more %cpu than number of threads it has
              ( thanks Jaromir Capik <jcapik@redhat.com> ) */
             if (u > 100.0 * n) u = 100.0 * n;
+#endif
             if (u > Cpu_pmax) u = Cpu_pmax;
             cp = scale_pcnt(u, W, Jn);
          }
index 59217265b851be6d65295c440eb31002447f7ddc..77180068cfcbc8fd0e0c5df12923849cd59e6d6c 100644 (file)
--- a/top/top.h
+++ b/top/top.h
@@ -48,6 +48,7 @@
 //#define TERMIOS_ONLY            /* just limp along with native input only  */
 //#define TREE_NORESET            /* sort keys do NOT force forest view OFF  */
 //#define TREE_SCANALL            /* rescan array w/ forest view, avoid sort */
+//#define TREE_VCPUOFF            /* a collapsed parent excludes child's cpu */
 //#define TREE_VPROMPT            /* pid collapse/expand prompt, vs. top row */
 //#define TREE_VWINALL            /* pid collapse/expand impacts all windows */
 //#define USE_X_COLHDR            /* emphasize header vs. whole col, for 'x' */