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>
This commit is contained in:
Jim Warner 2020-05-21 00:00:00 -05:00 committed by Craig Small
parent f93980910e
commit 5e1e14b019
4 changed files with 177 additions and 122 deletions

264
top/top.c
View File

@ -4412,6 +4412,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
@ -5224,6 +5225,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_faux_tot) w->combine_cpus = 0;
}
break;
case '1':
if (CHKw(w, View_CPUNOD)) OFFw(w, View_CPUSUM);
else TOGw(w, View_CPUSUM);
@ -5673,6 +5683,136 @@ static void keys_xtra (int ch) {
// show_msg(fmtmk("%s sort compatibility key honored", xmsg));
} // end: keys_xtra
/*###### 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 cpu_prt (const char *str, int nobuf) {
static char row[ROWMINSIZ];
static int tog;
char *p;
p = scat(row, str);
if (nobuf || !Curwin->double_up)
goto flush_it;
if (!tog) {
scat(p, Double_sp);
tog = 1;
return 0;
}
flush_it:
scat(p, "\n");
show_special(0, row);
row[0] = '\0';
tog = 0;
return 1;
} // end: cpu_prt
/*
* State display *Helper* function to calc and display the state
* percentages for a single cpu. In this way, we can support
* the following environments without the usual code bloat.
* 1) single cpu machines
* 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 cpu_tics (CPU_t *cpu, const char *pfx, int nobuf) {
/* we'll trim to zero if we get negative time ticks,
which has happened with some SMP kernels (pre-2.4?)
and when cpus are dynamically added or removed */
#define TRIMz(x) ((tz = (SIC_t)(x)) < 0 ? 0 : tz)
// user syst nice idle wait hirg sirq steal
SIC_t u_frme, s_frme, n_frme, i_frme, w_frme, x_frme, y_frme, z_frme, tot_frme, tz;
float scale;
int n;
u_frme = TRIMz(cpu->cur.u - cpu->sav.u);
s_frme = TRIMz(cpu->cur.s - cpu->sav.s);
n_frme = TRIMz(cpu->cur.n - cpu->sav.n);
i_frme = TRIMz(cpu->cur.i - cpu->sav.i);
w_frme = TRIMz(cpu->cur.w - cpu->sav.w);
x_frme = TRIMz(cpu->cur.x - cpu->sav.x);
y_frme = TRIMz(cpu->cur.y - cpu->sav.y);
z_frme = TRIMz(cpu->cur.z - cpu->sav.z);
tot_frme = u_frme + s_frme + n_frme + i_frme + w_frme + x_frme + y_frme + z_frme;
#ifndef CPU_ZEROTICS
if (tot_frme < cpu->edge)
tot_frme = u_frme = s_frme = n_frme = i_frme = w_frme = x_frme = y_frme = z_frme = 0;
#endif
if (1 > tot_frme) i_frme = tot_frme = 1;
scale = 100.0 / (float)tot_frme;
/* display some kinda' cpu state percentages
(who or what is explained by the passed prefix) */
if (Curwin->rc.graph_cpus) {
static struct {
const char *user, *syst, *type;
} gtab[] = {
{ "%-.*s~7", "%-.*s~8", Graph_bars },
{ "%-.*s~4", "%-.*s~6", Graph_blks }
};
char user[SMLBUFSIZ], syst[SMLBUFSIZ], dual[MEDBUFSIZ];
int ix = Curwin->rc.graph_cpus - 1;
float pct_user = (float)(u_frme + n_frme) * scale,
pct_syst = (float)(s_frme + x_frme + y_frme) * scale;
#ifndef QUICK_GRAPHS
int num_user = (int)((pct_user * Graph_adj) + .5),
num_syst = (int)((pct_syst * Graph_adj) + .5);
if (num_user + num_syst > Graph_len) num_syst = Graph_len - num_user;
snprintf(user, sizeof(user), gtab[ix].user, num_user, gtab[ix].type);
snprintf(syst, sizeof(syst), gtab[ix].syst, num_syst, gtab[ix].type);
#else
snprintf(user, sizeof(user), gtab[ix].user, (int)((pct_user * Graph_adj) + .5), gtab[ix].type);
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 = 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 = cpu_prt(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), nobuf);
}
return n;
#undef TRIMz
} // 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 (CPU_t *cpu, int nobuf) {
static CPU_t accum;
static int ix, beg;
char pfx[16];
int n;
accum.cur.u += cpu->cur.u; accum.cur.s += cpu->cur.s;
accum.cur.n += cpu->cur.n; accum.cur.i += cpu->cur.i;
accum.cur.w += cpu->cur.w; accum.cur.x += cpu->cur.x;
accum.cur.y += cpu->cur.y; accum.cur.z += cpu->cur.z;
accum.sav.u += cpu->sav.u; accum.sav.s += cpu->sav.s;
accum.sav.n += cpu->sav.n; accum.sav.i += cpu->sav.i;
accum.sav.w += cpu->sav.w; accum.sav.x += cpu->sav.x;
accum.sav.y += cpu->sav.y; accum.sav.z += cpu->sav.z;
if (!ix) beg = cpu->id;
if (nobuf || ix >= Curwin->combine_cpus) {
snprintf(pfx, sizeof(pfx), "%-7.7s:", fmtmk("%d-%d", beg, cpu->id));
n = cpu_tics(&accum, pfx, nobuf);
memset(&accum, 0, sizeof(CPU_t));
ix = beg = 0;
return n;
}
++ix;
return 0;
} // end: cpu_unify
/*###### Main Screen routines ##########################################*/
/*
@ -5687,7 +5827,7 @@ static void do_key (int ch) {
, 'I', 'k', 'r', 's', 'X', 'Y', 'Z', '0'
, kbd_ENTER, kbd_SPACE, '\0' } },
{ keys_summary,
{ '1', '2', '3', '4', 'C', 'l', 'm', 't', '\0' } },
{ '!', '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'
@ -5747,103 +5887,6 @@ all_done:
} // end: do_key
/*
* 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 char row[ROWMINSIZ];
static int tog;
char *p;
p = scat(row, str);
if (nobuf || !Curwin->double_up)
goto flush_it;
if (!tog) {
scat(p, Double_sp);
tog = 1;
return 0;
}
flush_it:
scat(p, "\n");
show_special(0, row);
row[0] = '\0';
tog = 0;
return 1;
} // end: sum_cpu
/*
* State display *Helper* function to calc and display the state
* percentages for a single cpu. In this way, we can support
* the following environments without the usual code bloat.
* 1) single cpu machines
* 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 (CPU_t *cpu, const char *pfx, int nobuf) {
/* we'll trim to zero if we get negative time ticks,
which has happened with some SMP kernels (pre-2.4?)
and when cpus are dynamically added or removed */
#define TRIMz(x) ((tz = (SIC_t)(x)) < 0 ? 0 : tz)
// user syst nice idle wait hirg sirq steal
SIC_t u_frme, s_frme, n_frme, i_frme, w_frme, x_frme, y_frme, z_frme, tot_frme, tz;
float scale;
int n;
u_frme = TRIMz(cpu->cur.u - cpu->sav.u);
s_frme = TRIMz(cpu->cur.s - cpu->sav.s);
n_frme = TRIMz(cpu->cur.n - cpu->sav.n);
i_frme = TRIMz(cpu->cur.i - cpu->sav.i);
w_frme = TRIMz(cpu->cur.w - cpu->sav.w);
x_frme = TRIMz(cpu->cur.x - cpu->sav.x);
y_frme = TRIMz(cpu->cur.y - cpu->sav.y);
z_frme = TRIMz(cpu->cur.z - cpu->sav.z);
tot_frme = u_frme + s_frme + n_frme + i_frme + w_frme + x_frme + y_frme + z_frme;
#ifndef CPU_ZEROTICS
if (tot_frme < cpu->edge)
tot_frme = u_frme = s_frme = n_frme = i_frme = w_frme = x_frme = y_frme = z_frme = 0;
#endif
if (1 > tot_frme) i_frme = tot_frme = 1;
scale = 100.0 / (float)tot_frme;
/* display some kinda' cpu state percentages
(who or what is explained by the passed prefix) */
if (Curwin->rc.graph_cpus) {
static struct {
const char *user, *syst, *type;
} gtab[] = {
{ "%-.*s~7", "%-.*s~8", Graph_bars },
{ "%-.*s~4", "%-.*s~6", Graph_blks }
};
char user[SMLBUFSIZ], syst[SMLBUFSIZ], dual[MEDBUFSIZ];
int ix = Curwin->rc.graph_cpus - 1;
float pct_user = (float)(u_frme + n_frme) * scale,
pct_syst = (float)(s_frme + x_frme + y_frme) * scale;
#ifndef QUICK_GRAPHS
int num_user = (int)((pct_user * Graph_adj) + .5),
num_syst = (int)((pct_syst * Graph_adj) + .5);
if (num_user + num_syst > Graph_len) num_syst = Graph_len - num_user;
snprintf(user, sizeof(user), gtab[ix].user, num_user, gtab[ix].type);
snprintf(syst, sizeof(syst), gtab[ix].syst, num_syst, gtab[ix].type);
#else
snprintf(user, sizeof(user), gtab[ix].user, (int)((pct_user * Graph_adj) + .5), gtab[ix].type);
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"
, pfx, pct_user, pct_syst, pct_user + pct_syst, Graph_len +4, dual), nobuf);
} else {
n = sum_cpu(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), nobuf);
}
return n;
#undef TRIMz
} // end: summary_hlp
/*
* In support of a new frame:
* 1) Display uptime and load average (maybe)
@ -5881,7 +5924,7 @@ static void summary_show (void) {
if (CHKw(w, View_CPUNOD)) {
if (Numa_node_sel < 0) {
// display the 1st /proc/stat line, then the nodes (if room)
Msg_row += summary_hlp(&Cpu_tics[smp_num_cpus], N_txt(WORD_allcpus_txt), 1);
Msg_row += cpu_tics(&Cpu_tics[smp_num_cpus], N_txt(WORD_allcpus_txt), 1);
// display each cpu node's states
for (i = 0; i < Numa_node_tot; i++) {
CPU_t *nod_ptr = &Cpu_tics[1 + smp_num_cpus + i];
@ -5890,7 +5933,7 @@ static void summary_show (void) {
if (nod_ptr->id) {
#endif
snprintf(tmp, sizeof(tmp), N_fmt(NUMA_nodenam_fmt), i);
Msg_row += summary_hlp(nod_ptr, tmp, 1);
Msg_row += cpu_tics(nod_ptr, tmp, 1);
#ifndef OFF_NUMASKIP
}
#endif
@ -5898,12 +5941,12 @@ static void summary_show (void) {
} else {
// display the node summary, then the associated cpus (if room)
snprintf(tmp, sizeof(tmp), N_fmt(NUMA_nodenam_fmt), Numa_node_sel);
Msg_row += summary_hlp(&Cpu_tics[1 + smp_num_cpus + Numa_node_sel], tmp, 1);
Msg_row += cpu_tics(&Cpu_tics[1 + smp_num_cpus + Numa_node_sel], tmp, 1);
for (i = 0; i < Cpu_faux_tot; i++) {
if (Numa_node_sel == Cpu_tics[i].node) {
if (!isROOM(anyFLG, 1)) break;
snprintf(tmp, sizeof(tmp), N_fmt(WORD_eachcpu_fmt), Cpu_tics[i].id);
Msg_row += summary_hlp(&Cpu_tics[i], tmp, 1);
Msg_row += cpu_tics(&Cpu_tics[i], tmp, 1);
}
}
}
@ -5911,13 +5954,20 @@ static void summary_show (void) {
numa_nope:
if (CHKw(w, View_CPUSUM)) {
// display just the 1st /proc/stat line
Msg_row += summary_hlp(&Cpu_tics[smp_num_cpus], N_txt(WORD_allcpus_txt), 1);
Msg_row += cpu_tics(&Cpu_tics[smp_num_cpus], N_txt(WORD_allcpus_txt), 1);
} else {
// display each cpu's states separately, screen height permitting...
for (i = 0; i < Cpu_faux_tot; i++) {
snprintf(tmp, sizeof(tmp), N_fmt(WORD_eachcpu_fmt), Cpu_tics[i].id);
Msg_row += summary_hlp(&Cpu_tics[i], tmp, (i+1 >= Cpu_faux_tot));
if (!isROOM(anyFLG, 1)) break;
if (w->combine_cpus) {
for (i = 0; i < Cpu_faux_tot; i++) {
Msg_row += cpu_unify(&Cpu_tics[i], (i+1 >= Cpu_faux_tot));
if (!isROOM(anyFLG, 1)) break;
}
} else {
for (i = 0; i < Cpu_faux_tot; i++) {
snprintf(tmp, sizeof(tmp), N_fmt(WORD_eachcpu_fmt), Cpu_tics[i].id);
Msg_row += cpu_tics(&Cpu_tics[i], tmp, (i+1 >= Cpu_faux_tot));
if (!isROOM(anyFLG, 1)) break;
}
}
}
} // end: View_STATES

View File

@ -437,6 +437,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
proc_t **ppt; // this window's proc_t ptr array
struct WIN_t *next, // next window in window stack
*prev; // prior window in window stack
@ -804,10 +805,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 (CPU_t *cpu, const char *pfx, int nobuf);
//atic int cpu_unify (CPU_t *cpu, 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 (CPU_t *cpu, const char *pfx, int nobuf);
//atic void summary_show (void);
//atic const char *task_show (const WIN_t *q, const int idx);
//atic void window_hlp (void);

View File

@ -413,11 +413,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\"");
@ -487,8 +487,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");
@ -501,6 +502,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");
}
@ -564,7 +566,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"
@ -654,22 +656,22 @@ static void build_uniq_nlstab (void) {
Uniq_nlstab[STATE_line_1_fmt] = _("%s:~3"
" %3u ~2total,~3 %3u ~2running,~3 %3u ~2sleeping,~3 %3u ~2stopped,~3 %3u ~2zombie~3\n");
Uniq_nlstab[STATE_lin2x4_fmt] = _("%%%s~3"
Uniq_nlstab[STATE_lin2x4_fmt] = _("%s~3"
" %#5.1f ~2user,~3 %#5.1f ~2system,~3 %#5.1f ~2nice,~3 %#5.1f ~2idle~3");
Uniq_nlstab[STATE_lin2x5_fmt] = _("%%%s~3"
Uniq_nlstab[STATE_lin2x5_fmt] = _("%s~3"
" %#5.1f ~2user,~3 %#5.1f ~2system,~3 %#5.1f ~2nice,~3 %#5.1f ~2idle,~3 %#5.1f ~2IO-wait~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 */
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 */

View File

@ -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,