Hi, I've written cgroup support for Jim's new top. It wasn't straightforward because top counts only with one resizeable string column (cmdline). Attached patch includes support for more resizeable columns and cgroup displaying. It probably needs some tuning because it parses cgroups array many times while sorting. I will change procps cgroup interface to avoid this issue if Jim accepts this patch. Jim, could you review this patch, please? Thanks. Jan
diff --git a/top.c b/top.c index d275019..84ca410 100644 --- a/top.c +++ b/top.c @@ -186,6 +186,44 @@ static int *PHash_sav = HHash_one, // alternating 'old/new' hash tables *PHash_new = HHash_two; #endif + +/* creates string from cgroup array */ +void parse_cgroup(char *output, size_t output_size, const proc_t *p, int max_len) +{ + char *endp = output; + + output[0] = 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 + char *ccgroup = strchr(*pcgroup, ':'); + if(ccgroup == NULL) + ccgroup = *pcgroup; + else + ccgroup++; + + if(endp != output) + endp += escape_str(endp, ";", output_size, &max_len); + + endp += escape_str(endp, ccgroup, output_size, &max_len); + + pcgroup++; + } + } + + if(endp == output) + strcpy(output, "-"); +} + /*###### Sort callbacks ################################################*/ /* @@ -206,6 +244,17 @@ static int SCB_NAME(CMD) (const proc_t **P, const proc_t **Q) { // this part also handles the compare if both are kernel threads return Frame_srtflg * STRSORTCMP((*Q)->cmd, (*P)->cmd); } + +static int SCB_NAME(CGR) (const proc_t **P, const proc_t **Q) { + char p[ROWBUFSIZ]; + char q[ROWBUFSIZ]; + + parse_cgroup(p, sizeof(p), *P, sizeof(p)); + parse_cgroup(q, sizeof(q), *Q, sizeof(q)); + + return Frame_srtflg * STRSORTCMP(q, p); +} + SCB_NUM1(COD, trs) SCB_NUMx(CPN, processor) SCB_NUM1(CPU, pcpu) @@ -1158,6 +1207,7 @@ static inline int user_matched (WIN_t *q, const proc_t *p) { #define L_statm PROC_FILLMEM #define L_status PROC_FILLSTATUS #define L_CMDLINE PROC_FILLARG +#define L_CGROUP PROC_FILLCGROUP #define L_EUSER PROC_FILLUSR #define L_OUSER PROC_FILLSTATUS | PROC_FILLUSR #define L_EGROUP PROC_FILLSTATUS | PROC_FILLGRP @@ -1224,10 +1274,11 @@ static FLD_t Fieldstab[] = { { "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 + { "CGROUP ", "%-*.*s ", -1, -1, SF(CGR), L_EITHER, "Control Group" } #undef SF }; @@ -1317,6 +1368,7 @@ static void calibrate_fields (void) { int x, hdrmax = 0; #endif int i, needpsdb = 0; + int strcount, divadder = 0; // block SIGWINCH signals while we do our thing... sigemptyset(&newss); @@ -1360,7 +1412,21 @@ static void calibrate_fields (void) { 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 */ w->maxpflgs = i; - w->maxcmdln = Screen_cols - (strlen(w->columnhdr) - strlen(Fieldstab[P_CMD].head)) - 1; + w->maxcmdln = Screen_cols - strlen(w->columnhdr); + + /* count string format columns */ + strcount = 0; + for (i = 0; i < w->maxpflgs; i++) { + if(w->procflgs[i] == P_CMD || w->procflgs[i] == P_CGR) { + strcount++; + w->maxcmdln += strlen(Fieldstab[w->procflgs[i]].head); + } + } + + strcount = strcount ? strcount : 1; + divadder = w->maxcmdln % strcount; + w->maxcmdln = w->maxcmdln/strcount - 1; + /* establish the field where all remaining fields would still fit within screen width, including a leading window number */ @@ -1373,7 +1439,7 @@ static void calibrate_fields (void) { if (Screen_cols < ((int)(s - w->columnhdr) + (int)strlen(h))) break; s = scat(s, h); } - w->endpflg = i ? i + 1 : i; + w->endpflg = w->maxpflgs ? w->maxpflgs + 1 : w->maxpflgs; /* finally, we can build the true run-time columns header, format the command column heading, if P_CMD is really being displayed, and @@ -1389,10 +1455,19 @@ static void calibrate_fields (void) { 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 if (P_CGR == f) { + Frames_libflags |= L_CGROUP; + s = scat(s, fmtmk(Fieldstab[P_CGR].fmts, w->maxcmdln, w->maxcmdln, h)); } else s = scat(s, h); Frames_libflags |= Fieldstab[w->procflgs[i]].lflg; } + + /* compensate maxcmdln division */ + if(divadder) { + s = scat(s, fmtmk("%*s", divadder, "")); + divadder = 0; + } #ifdef EQUCOLHDRYES // prepare to even out column header lengths... if (hdrmax < (x = strlen(w->columnhdr))) hdrmax = x; @@ -3108,6 +3183,12 @@ static void task_show (const WIN_t *q, const proc_t *p) { makeCOL(q->maxcmdln, q->maxcmdln, tmp); } break; + case P_CGR: + { char tmp[ROWBUFSIZ]; + parse_cgroup(tmp, sizeof(tmp), p, q->maxcmdln); + makeCOL(q->maxcmdln, q->maxcmdln, tmp); + } + break; case P_COD: makeCOL(scale_num(pages2K(p->trs), w, s)); break; diff --git a/top.h b/top.h index e3fb307..0a1e006 100644 --- a/top.h +++ b/top.h @@ -126,7 +126,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, // not really pflags, used with tbl indexing & col highlighting P_MAXPFLGS, X_XON, X_XOF };