diff options
author | Jim Warner <james.warner@comcast.net> | 2020-05-23 08:51:43 -0500 |
---|---|---|
committer | Craig Small <csmall@dropbear.xyz> | 2020-05-28 19:49:40 +1000 |
commit | 94d6db6b40bda4a49785855745e86607019df359 (patch) | |
tree | 623c036a129ea8b9989c97abf9bb781cb76a5ca6 /top | |
parent | 6e5f2c8de695b9330eed88da0a6eb51f57798871 (diff) | |
download | procps-ng-94d6db6b40bda4a49785855745e86607019df359.tar.gz |
top: add '!' toggle for combined cpus display, program
When implementing that earlier '4' toggle, in response
to the issue referenced below, I got to thinking about
those environments with massively parallel processors.
Such environments may not benefit from the '4' toggle.
So, I decided to implement a feature that could enable
use of those '1' and/or '4' toggles no matter how many
active processors top may have ultimately encountered.
With the new '!' toggle, adjacent cpus can be combined
to any degree, represented as a single cpu group/line.
Reference(s):
https://gitlab.com/procps-ng/procps/-/issues/172
Signed-off-by: Jim Warner <james.warner@comcast.net>
Diffstat (limited to 'top')
-rw-r--r-- | top/top.c | 233 | ||||
-rw-r--r-- | top/top.h | 7 | ||||
-rw-r--r-- | top/top_nls.c | 20 | ||||
-rw-r--r-- | top/top_nls.h | 4 |
4 files changed, 163 insertions, 101 deletions
@@ -3991,6 +3991,7 @@ static void win_reset (WIN_t *q) { // NOHISEL_xxx is redundant (already turned off by osel_clear) OFFw(q, NOHIFND_xxx | NOHISEL_xxx); #endif + q->combine_cpus = 0; } // end: win_reset @@ -4806,6 +4807,15 @@ static void keys_summary (int ch) { WIN_t *w = Curwin; // avoid gcc bloat with a local copy switch (ch) { + case '!': + if (CHKw(w, View_CPUSUM) || CHKw(w, View_CPUNOD)) + show_msg(N_txt(XTRA_modebad_txt)); + else { + if (!w->combine_cpus) w->combine_cpus = 1; + else w->combine_cpus *= 2; + if (w->combine_cpus >= Cpu_cnt) w->combine_cpus = 0; + } + break; case '1': if (CHKw(w, View_CPUNOD)) OFFw(w, View_CPUSUM); else TOGw(w, View_CPUSUM); @@ -5255,84 +5265,12 @@ static void keys_xtra (int ch) { // show_msg(fmtmk("%s sort compatibility key honored", xmsg)); } // end: keys_xtra -/*###### Main Screen routines ##########################################*/ - - /* - * Process keyboard input during the main loop */ -static void do_key (int ch) { - static struct { - void (*func)(int ch); - char keys[SMLBUFSIZ]; - } key_tab[] = { - { keys_global, - { '?', 'B', 'd', 'E', 'e', 'F', 'f', 'g', 'H', 'h' - , 'I', 'k', 'r', 's', 'X', 'Y', 'Z', '0' - , kbd_ENTER, kbd_SPACE, '\0' } }, - { keys_summary, - { '1', '2', '3', '4', 'C', 'l', 'm', 't', '\0' } }, - { keys_task, - { '#', '<', '>', 'b', 'c', 'i', 'J', 'j', 'n', 'O', 'o' - , 'R', 'S', 'U', 'u', 'V', 'v', 'x', 'y', 'z' - , kbd_CtrlO, '\0' } }, - { keys_window, - { '+', '-', '=', '_', '&', 'A', 'a', 'G', 'L', 'w' - , kbd_UP, kbd_DOWN, kbd_LEFT, kbd_RIGHT, kbd_PGUP, kbd_PGDN - , kbd_HOME, kbd_END, '\0' } }, - { keys_xtra, - { 'M', 'N', 'P', 'T', '\0'} } - }; - int i; - - switch (ch) { - case 0: // ignored (always) - case kbd_ESC: // ignored (sometimes) - goto all_done; - case 'q': // no return from this guy - bye_bye(NULL); - case 'W': // no need for rebuilds - write_rcfile(); - goto all_done; - default: // and now, the real work... - for (i = 0; i < MAXTBL(key_tab); ++i) - if (strchr(key_tab[i].keys, ch)) { - key_tab[i].func(ch); - Frames_signal = BREAK_kbd; - goto all_done; - } - }; - /* The Frames_signal above will force a rebuild of column headers. - It's NOT simply lazy programming. Below are some keys that may - require new column headers and/or new library item enumerators: - 'A' - likely - 'c' - likely when !Mode_altscr, maybe when Mode_altscr - 'F' - likely - 'f' - likely - 'g' - likely - 'H' - likely - 'I' - likely - 'J' - always - 'j' - always - 'Z' - likely, if 'Curwin' changed when !Mode_altscr - '-' - likely (restricted to Mode_altscr) - '_' - likely (restricted to Mode_altscr) - '=' - maybe, but only when Mode_altscr - '+' - likely (restricted to Mode_altscr) - PLUS, likely for FOUR of the EIGHT cursor motion keys (scrolled) - ( At this point we have a human being involved and so have all the time ) - ( in the world. We can afford a few extra cpu cycles every now & then! ) - */ - - show_msg(N_txt(UNKNOWN_cmds_txt)); -all_done: - sysinfo_refresh(1); // let's be more responsive to hot-pluggin' - putp((Cursor_state = Cap_curs_hide)); -} // end: do_key - +/*###### Cpu Display Secondary support (summary_show helpers) ##########*/ /* * Cpu *Helper* function to combine and or show the state * percentages for 1 cpu or 2 adjacent cpus (one single line). */ -static inline int sum_cpu (const char *str, int nobuf) { +static inline int cpu_prt (const char *str, int nobuf) { static char row[ROWMINSIZ]; static int tog; char *p; @@ -5351,7 +5289,7 @@ flush_it: row[0] = '\0'; tog = 0; return 1; -} // end: sum_cpu +} // end: cpu_prt /* @@ -5362,7 +5300,7 @@ flush_it: * 2) modest smp boxes with room for each cpu's percentages * 3) massive smp guys leaving little or no room for process * display and thus requiring the cpu summary toggle */ -static int summary_hlp (struct stat_stack *this, const char *pfx, int nobuf) { +static int cpu_tics (struct stat_stack *this, const char *pfx, int nobuf) { // a tailored 'results stack value' extractor macro #define rSv(E) TIC_VAL(E, this) SIC_t idl_frme, tot_frme; @@ -5398,10 +5336,10 @@ static int summary_hlp (struct stat_stack *this, const char *pfx, int nobuf) { snprintf(syst, sizeof(syst), gtab[ix].syst, (int)((pct_syst * Graph_adj) + .4), gtab[ix].type); #endif snprintf(dual, sizeof(dual), "%s%s", user, syst); - n = sum_cpu(fmtmk("%%%s ~3%#5.1f~2/%-#5.1f~3 %3.0f[~1%-*s]~1" + n = cpu_prt(fmtmk("%s ~3%#5.1f~2/%-#5.1f~3 %3.0f[~1%-*s]~1" , pfx, pct_user, pct_syst, pct_user + pct_syst, Graph_len +4, dual), nobuf); } else { - n = sum_cpu(fmtmk(Cpu_States_fmts, pfx + n = cpu_prt(fmtmk(Cpu_States_fmts, pfx , (float)rSv(stat_US) * scale, (float)rSv(stat_SY) * scale , (float)rSv(stat_NI) * scale, (float)idl_frme * scale , (float)rSv(stat_IO) * scale, (float)rSv(stat_IR) * scale @@ -5409,7 +5347,119 @@ static int summary_hlp (struct stat_stack *this, const char *pfx, int nobuf) { } return n; #undef rSv -} // end: summary_hlp +} // end: cpu_tics + + + /* + * Cpu *Helper* function to combine adjacent cpu stats + * in an effort to reduce total number of processors shown */ +static int cpu_unify (struct stat_stack *this, int nobuf) { + // a tailored 'results stack value' extractor macro + #define rSv(E,T) STAT_VAL(E, T, this, Stat_ctx) + static struct stat_result stack[MAXTBL(Stat_items)]; + static struct stat_stack accum = { &stack[0] }; + static int ix, beg; + char pfx[16]; + int n; + + // entries for stat_ID & stat_NU are unused + stack[stat_US].result.sl_int += rSv(stat_US, sl_int); + stack[stat_SY].result.sl_int += rSv(stat_SY, sl_int); + stack[stat_NI].result.sl_int += rSv(stat_NI, sl_int); + stack[stat_IL].result.sl_int += rSv(stat_IL, sl_int); + stack[stat_IO].result.sl_int += rSv(stat_IO, sl_int); + stack[stat_IR].result.sl_int += rSv(stat_IR, sl_int); + stack[stat_SI].result.sl_int += rSv(stat_SI, sl_int); + stack[stat_ST].result.sl_int += rSv(stat_ST, sl_int); + stack[stat_SUM_USR].result.sl_int += rSv(stat_SUM_USR, sl_int); + stack[stat_SUM_SYS].result.sl_int += rSv(stat_SUM_SYS, sl_int); + stack[stat_SUM_TOT].result.sl_int += rSv(stat_SUM_TOT, sl_int); + + if (!ix) beg = rSv(stat_ID, s_int); + if (nobuf || ix >= Curwin->combine_cpus) { + snprintf(pfx, sizeof(pfx), "%-7.7s:", fmtmk("%d-%d", beg, rSv(stat_ID, s_int))); + n = cpu_tics(&accum, pfx, nobuf); + memset(&stack, 0, sizeof(stack)); + ix = beg = 0; + return n; + } + ++ix; + return 0; + #undef rSv +} // end: cpu_unify + +/*###### Main Screen routines ##########################################*/ + + /* + * Process keyboard input during the main loop */ +static void do_key (int ch) { + static struct { + void (*func)(int ch); + char keys[SMLBUFSIZ]; + } key_tab[] = { + { keys_global, + { '?', 'B', 'd', 'E', 'e', 'F', 'f', 'g', 'H', 'h' + , 'I', 'k', 'r', 's', 'X', 'Y', 'Z', '0' + , kbd_ENTER, kbd_SPACE, '\0' } }, + { keys_summary, + { '!', '1', '2', '3', '4', 'C', 'l', 'm', 't', '\0' } }, + { keys_task, + { '#', '<', '>', 'b', 'c', 'i', 'J', 'j', 'n', 'O', 'o' + , 'R', 'S', 'U', 'u', 'V', 'v', 'x', 'y', 'z' + , kbd_CtrlO, '\0' } }, + { keys_window, + { '+', '-', '=', '_', '&', 'A', 'a', 'G', 'L', 'w' + , kbd_UP, kbd_DOWN, kbd_LEFT, kbd_RIGHT, kbd_PGUP, kbd_PGDN + , kbd_HOME, kbd_END, '\0' } }, + { keys_xtra, + { 'M', 'N', 'P', 'T', '\0'} } + }; + int i; + + switch (ch) { + case 0: // ignored (always) + case kbd_ESC: // ignored (sometimes) + goto all_done; + case 'q': // no return from this guy + bye_bye(NULL); + case 'W': // no need for rebuilds + write_rcfile(); + goto all_done; + default: // and now, the real work... + for (i = 0; i < MAXTBL(key_tab); ++i) + if (strchr(key_tab[i].keys, ch)) { + key_tab[i].func(ch); + Frames_signal = BREAK_kbd; + goto all_done; + } + }; + /* The Frames_signal above will force a rebuild of column headers. + It's NOT simply lazy programming. Below are some keys that may + require new column headers and/or new library item enumerators: + 'A' - likely + 'c' - likely when !Mode_altscr, maybe when Mode_altscr + 'F' - likely + 'f' - likely + 'g' - likely + 'H' - likely + 'I' - likely + 'J' - always + 'j' - always + 'Z' - likely, if 'Curwin' changed when !Mode_altscr + '-' - likely (restricted to Mode_altscr) + '_' - likely (restricted to Mode_altscr) + '=' - maybe, but only when Mode_altscr + '+' - likely (restricted to Mode_altscr) + PLUS, likely for FOUR of the EIGHT cursor motion keys (scrolled) + ( At this point we have a human being involved and so have all the time ) + ( in the world. We can afford a few extra cpu cycles every now & then! ) + */ + + show_msg(N_txt(UNKNOWN_cmds_txt)); +all_done: + sysinfo_refresh(1); // let's be more responsive to hot-pluggin' + putp((Cursor_state = Cap_curs_hide)); +} // end: do_key /* @@ -5450,14 +5500,14 @@ static void summary_show (void) { if (Numa_node_sel < 0) { numa_oops: // display the 1st /proc/stat line, then the nodes (if room) - Msg_row += summary_hlp(Stat_reap->summary, N_txt(WORD_allcpus_txt), 1); + Msg_row += cpu_tics(Stat_reap->summary, N_txt(WORD_allcpus_txt), 1); // display each cpu node's states for (i = 0; i < Numa_node_tot; i++) { struct stat_stack *nod_ptr = Stat_reap->nodes->stacks[i]; if (NOD_VAL(stat_ID, i) == STAT_NODE_INVALID) continue; if (!isROOM(anyFLG, 1)) break; snprintf(tmp, sizeof(tmp), N_fmt(NUMA_nodenam_fmt), NOD_VAL(stat_ID, i)); - Msg_row += summary_hlp(nod_ptr, tmp, 1); + Msg_row += cpu_tics(nod_ptr, tmp, 1); } } else { // display the node summary, then the associated cpus (if room) @@ -5465,12 +5515,12 @@ numa_oops: if (Numa_node_sel == NOD_VAL(stat_ID, i)) break; if (i == Numa_node_tot) goto numa_oops; snprintf(tmp, sizeof(tmp), N_fmt(NUMA_nodenam_fmt), Numa_node_sel); - Msg_row += summary_hlp(Stat_reap->nodes->stacks[Numa_node_sel], tmp, 1); + Msg_row += cpu_tics(Stat_reap->nodes->stacks[Numa_node_sel], tmp, 1); for (i = 0; i < Cpu_cnt; i++) { if (Numa_node_sel == CPU_VAL(stat_NU, i)) { if (!isROOM(anyFLG, 1)) break; snprintf(tmp, sizeof(tmp), N_fmt(WORD_eachcpu_fmt), CPU_VAL(stat_ID, i)); - Msg_row += summary_hlp(Stat_reap->cpus->stacks[i], tmp, 1); + Msg_row += cpu_tics(Stat_reap->cpus->stacks[i], tmp, 1); } } } @@ -5479,13 +5529,20 @@ numa_oops: numa_nope: if (CHKw(w, View_CPUSUM)) { // display just the 1st /proc/stat line - Msg_row += summary_hlp(Stat_reap->summary, N_txt(WORD_allcpus_txt), 1); + Msg_row += cpu_tics(Stat_reap->summary, N_txt(WORD_allcpus_txt), 1); } else { // display each cpu's states separately, screen height permitting... - for (i = 0; i < Cpu_cnt; i++) { - snprintf(tmp, sizeof(tmp), N_fmt(WORD_eachcpu_fmt), CPU_VAL(stat_ID, i)); - Msg_row += summary_hlp(Stat_reap->cpus->stacks[i], tmp, (i+1 >= Cpu_cnt)); - if (!isROOM(anyFLG, 1)) break; + if (w->combine_cpus) { + for (i = 0; i < Cpu_cnt; i++) { + Msg_row += cpu_unify(Stat_reap->cpus->stacks[i], (i+1 >= Cpu_cnt)); + if (!isROOM(anyFLG, 1)) break; + } + } else { + for (i = 0; i < Cpu_cnt; i++) { + snprintf(tmp, sizeof(tmp), N_fmt(WORD_eachcpu_fmt), CPU_VAL(stat_ID, i)); + Msg_row += cpu_tics(Stat_reap->cpus->stacks[i], tmp, (i+1 >= Cpu_cnt)); + if (!isROOM(anyFLG, 1)) break; + } } } } // end: View_STATES @@ -356,6 +356,7 @@ typedef struct WIN_t { char *findstr; // window's current/active search string int findlen; // above's strlen, without call overhead int double_up; // show individual cpus 2 abreast + int combine_cpus; // keep combining adjacent cpus struct pids_stack **ppt; // this window's stacks ptr array struct WIN_t *next, // next window in window stack *prev; // prior window in window stack @@ -668,10 +669,12 @@ typedef struct WIN_t { //atic void keys_task (int ch); //atic void keys_window (int ch); //atic void keys_xtra (int ch); +/*------ Cpu Display Secondary Support (summary_show helpers) ----------*/ +//atic inline int cpu_prt (const char *str, int nobuf); +//atic int cpu_tics (struct stat_stack *this, const char *pfx, int nobuf); +//atic int cpu_unify (struct stat_stack *this, int nobuf); /*------ Main Screen routines ------------------------------------------*/ //atic void do_key (int ch); -//atic inline int sum_cpu (const char *str, int nobuf); -//atic int summary_hlp (struct stat_stack *this, const char *pfx, int nobuf); //atic void summary_show (void); //atic const char *task_show (const WIN_t *q, struct pids_stack *p); //atic void window_hlp (void); diff --git a/top/top_nls.c b/top/top_nls.c index 29856b7..c69385d 100644 --- a/top/top_nls.c +++ b/top/top_nls.c @@ -416,11 +416,11 @@ static void build_norm_nlstab (void) { Norm_nlstab[WORD_process_txt] = _("Tasks"); /* Translation Hint: The following "word" is meant to represent either a single . cpu or all of the processors in a multi-processor computer - . (should be exactly 6 characters, not counting the colon)*/ - Norm_nlstab[WORD_allcpus_txt] = _("Cpu(s):"); + . (should be exactly 6 characters, excluding leading % & colon) */ + Norm_nlstab[WORD_allcpus_txt] = _("%Cpu(s):"); /* Translation Hint: The following "word" is meant to represent a single processor - . (should be exactly 3 characters) */ - Norm_nlstab[WORD_eachcpu_fmt] = _("Cpu%-3d:"); + . (should be exactly 3 characters, excluding leading %%, fmt chars & colon) */ + Norm_nlstab[WORD_eachcpu_fmt] = _("%%Cpu%-3d:"); /* Translation Hint: The following word "another" must have 1 trailing space */ Norm_nlstab[WORD_another_txt] = _("another "); Norm_nlstab[FIND_no_next_txt] = _("Locate next inactive, use \"L\""); @@ -490,8 +490,9 @@ static void build_norm_nlstab (void) { Norm_nlstab[WORD_exclude_txt] = _("exclude"); Norm_nlstab[OSEL_statlin_fmt] = _("<Enter> to resume, filters: %s"); Norm_nlstab[WORD_noneone_txt] = _("none"); -/* Translation Hint: The following word 'Node' should be exactly 4 characters */ - Norm_nlstab[NUMA_nodenam_fmt] = _("Node%-2d:"); +/* Translation Hint: The following word 'Node' should be exactly + 4 characters, excluding leading %%, fmt chars & colon) */ + Norm_nlstab[NUMA_nodenam_fmt] = _("%%Node%-2d:"); Norm_nlstab[NUMA_nodeget_fmt] = _("expand which node (0-%d)"); Norm_nlstab[NUMA_nodebad_txt] = _("invalid node"); Norm_nlstab[NUMA_nodenot_txt] = _("sorry, NUMA extensions unavailable"); @@ -507,6 +508,7 @@ static void build_norm_nlstab (void) { Norm_nlstab[BAD_memscale_fmt] = _("bad memory scaling arg '%c'"); Norm_nlstab[XTRA_vforest_fmt] = _("PID to collapse/expand [default pid = %d]"); Norm_nlstab[XTRA_size2up_txt] = _("terminal is not wide enough"); + Norm_nlstab[XTRA_modebad_txt] = _("wrong mode, command inactive"); } @@ -570,7 +572,7 @@ static void build_uniq_nlstab (void) { " V,v . Toggle: '~1V~2' forest view; '~1v~2' hide/show forest view children\n" "\n" "%s" - " W,Y Write configuration file '~1W~2'; Inspect other output '~1Y~2'\n" + " W,Y,! Write config file '~1W~2'; Inspect other output '~1Y~2'; Combine Cpus '~1!~2'\n" " q Quit\n" " ( commands shown with '.' require a ~1visible~2 task display ~1window~2 ) \n" "Press '~1h~2' or '~1?~2' for help with ~1Windows~2,\n" @@ -663,13 +665,13 @@ static void build_uniq_nlstab (void) { /* Translation Hint: Only the following abbreviations need be translated . us = user, sy = system, ni = nice, id = idle, wa = wait, . hi hardware interrupt, si = software interrupt */ - Uniq_nlstab[STATE_lin2x6_fmt] = _("%%%s~3" + Uniq_nlstab[STATE_lin2x6_fmt] = _("%s~3" " %#5.1f ~2us,~3 %#5.1f ~2sy,~3 %#5.1f ~2ni,~3 %#5.1f ~2id,~3 %#5.1f ~2wa,~3 %#5.1f ~2hi,~3 %#5.1f ~2si~3"); /* Translation Hint: Only the following abbreviations need be translated . us = user, sy = system, ni = nice, id = idle, wa = wait, . hi hardware interrupt, si = software interrupt, st = steal time */ - Uniq_nlstab[STATE_lin2x7_fmt] = _("%%%s~3" + Uniq_nlstab[STATE_lin2x7_fmt] = _("%s~3" "%#5.1f ~2us,~3%#5.1f ~2sy,~3%#5.1f ~2ni,~3%#5.1f ~2id,~3%#5.1f ~2wa,~3%#5.1f ~2hi,~3%#5.1f ~2si,~3%#5.1f ~2st~3"); /* Translation Hint: this must be translated as 2 lines with words above & below aligned */ diff --git a/top/top_nls.h b/top/top_nls.h index 51cbe19..e728b76 100644 --- a/top/top_nls.h +++ b/top/top_nls.h @@ -82,8 +82,8 @@ enum norm_nls { WORD_abv_mem_txt, WORD_abv_swp_txt, WORD_allcpus_txt, WORD_another_txt, WORD_eachcpu_fmt, WORD_exclude_txt, WORD_include_txt, WORD_noneone_txt, WORD_process_txt, WORD_threads_txt, WRITE_rcfile_fmt, WRONG_switch_fmt, - XTRA_badflds_fmt, XTRA_fixwide_fmt, XTRA_size2up_txt, XTRA_vforest_fmt, - XTRA_warncfg_txt, XTRA_winsize_txt, + XTRA_badflds_fmt, XTRA_fixwide_fmt, XTRA_modebad_txt, XTRA_size2up_txt, + XTRA_vforest_fmt, XTRA_warncfg_txt, XTRA_winsize_txt, #ifndef INSP_OFFDEMO YINSP_demo01_txt, YINSP_demo02_txt, YINSP_demo03_txt, YINSP_deqfmt_txt, YINSP_deqtyp_txt, YINSP_dstory_txt, |