enhanced libproc cgroup/cmdline support, exploited by top

Library Changes
. added PROC_EDITCMDLCVT flag
. added an internal (static) fill_cmdline_cvt function:
  - reads and "escapes" /proc/#/cmdline
  - returns result as a single string in a single vector
  - callers are guaranteed a cmdline (no more NULL)
. added vectorize_this_str function, exploited by
  fill_cgroup_cvt, fill_cmdline_cvt
. generalized read_cmdline function as read_unvectored, now
  exploited by fill_cgroup_cvt, fill_cmdline_cvt, read_cmdline
  ( cgroup and cmdline no longer need be converted to string )
  ( vectors before being transformed to final representation )
. fixed bug regarding skipped group numbers (when enabled)
. escape_str made responsible for all single byte translation
  with distinction between control chars + other unprintable
. added escaped_copy function for already escaped strings
. reorganized parts of proc_t to restore formatting standards
  ( displacement changes shouldn't matter with new version # )
. former ZAP_SUSEONLY #define now OOMEM_ENABLE
. added to library.map: escaped_copy; read_cmdline

Top Program Changes
. exploited the new PROC_EDITCMDLCVT provision
. eliminated now obsolete #include "proc/escape.h"
. changed the P_WCH display format if no kernel symbol table
. fixed very old bug in lflgs for out-of-view sort fields
. former ZAP_SUSEONLY #define now OOMEM_ENABLE

Ps Program Changes
. exploited the new PROC_EDITCMDLCVT provision
. exploited the new escaped_copy function
. consolidated pr_args and pr_comm into pr_argcom

Signed-off-by: Jan Görig <jgorig@redhat.com>
This commit is contained in:
Jim Warner
2011-05-18 10:33:44 +02:00
committed by Jan Görig
parent 8621387c77
commit 7b0fc19e9d
11 changed files with 197 additions and 193 deletions

View File

@ -12,6 +12,7 @@
#include "version.h"
#include "readproc.h"
#include "alloc.h"
#include "escape.h"
#include "pwcache.h"
#include "devname.h"
#include "procps.h"
@ -365,7 +366,7 @@ LEAVE(0x220);
}
///////////////////////////////////////////////////////////////////////
#ifdef ZAP_SUSEONLY
#ifdef OOMEM_ENABLE
static void oomscore2proc(const char* S, proc_t *restrict P)
{
sscanf(S, "%d", &P->oom_score);
@ -527,13 +528,15 @@ static char** file2strvec(const char* directory, const char* what) {
return ret;
}
// warning: interface may change
int read_cmdline(char *restrict const dst, unsigned sz, unsigned pid){
// this is the former under utilized 'read_cmdline', which has been
// generalized in support of these new libproc flags:
// PROC_EDITCGRPCVT, PROC_EDITCMDLCVT
static int read_unvectored(char *restrict const dst, unsigned sz, unsigned pid, const char *what, char sep) {
char name[32];
int fd;
unsigned n = 0;
dst[0] = '\0';
snprintf(name, sizeof name, "/proc/%u/cmdline", pid);
snprintf(name, sizeof name, "/proc/%u/%s", pid, what);
fd = open(name, O_RDONLY);
if(fd==-1) return 0;
for(;;){
@ -543,62 +546,85 @@ int read_cmdline(char *restrict const dst, unsigned sz, unsigned pid){
break;
}
n += r;
if(n==sz) break; // filled the buffer
if(n==sz) { // filled the buffer
--n; // make room for '\0'
break;
}
if(r==0) break; // EOF
}
close(fd);
if(n){
int i;
if(n==sz) n--;
dst[n] = '\0';
i=n;
while(i--){
int c = dst[i];
if(c<' ' || c>'~') dst[i]=' ';
}
int i=n;
while(i--)
if(dst[i]=='\n' || dst[i]=='\0') dst[i]=sep;
}
dst[n] = '\0';
return n;
}
// This routine reads /proc/#/cgroup for a single task.
// It is similar to file2strvec except we filter and concatenate
// the data into a single string represented as a single vector.
static char** fill_cgroup_cvt(const char* directory) {
#define vMAX ( sizeof(dbuf) - (int)(dst - dbuf) )
char sbuf[1024], dbuf[1024];
char *src, *dst, *grp, *eob, **ret, **q;
int align, tot, x;
static char** vectorize_this_str (const char* src) {
#define pSZ (sizeof(char*))
char *cpy, **vec;
int adj, tot;
*(dst = dbuf) = '\0'; // empty destination
tot = file2str(directory, "cgroup", sbuf, sizeof(sbuf));
if (0 < tot) { // ignore true errors
eob = sbuf + tot;
for (src = sbuf; src < eob; src++) // disappear those darn nl's
if ('\n' == *src) *src = 0;
for (src = sbuf; src < eob; src += x) {
x = 1; // loop assist
if (!*src) continue;
x = strlen((grp = src));
if ('/' == grp[x - 1]) continue; // skip empty root cgroups
#if 0 // ( undecided on the next! )
if (strchr(grp, ':')) ++grp; // jump past hierarchy number
#endif // ( we'll keep it for now! )
dst += snprintf(dst, vMAX, "%s%s", (dst > dbuf) ? "," : "", grp);
}
}
if (!dbuf[0]) strncpy(dbuf, "-", sizeof(dbuf));
tot = strlen(dbuf) + 1; // prep for our vectors
align = (sizeof(char*)-1) - ((tot + sizeof(char*)-1) & (sizeof(char*)-1));
dst = xcalloc(NULL, tot + align + (2 * sizeof(char*)));
strncpy(dst, dbuf, tot); // propogate our handiwork
eob = dst + tot + align; // point to vectors home
q = ret = (char**)(eob);
*q++ = dst; // point 1st vector to string
*q = 0; // delimit 2nd (last) vector
return ret; // ==> free(*ret) to dealloc
tot = strlen(src) + 1; // prep for our vectors
adj = (pSZ-1) - ((tot + pSZ-1) & (pSZ-1)); // calc alignment bytes
cpy = xcalloc(NULL, tot + adj + (2 * pSZ)); // get new larger buffer
snprintf(cpy, tot, "%s", src); // duplicate their string
vec = (char**)(cpy + tot + adj); // prep pointer to pointers
*vec = cpy; // point 1st vector to string
*(vec+1) = NULL; // null ptr 'list' delimit
return vec; // ==> free(*vec) to dealloc
#undef pSZ
}
// This routine reads /proc/#/cgroup for a single task.
// It is similar to file2strvec except we filter and concatenate
// the data into a single string represented as a single vector.
static void fill_cgroup_cvt (proc_t *restrict p) {
#define vMAX ( sizeof(dbuf) - (int)(dst - dbuf) )
char sbuf[1024], dbuf[1024];
char *src, *dst, *grp, *eob;
int tot, x, whackable_int = sizeof(dbuf);
*(dst = dbuf) = '\0'; // empty destination
tot = read_unvectored(sbuf, sizeof(sbuf), p->tid, "cgroup", '\0');
for (src = sbuf, eob = sbuf + tot; src < eob; src += x) {
x = 1; // loop assist
if (!*src) continue;
x = strlen((grp = src));
if ('/' == grp[x - 1]) continue; // skip empty root cgroups
#if 0
grp += strspn(grp, "0123456789:"); // jump past group number
#endif
dst += snprintf(dst, vMAX, "%s", (dst > dbuf) ? "," : "");
dst += escape_str(dst, grp, vMAX, &whackable_int);
}
p->cgroup = vectorize_this_str(dbuf[0] ? dbuf : "-");
#undef vMAX
}
// This routine reads /proc/#/cmdline for the designated task, "escapes"
// the result into a single string represented as a single vector and
// guarantees the caller a valid proc_t.cmdline pointer.
static void fill_cmdline_cvt (proc_t *restrict p) {
#define uFLG ( ESC_BRACKETS | ESC_DEFUNCT )
char sbuf[2048], dbuf[2048];
int whackable_int = sizeof(dbuf);
if (read_unvectored(sbuf, sizeof(sbuf), p->tid, "cmdline", ' '))
escape_str(dbuf, sbuf, sizeof(dbuf), &whackable_int);
else
escape_command(dbuf, p, sizeof(dbuf), &whackable_int, uFLG);
p->cmdline = vectorize_this_str(dbuf);
#undef uFLG
}
// warning: interface may change
int read_cmdline(char *restrict const dst, unsigned sz, unsigned pid) {
return read_unvectored(dst, sz, pid, "cmdline", ' ');
}
/* These are some nice GNU C expression subscope "inline" functions.
* The can be used with arbitrary types and evaluate their arguments
@ -681,31 +707,36 @@ static proc_t* simple_readproc(PROCTAB *restrict const PT, proc_t *restrict cons
}
}
if ((flags & PROC_FILLCOM) || (flags & PROC_FILLARG)) /* read+parse /proc/#/cmdline */
p->cmdline = file2strvec(path, "cmdline");
else
p->cmdline = NULL;
if (unlikely(flags & PROC_FILLENV)) /* read+parse /proc/#/environ */
p->environ = file2strvec(path, "environ");
if (unlikely(flags & PROC_FILLENV)) /* read /proc/#/environ */
p->environ = file2strvec(path, "environ");
else
p->environ = NULL;
#ifdef ZAP_SUSEONLY
if (unlikely(flags & PROC_FILLOOM)) {
if (likely( file2str(path, "oom_score", sbuf, sizeof sbuf) != -1 ))
oomscore2proc(sbuf, p);
if (likely( file2str(path, "oom_adj", sbuf, sizeof sbuf) != -1 ))
oomadj2proc(sbuf, p);
} /* struct has been zeroed out before, so no worries about clearing garbage here */
#endif
if(linux_version_code>=LINUX_VERSION(2,6,24) && (flags & PROC_FILLCGROUP)) {
if((flags & PROC_EDITCGRPCVT)) {
p->cgroup = fill_cgroup_cvt(path); /* read /proc/#/cgroup and edit results */
} else {
p->cgroup = file2strvec(path, "cgroup"); /* read /proc/#/cgroup */
}
if (flags & (PROC_FILLCOM|PROC_FILLARG)) { /* read /proc/#/cmdline */
if (flags & PROC_EDITCMDLCVT)
fill_cmdline_cvt(p);
else
p->cmdline = file2strvec(path, "cmdline");
} else
p->cgroup = NULL;
p->cmdline = NULL;
if ((flags & PROC_FILLCGROUP) /* read /proc/#/cgroup, if possible */
&& linux_version_code >= LINUX_VERSION(2,6,24)) {
if (flags & PROC_EDITCGRPCVT)
fill_cgroup_cvt(p);
else
p->cgroup = file2strvec(path, "cgroup");
} else
p->cgroup = NULL;
#ifdef OOMEM_ENABLE
if (unlikely(flags & PROC_FILLOOM)) {
if (likely( file2str(path, "oom_score", sbuf, sizeof sbuf) != -1 ))
oomscore2proc(sbuf, p);
if (likely( file2str(path, "oom_adj", sbuf, sizeof sbuf) != -1 ))
oomadj2proc(sbuf, p);
}
#endif
return p;
next_proc: