cgroup support. rcfile errors default to fatal

This source patch addresses the following:
 Conceivable Buglets Avoided
  . added sanity checks for previous assumptions re: pid size, num cpus
  . changed response for rcfile errors to fatal, vs. silent default
 Enhancements
  . reorganized WIN_t struct for reduced padding and better readability
  . generalized code for multiple var-width cols (divorced from pflags)
  . absorbed jan gorig's cgroup patch, with attribution
 Cosmetic
  . reduced numerous #ifdef directives, especially in calibrate_fields
This commit is contained in:
Craig Small 2011-04-15 09:24:29 +10:00
parent 9bbaa0b3d2
commit 3ec6a27e8a
2 changed files with 112 additions and 67 deletions

135
top.c
View File

@ -186,6 +186,43 @@ static int *PHash_sav = HHash_one, // alternating 'old/new' hash tables
*PHash_new = HHash_two;
#endif
/*###### Temporary Placement ###########################################*/
/* For cgroup support inauguration, thanks to:
Jan Gorig <jgorig@redhat.com> */
/*
* Create string from cgroup array --
* ( eventually to find a home in libproc ? ) */
static void parse_cgroup (char *dst, size_t max, const proc_t *p) {
int whackable_int;
char *ccgroup, *endp = dst;
*dst = '\0';
if (p->cgroup) {
char **pcgroup = p->cgroup;
while (*pcgroup != NULL) {
// skip root cgroups
if (!**pcgroup || (*pcgroup)[strlen(*pcgroup) - 1] == '/') {
pcgroup++;
continue;
}
// skip initial cgroup number
ccgroup = strchr(*pcgroup, ':');
if (ccgroup == NULL)
ccgroup = *pcgroup;
else
ccgroup++;
if (endp != dst)
endp += escape_str(endp, ";", max, &whackable_int);
endp += escape_str(endp, ccgroup, max, &whackable_int);
pcgroup++;
}
}
if (!*dst) strncpy(dst, "-", max);
}
/*###### Sort callbacks ################################################*/
/*
@ -194,6 +231,14 @@ static int *PHash_sav = HHash_one, // alternating 'old/new' hash tables
* routine may serve more than one column.
*/
static int SCB_NAME(CGR) (const proc_t **P, const proc_t **Q) {
char p[SCREENMAX], q[SCREENMAX];
/* we won't always re-parse these cgroups -- besides, it's only
a recurring burden when CGROUP is chosen as the sort column! */
parse_cgroup(p, sizeof(p), *P);
parse_cgroup(q, sizeof(q), *Q);
return Frame_srtflg * STRSORTCMP(q, p);
}
static int SCB_NAME(CMD) (const proc_t **P, const proc_t **Q) {
/* if a process doesn't have a cmdline, we'll consider it a kernel thread
-- since displayed tasks are given special treatment, we must too */
@ -361,7 +406,7 @@ static void bye_bye (const char *str) {
"\n\t winflags = %08x, maxpflgs = %d"
#endif
"\n\t fieldscur = %s, sortindx = %d"
"\n\t maxtasks = %d, maxcmdln = %d, winlines = %d"
"\n\t maxtasks = %d, varcolsz = %d, winlines = %d"
"\n\t strlen(columnhdr) = %d"
"\n"
, __func__
@ -392,7 +437,7 @@ static void bye_bye (const char *str) {
, Curwin->rc.winname, Curwin->grpname
, Curwin->rc.winflags, Curwin->maxpflgs
, Curwin->rc.fieldscur, Curwin->rc.sortindx
, Curwin->rc.maxtasks, Curwin->maxcmdln, Curwin->winlines
, Curwin->rc.maxtasks, Curwin->varcolsz, Curwin->winlines
, (int)strlen(Curwin->columnhdr)
);
}
@ -1167,6 +1212,7 @@ static inline int user_matched (WIN_t *q, const proc_t *p) {
#define L_stat PROC_FILLSTAT
#define L_statm PROC_FILLMEM
#define L_status PROC_FILLSTATUS
#define L_CGROUP PROC_FILLCGROUP
#define L_CMDLINE PROC_FILLARG
#define L_EUSER PROC_FILLUSR
#define L_OUSER PROC_FILLSTATUS | PROC_FILLUSR
@ -1188,7 +1234,8 @@ static FLD_t Fieldstab[] = {
#define SF(f) (QFP_t)SCB_NAME(f)
/* .head + .fmts anomolies:
entries shown with NULL are valued at runtime (see zap_fieldstab)
entries shown with NULL are either valued at runtime (see zap_fieldstab)
or, in the case of .fmts, represent variable width fields
.lflg anomolies:
P_UED, L_NONE - natural outgrowth of 'stat()' in readproc (euid)
P_CPU, L_stat - never filled by libproc, but requires times (pcpu)
@ -1228,16 +1275,17 @@ static FLD_t Fieldstab[] = {
{ "nMin ", "%4.4s ", 4, SK_no, SF(FL2), L_stat, "Minor Page Faults" },
{ "nDRT ", "%4.4s ", 4, SK_no, SF(DRT), L_statm, "Dirty Pages Count" },
{ "S ", "%c ", -1, -1, SF(STA), L_EITHER, "Process Status" },
// next entry's special: '.head' will be formatted using table entry's own
// '.fmts' plus runtime supplied conversion args!
{ "COMMAND ", "%-*.*s ", -1, -1, SF(CMD), L_EITHER, "Command Name/Line" },
// next entry's special: '.head' is variable width (see calibrate_fields)
{ "COMMAND ", NULL, -1, -1, SF(CMD), L_EITHER, "Command Name/Line" },
{ "WCHAN ", "%-9.9s ", -1, -1, SF(WCH), L_stat, "Sleeping in Function" },
// next entry's special: the 0's will be replaced with '.'!
#ifdef CASEUP_HEXES
{ "Flags ", "%08lX ", -1, -1, SF(FLG), L_stat, "Task Flags <sched.h>" }
{ "Flags ", "%08lX ", -1, -1, SF(FLG), L_stat, "Task Flags <sched.h>" },
#else
{ "Flags ", "%08lx ", -1, -1, SF(FLG), L_stat, "Task Flags <sched.h>" }
{ "Flags ", "%08lx ", -1, -1, SF(FLG), L_stat, "Task Flags <sched.h>" },
#endif
// next entry's like P_CMD, and '.head' must be the same length -- they share varcolsz
{ "CGROUP ", NULL, -1, -1, SF(CGR), L_CGROUP, "Control Group" }
#ifdef ZAP_SUSEONLY
#define L_oom PROC_FILLOOM
,{ "Adj ", "%3d ", -1, -1, SF(OOA), L_oom, "oom_adjustment (2^X)" }
@ -1333,6 +1381,7 @@ static void calibrate_fields (void) {
int x, hdrmax = 0;
#endif
int i, needpsdb = 0;
int varcolcnt;
// block SIGWINCH signals while we do our thing...
sigemptyset(&newss);
@ -1345,11 +1394,8 @@ static void calibrate_fields (void) {
do {
if (VIZISw(w)) {
#ifdef USE_X_COLHDR
#ifdef EQUCOLHDRYES
w->hdrcaplen = 0;
#endif
#endif
w->hdrcaplen = 0; // > 0 only with USE_X_COLHDR but ref'd throughout
// ( we were proliferating way too many #ifdef's )
// build window's pflgsall array, establish upper bounds for maxpflgs
for (i = 0, w->totpflgs = 0; i < P_MAXPFLGS; i++) {
if (FLDviz(w, i)) {
@ -1369,7 +1415,7 @@ static void calibrate_fields (void) {
/* build a preliminary columns header not to exceed screen width
while accounting for a possible leading window number */
w->maxcmdln = 0;
w->varcolsz = varcolcnt = 0;
*(s = w->columnhdr) = '\0';
if (Rc.mode_altscr) s = scat(s, " ");
for (i = 0; i + w->begpflg < w->totpflgs; i++) {
@ -1381,15 +1427,16 @@ static void calibrate_fields (void) {
h = Fieldstab[f].head;
// oops, won't fit -- we're outta here...
if (Screen_cols <= ((int)(s - w->columnhdr) + (int)strlen(h))) break;
if (P_CMD == f) w->maxcmdln = strlen(h) - 1;
if (!Fieldstab[f].fmts) { ++varcolcnt; w->varcolsz += strlen(h) - 1; }
s = scat(s, h);
}
/* establish the final maxpflgs and prepare to grow the command column
heading via maxcmdln - it may be a fib if P_CMD wasn't encountered,
but that's ok because it won't be displayed anyway */
/* establish the final maxpflgs and prepare to grow the variable column
heading(s) via varcolsz - it may be a fib if their pflags wern't
encountered, but that's ok because they won't be displayed anyway */
w->maxpflgs = i;
w->maxcmdln += Screen_cols - strlen(w->columnhdr) - 1;
w->varcolsz += Screen_cols - strlen(w->columnhdr);
if (varcolcnt) w->varcolsz = w->varcolsz / varcolcnt;
/* establish the field where all remaining fields would still
fit within screen width, including a leading window number */
@ -1417,39 +1464,29 @@ static void calibrate_fields (void) {
#ifdef USE_X_COLHDR
if (CHKw(w, Show_HICOLS) && f == w->rc.sortindx) {
s = scat(s, fmtmk("%s%s", Caps_off, w->capclr_msg));
#ifdef EQUCOLHDRYES
w->hdrcaplen += strlen(Caps_off) + strlen(w->capclr_msg);
#endif
}
#endif
#ifndef USE_X_COLHDR
#else
if (P_MAXPFLGS < f) continue;
#endif
h = Fieldstab[f].head;
if (P_WCH == f) needpsdb = 1;
if (P_CMD == f) {
if (CHKw(w, Show_CMDLIN)) Frames_libflags |= L_CMDLINE;
s = scat(s, fmtmk(Fieldstab[P_CMD].fmts, w->maxcmdln, w->maxcmdln, h));
} else
s = scat(s, h);
if (P_CMD == f && CHKw(w, Show_CMDLIN)) Frames_libflags |= L_CMDLINE;
if (Fieldstab[f].fmts) s = scat(s, h);
else s = scat(s, fmtmk(VARCOL_fmts, w->varcolsz, w->varcolsz, h));
Frames_libflags |= Fieldstab[w->procflgs[i]].lflg;
#ifdef USE_X_COLHDR
if (CHKw(w, Show_HICOLS) && f == w->rc.sortindx) {
s = scat(s, fmtmk("%s%s", Caps_off, w->capclr_hdr));
#ifdef EQUCOLHDRYES
w->hdrcaplen += strlen(Caps_off) + strlen(w->capclr_hdr);
#endif
}
#endif
}
#ifdef EQUCOLHDRYES
// prepare to even out column header lengths...
#ifdef USE_X_COLHDR
if (hdrmax + w->hdrcaplen < (x = strlen(w->columnhdr))) hdrmax = x - w->hdrcaplen;
#else
if (hdrmax < (x = strlen(w->columnhdr))) hdrmax = x;
#endif
#endif
} // end: VIZISw(w)
if (Rc.mode_altscr) w = w->next;
} while (w != Curwin);
@ -1461,13 +1498,8 @@ static void calibrate_fields (void) {
for (i = 0; i < GROUPSMAX; i++) {
w = &Winstk[i];
if (CHKw(w, Show_TASKON))
#ifdef USE_X_COLHDR
if (hdrmax + w->hdrcaplen > (x = strlen(w->columnhdr)))
memset(&w->columnhdr[x], ' ', hdrmax + w->hdrcaplen - x);
#else
if (hdrmax > (x = strlen(w->columnhdr)))
memset(&w->columnhdr[x], ' ', hdrmax - x);
#endif
}
#endif
@ -1652,6 +1684,7 @@ static void zap_fieldstab (void) {
Fieldstab[P_PPD].head = " PPID ";
Fieldstab[P_PPD].fmts = "%5d ";
if (5 < (digits = get_pid_digits())) {
if (10 < digits) error_exit("failed pid size test");
snprintf(fmts_pid, sizeof(fmts_pid), "%%%uu ", digits);
Fieldstab[P_PID].head = " PID " + 10 - digits;
Fieldstab[P_PID].fmts = fmts_pid;
@ -1661,7 +1694,8 @@ static void zap_fieldstab (void) {
Fieldstab[P_CPN].head = "P ";
Fieldstab[P_CPN].fmts = "%1d ";
if (1 < (digits = (unsigned)snprintf(buf, sizeof(buf), "%d", Cpu_tot))) {
if (1 < (digits = (unsigned)snprintf(buf, sizeof(buf), "%u", (unsigned)Cpu_tot))) {
if (5 < digits) error_exit("failed num cpus test");
snprintf(fmts_cpu, sizeof(fmts_cpu), "%%%ud ", digits);
Fieldstab[P_CPN].head = " P " + 5 - digits;
Fieldstab[P_CPN].fmts = fmts_cpu;
@ -2042,7 +2076,7 @@ static void before (char *me) {
* line b: contains w->winflags, sortindx, maxtasks
* line c: contains w->summclr, msgsclr, headclr, taskclr */
static void configs_read (void) {
#ifdef FATAL_RCFILE
#ifndef RCFILE_NOERR
static const char err_rcid[] = "incompatible rcfile, you should delete '%s'";
static const char err_flds[] = "window entry #%d corrupt, please delete '%s'";
#else
@ -2075,7 +2109,7 @@ static void configs_read (void) {
"Mode_altscr=%d, Mode_irixps=%d, Delay_time=%f, Curwin=%d\n"
, &id, &Rc.mode_altscr, &Rc.mode_irixps, &tmp_delay, &i))
|| RCF_VERSION_ID != id)
#ifdef FATAL_RCFILE
#ifndef RCFILE_NOERR
error_exit(fmtmk(err_rcid, Rc_name));
#else
goto just_default_em;
@ -2092,7 +2126,7 @@ static void configs_read (void) {
# error "Hey, fix the above fscanf 'PFLAGSSIZ' dependency !"
#endif
if (strlen(Winstk[i].rc.fieldscur) != sizeof(DEF_FIELDS) - 1)
#ifdef FATAL_RCFILE
#ifndef RCFILE_NOERR
error_exit(fmtmk(err_flds, i+1, Rc_name));
#else
goto just_default_em;
@ -2100,7 +2134,7 @@ static void configs_read (void) {
for (x = 0; x < P_MAXPFLGS; ++x) {
int f = FLDget(&Winstk[i], x);
if (P_MAXPFLGS <= f)
#ifdef FATAL_RCFILE
#ifndef RCFILE_NOERR
error_exit(fmtmk(err_flds, i+1, Rc_name));
#else
goto just_default_em;
@ -2120,7 +2154,7 @@ static void configs_read (void) {
if (!Secure_mode) Rc.delay_time = tmp_delay;
return;
#ifndef FATAL_RCFILE
#ifdef RCFILE_NOERR
just_default_em:
fclose(fp);
Rc = rcdef;
@ -3167,6 +3201,7 @@ static void task_show (const WIN_t *q, const proc_t *p) {
int s = Fieldstab[i].scale; // string must be altered !
int w = Fieldstab[i].width;
if (!f) f = VARCOL_fmts; // ah ha, a variable width field
switch (i) {
#ifndef USE_X_COLHDR
// these 2 aren't real procflgs, they're used in column highlighting!
@ -3178,14 +3213,20 @@ static void task_show (const WIN_t *q, const proc_t *p) {
rp = scat(rp, X_XON == i ? q->capclr_rowhigh : q->capclr_rownorm);
continue;
#endif
case P_CGR:
{ char tmp[SCREENMAX];
parse_cgroup(tmp, sizeof(tmp), p);
makeCOL(q->varcolsz, q->varcolsz, tmp);
}
break;
case P_CMD:
{ char tmp[SCREENMAX];
unsigned flags;
int whackable_int = q->maxcmdln;
int whackable_int = q->varcolsz;
if (CHKw(q, Show_CMDLIN)) flags = ESC_DEFUNCT | ESC_BRACKETS | ESC_ARGS;
else flags = ESC_DEFUNCT;
escape_command(tmp, p, sizeof(tmp), &whackable_int, flags);
makeCOL(q->maxcmdln, q->maxcmdln, tmp);
makeCOL(q->varcolsz, q->varcolsz, tmp);
}
break;
case P_COD:

44
top.h
View File

@ -25,13 +25,13 @@
//#define CASEUP_HEXES /* show any hex values in upper case */
//#define CASEUP_SUFIX /* show time/mem/cnts suffix in upper case */
//#define EQUCOLHDRYES /* yes, do equalize column header lengths */
//#define FATAL_RCFILE /* rcfile errors are fatal, vs. silent fix */
//#define FIELD_CURSOR /* cursor follows selection w/ fields mgmt */
//#define OFF_HST_HASH /* use BOTH qsort+bsrch vs. hashing scheme */
//#define OFF_STDIOLBF /* disable our own stdout _IOFBF override */
//#define PRETEND2_5_X /* pretend we're linux 2.5.x (for IO-wait) */
//#define PRETEND4CPUS /* pretend we're smp with 4 ticsers (sic) */
//#define PRETENDNOCAP /* use a terminal without essential caps */
//#define RCFILE_NOERR /* rcfile errs silently default, vs. fatal */
//#define RESIZE_LIMIT /* enable limits on lower sigwinch bounds */
//#define RMAN_IGNORED /* don't consider auto right margin glitch */
//#define STRCMPNOCASE /* use strcasecmp vs. strcmp when sorting */
@ -136,7 +136,7 @@ enum pflag {
P_CPN, P_CPU, P_TME, P_TM2,
P_MEM, P_VRT, P_SWP, P_RES, P_COD, P_DAT, P_SHR,
P_FL1, P_FL2, P_DRT,
P_STA, P_CMD, P_WCH, P_FLG,
P_STA, P_CMD, P_WCH, P_FLG, P_CGR,
#ifdef ZAP_SUSEONLY
P_OOA, P_OOM,
#endif
@ -285,36 +285,34 @@ typedef struct RCF_t {
maintainence, the only real additional per frame cost of having
windows is an extra sort -- but that's just on pointers! */
typedef struct WIN_t {
int winnum, // window's num (array pos + 1)
winlines; // task window's rows (volatile)
FLG_t pflgsall [PFLAGSSIZ]; // all 'active/on' fieldscur, as enum
FLG_t procflgs [PFLAGSSIZ]; // fieldscur subset, as enum
int maxpflgs, // number of displayed procflgs ("on" in fieldscur)
FLG_t pflgsall [PFLAGSSIZ], // all 'active/on' fieldscur, as enum
procflgs [PFLAGSSIZ]; // fieldscur subset, as enum
RCW_t rc; // stuff that gets saved in the rcfile
int winnum, // a window's number (array pos + 1)
winlines, // current task window's rows (volatile)
maxpflgs, // number of displayed procflgs ("on" in fieldscur)
totpflgs, // total of displayable procflgs in pflgsall array
begpflg, // scrolled beginning pos into pflgsall array
endpflg, // scrolled ending pos into pflgsall array
begtask, // scrolled beginning pos into Frame_maxtask
maxcmdln; // max length of a process' command line
RCW_t rc; // stuff that gets saved in the rcfile
varcolsz, // max length of variable width column(s)
usrseluid, // validated uid for 'u/U' user selection
usrseltyp, // the basis for matching above uid
hdrcaplen; // column header xtra caps len, if any
char capclr_sum [CLRBUFSIZ], // terminfo strings built from
capclr_msg [CLRBUFSIZ], // RCW_t colors (& rebuilt too),
capclr_pmt [CLRBUFSIZ], // but NO recurring costs !
capclr_hdr [CLRBUFSIZ], // note: sum, msg and pmt strs
capclr_rowhigh [CLRBUFSIZ], // are only used when this
capclr_rownorm [CLRBUFSIZ]; // window is the 'Curwin'!
char cap_bold [CAPBUFSIZ]; // support for View_NOBOLD toggle
int usrseluid, // validated uid for 'u/U' select
usrseltyp; // basis for matching above uid
char grpname [GRPNAMSIZ], // window number:name, printable
capclr_rownorm [CLRBUFSIZ], // window is the 'Curwin'!
cap_bold [CAPBUFSIZ], // support for View_NOBOLD toggle
grpname [GRPNAMSIZ], // window number:name, printable
#ifdef USE_X_COLHDR
columnhdr [ROWMINSIZ]; // column headings for procflgs
#ifdef EQUCOLHDRYES
int hdrcaplen; // xtra length of caps strings
#endif
columnhdr [ROWMINSIZ], // column headings for procflgs
#else
columnhdr [SCREENMAX]; // column headings for procflgs
columnhdr [SCREENMAX], // column headings for procflgs
#endif
char *captab [CAPTABMAX]; // captab needed by show_special()
*captab [CAPTABMAX]; // captab needed by show_special()
struct WIN_t *next, // next window in window stack
*prev; // prior window in window stack
} WIN_t;
@ -459,6 +457,10 @@ typedef struct WIN_t {
"Usr", USR_FIELDS } \
} }
/* The format string used with variable width columns --
see 'calibrate_fields' for supporting logic. */
#define VARCOL_fmts "%-*.*s "
/* Summary Lines specially formatted string(s) --
see 'show_special' for syntax details + other cautions. */
#define LOADAV_line "%s -%s\n"
@ -633,6 +635,8 @@ typedef struct WIN_t {
/*###### Some Prototypes (ha!) #########################################*/
/* These 'prototypes' are here solely for documentation purposes */
/*------ Temporary Placement -------------------------------------------*/
//atic void parse_cgroup (char *dst, size_t max, const proc_t *p);
/*------ Sort callbacks ------------------------------------------------*/
/* for each possible field, in the form of: */
/*atic int sort_P_XXX (const proc_t **P, const proc_t **Q); */