From 7c7903e50dff8719408b2a95d75f94ec1407aff7 Mon Sep 17 00:00:00 2001 From: Jim Warner Date: Mon, 25 Jun 2018 00:00:00 -0500 Subject: [PATCH] top: parent total cpu includes collapsed children, pgm 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 --- top/top.c | 59 +++++++++++++++++++++++++++++++++++++------------------ top/top.h | 1 + 2 files changed, 41 insertions(+), 19 deletions(-) diff --git a/top/top.c b/top/top.c index 848ef4aa..bc9536ed 100644 --- 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 ) */ if (u > 100.0 * n) u = 100.0 * n; +#endif if (u > Cpu_pmax) u = Cpu_pmax; cp = scale_pcnt(u, W, Jn); } diff --git a/top/top.h b/top/top.h index 59217265..77180068 100644 --- 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' */