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

@@ -50,13 +50,6 @@ static int escape_str_utf8(char *restrict dst, const char *restrict src, int buf
my_cells++;
my_bytes++;
} else if (len==1) {
/* non-multibyte */
*(dst++) = isprint(*src) ? *src : '?';
src++;
my_cells++;
my_bytes++;
} else if (!iswprint(wc)) {
/* multibyte - no printable */
*(dst++) = '?';
@@ -98,7 +91,7 @@ static int escape_str_utf8(char *restrict dst, const char *restrict src, int buf
}
//fprintf(stdout, "cells: %d\n", my_cells);
}
*(dst++) = '\0';
*dst = '\0';
// fprintf(stderr, "maxcells: %d, my_cells; %d\n", *maxcells, my_cells);
@@ -114,14 +107,14 @@ int escape_str(char *restrict dst, const char *restrict src, int bufsize, int *m
int my_cells = 0;
int my_bytes = 0;
const char codes[] =
"Z-------------------------------"
"********************************"
"********************************"
"*******************************-"
"--------------------------------"
"********************************"
"********************************"
"********************************";
"Z..............................."
"||||||||||||||||||||||||||||||||"
"||||||||||||||||||||||||||||||||"
"|||||||||||||||||||||||||||||||."
"????????????????????????????????"
"????????????????????????????????"
"????????????????????????????????"
"????????????????????????????????";
#if (__GNU_LIBRARY__ >= 6)
static int utf_init=0;
@@ -131,9 +124,10 @@ int escape_str(char *restrict dst, const char *restrict src, int bufsize, int *m
char *enc = nl_langinfo(CODESET);
utf_init = enc && strcasecmp(enc, "UTF-8")==0 ? 1 : -1;
}
if (utf_init==1)
if (utf_init==1 && MB_CUR_MAX>1) {
/* UTF8 locales */
return escape_str_utf8(dst, src, bufsize, maxcells);
}
#endif
if(bufsize > *maxcells+1) bufsize=*maxcells+1; // FIXME: assumes 8-bit locale
@@ -143,12 +137,12 @@ int escape_str(char *restrict dst, const char *restrict src, int bufsize, int *m
break;
c = (unsigned char) *(src++);
if(!c) break;
if(codes[c]=='-') c='?';
if(codes[c]!='|') c=codes[c];
my_cells++;
my_bytes++;
*(dst++) = c;
}
*(dst++) = '\0';
*dst = '\0';
*maxcells -= my_cells;
return my_bytes; // bytes of text, excluding the NUL
@@ -214,3 +208,16 @@ int escape_command(char *restrict const outbuf, const proc_t *restrict const pp,
outbuf[end] = '\0';
return end; // bytes, not including the NUL
}
/////////////////////////////////////////////////
// copy an already 'escaped' string,
// using the traditional escape.h calling conventions
int escaped_copy(char *restrict dst, const char *restrict src, int bufsize, int *maxroom){
int n;
if (bufsize > *maxroom+1) bufsize = *maxroom+1;
n = snprintf(dst, bufsize, "%s", src);
if (n >= bufsize) n = bufsize-1;
*maxroom -= n;
return n;
}

View File

@@ -17,6 +17,7 @@ EXTERN_C_BEGIN
extern int escape_strlist(char *restrict dst, const char *restrict const *restrict src, size_t n, int *cells);
extern int escape_str(char *restrict dst, const char *restrict src, int bufsize, int *maxcells);
extern int escape_command(char *restrict const outbuf, const proc_t *restrict const pp, int bytes, int *cells, unsigned flags);
extern int escaped_copy(char *restrict dst, const char *restrict src, int bufsize, int *maxroom);
EXTERN_C_END
#endif

View File

@@ -6,7 +6,7 @@ global:
__cyg_profile_func_enter; __cyg_profile_func_exit; main;
readproc; readtask; readproctab; readproctab2; look_up_our_self; escape_command;
escape_str; escape_strlist;
escape_str; escape_strlist; escaped_copy; read_cmdline;
openproc; closeproc;
tty_to_dev; dev_to_tty; open_psdb_message; open_psdb; lookup_wchan;
display_version; procps_version; linux_version_code;

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:

View File

@@ -111,8 +111,9 @@ typedef struct proc_t {
cmin_flt, // stat cumulative min_flt of process and child processes
cmaj_flt; // stat cumulative maj_flt of process and child processes
char
**environ, // (special) environment string vector (/proc/#/environ)
**cmdline; // (special) command line string vector (/proc/#/cmdline)
**environ, // (special) environment string vector (/proc/#/environ)
**cmdline, // (special) command line string vector (/proc/#/cmdline)
**cgroup; // (special) cgroup string vector (/proc/#/cgroup)
char
// Be compatible: Digital allows 16 and NT allows 14 ???
euser[P_G_SZ], // stat(),status effective user name
@@ -140,11 +141,11 @@ typedef struct proc_t {
tpgid, // stat terminal process group id
exit_signal, // stat might not be SIGCHLD
processor; // stat current (or most recent?) CPU
#ifdef ZAP_SUSEONLY
int oom_score, // oom_score (badness for OOM killer)
oom_adj; // oom_adj (adjustment to OOM score)
#ifdef OOMEM_ENABLE
int
oom_score, // oom_score (badness for OOM killer)
oom_adj; // oom_adj (adjustment to OOM score)
#endif
char **cgroup; // cgroup current cgroup, looks like a classic filepath
} proc_t;
// PROCTAB: data structure holding the persistent information readproc needs
@@ -252,6 +253,7 @@ extern proc_t * get_proc_stats(pid_t pid, proc_t *p);
#define PROC_UID 0x4000 // user id numbers ( length needed )
#define PROC_EDITCGRPCVT 0x10000 // edit `cgroup' as single vector
#define PROC_EDITCMDLCVT 0x20000 // edit `cmdline' as single vector
// it helps to give app code a few spare bits
#define PROC_SPARE_1 0x01000000

View File

@@ -24,7 +24,7 @@
#include <netinet/in.h> /* htons */
#endif
#ifndef ZAP_SUSEONLY
#ifndef OOMEM_ENABLE
long smp_num_cpus; /* number of CPUs */
#endif
@@ -182,7 +182,7 @@ static void old_Hertz_hack(void){
setlocale(LC_NUMERIC, savelocale);
jiffies = user_j + nice_j + sys_j + other_j;
seconds = (up_1 + up_2) / 2;
#ifndef ZAP_SUSEONLY
#ifndef OOMEM_ENABLE
h = (unsigned)( (double)jiffies/seconds/smp_num_cpus );
#else
h = (unsigned)( (double)jiffies/seconds/smp_num_cpus() );
@@ -252,7 +252,7 @@ static int check_for_privs(void){
return !!rc;
}
#ifdef ZAP_SUSEONLY
#ifdef OOMEM_ENABLE
long smp_num_cpus(void)
{
static long _smp_num_cpus=-1; /* number of CPUs */
@@ -279,7 +279,7 @@ static void init_libproc(void) __attribute__((constructor));
static void init_libproc(void){
have_privs = check_for_privs();
init_Linux_version(); /* Must be called before we check code */
#ifndef ZAP_SUSEONLY
#ifndef OOMEM_ENABLE
// ought to count CPUs in /proc/stat instead of relying
// on glibc, which foolishly tries to parse /proc/cpuinfo
//

View File

@@ -7,7 +7,7 @@
EXTERN_C_BEGIN
extern unsigned long long Hertz; /* clock tick frequency */
#ifndef ZAP_SUSEONLY
#ifndef OOMEM_ENABLE
extern long smp_num_cpus; /* number of CPUs */
#else
extern long smp_num_cpus(void); /* number of CPUs */