From bb4f08ba297a67a043f7547670aa8623b54c2e67 Mon Sep 17 00:00:00 2001 From: Craig Small Date: Thu, 11 Aug 2011 07:42:14 +1000 Subject: [PATCH] refined library i/f and improved performance Library changes readproc . added readeither to more efficiently fill both process and thread proc_t . added readproctab3, uses readeither . included task path support in: fill_cgroup_cvt, fill_cmdline_cvt, read_unvectored . QUICK_THREADS #define allows copying process info vs. repeatedly reading . simple_nexttid no longer values ppid . path var made non-static in readtask . documented 'proc_data_t' in .h file . tweaked some c comments & formatting library.map . added new readeither, readproctab3 Program changes ps . exploits readproctab3 where possible . improved args/comm compliance top . exploits readeither --- proc/library.map | 2 +- proc/readproc.c | 317 ++++++++++++++++++++++++++++++++--------------- proc/readproc.h | 35 +++--- ps/display.c | 7 +- ps/output.c | 45 +++++-- top.c | 56 +++------ 6 files changed, 292 insertions(+), 170 deletions(-) diff --git a/proc/library.map b/proc/library.map index fe31dacc..199b9ac8 100644 --- a/proc/library.map +++ b/proc/library.map @@ -7,7 +7,7 @@ global: readproc; readtask; readproctab; readproctab2; look_up_our_self; escape_command; escape_str; escape_strlist; escaped_copy; read_cmdline; - openproc; closeproc; freeproc; + openproc; closeproc; freeproc; readeither; readproctab3; tty_to_dev; dev_to_tty; open_psdb_message; open_psdb; lookup_wchan; display_version; procps_version; linux_version_code; Hertz; smp_num_cpus; have_privs; getbtime; diff --git a/proc/readproc.c b/proc/readproc.c index d30de742..0595f366 100644 --- a/proc/readproc.c +++ b/proc/readproc.c @@ -38,6 +38,12 @@ extern void __cyg_profile_func_enter(void*,void*); #define LEAVE(x) #endif +#ifdef QUICK_THREADS +// used when multi-threaded and some memory must not be freed +#define MK_THREAD(q) q->pad_1 = '\xee' +#define IS_THREAD(q) ( q->pad_1 == '\xee' ) +#endif + #ifndef SIGNAL_STRING // convert hex string to unsigned long long static unsigned long long unhex(const char *restrict cp){ @@ -56,11 +62,17 @@ static int task_dir_missing; // free any additional dynamically acquired storage associated with a proc_t // ( and if it's to be reused, refresh it otherwise destroy it ) static inline void free_acquired (proc_t *p, int reuse) { - if (p->environ) free((void*)*p->environ); - if (p->cmdline) free((void*)*p->cmdline); - if (p->cgroup) free((void*)*p->cgroup); - if (p->supgid) free(p->supgid); - if (p->supgrp) free(p->supgrp); +#ifdef QUICK_THREADS + if (!IS_THREAD(p)) { +#endif + if (p->environ) free((void*)*p->environ); + if (p->cmdline) free((void*)*p->cmdline); + if (p->cgroup) free((void*)*p->cgroup); + if (p->supgid) free(p->supgid); + if (p->supgrp) free(p->supgrp); +#ifdef QUICK_THREADS + } +#endif memset(p, reuse ? '\0' : '\xff', sizeof(*p)); } @@ -571,14 +583,15 @@ static char** file2strvec(const char* directory, const char* what) { // 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]; +static int read_unvectored(char *restrict const dst, unsigned sz, const char* whom, const char *what, char sep) { + char path[PROCPATHLEN]; int fd; unsigned n = 0; - snprintf(name, sizeof name, "/proc/%u/%s", pid, what); - fd = open(name, O_RDONLY); + snprintf(path, sizeof(path), "%s/%s", whom, what); + fd = open(path, O_RDONLY); if(fd==-1) return 0; + for(;;){ ssize_t r = read(fd,dst+n,sz-n); if(r==-1){ @@ -618,17 +631,17 @@ static char** vectorize_this_str (const char* src) { #undef pSZ } - // This routine reads /proc/#/cgroup for a single task. + // This routine reads a 'cgroup' for the designated proc_t. // 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) { +static void fill_cgroup_cvt (const char* directory, 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'); + tot = read_unvectored(sbuf, sizeof(sbuf), directory, "cgroup", '\0'); for (src = sbuf, eob = sbuf + tot; src < eob; src += x) { x = 1; // loop assist if (!*src) continue; @@ -644,15 +657,15 @@ static void fill_cgroup_cvt (proc_t *restrict p) { #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) { + // This routine reads a 'cmdline' for the designated proc_t, "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 (const char* directory, 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", ' ')) + if (read_unvectored(sbuf, sizeof(sbuf), directory, "cmdline", ' ')) escape_str(dbuf, sbuf, sizeof(dbuf), &whackable_int); else escape_command(dbuf, p, sizeof(dbuf), &whackable_int, uFLG); @@ -662,7 +675,9 @@ static void fill_cmdline_cvt (proc_t *restrict p) { // warning: interface may change int read_cmdline(char *restrict const dst, unsigned sz, unsigned pid) { - return read_unvectored(dst, sz, pid, "cmdline", ' '); + char path[PROCPATHLEN]; + snprintf(path, sizeof(path), "/proc/%u", pid); + return read_unvectored(dst, sz, path, "cmdline", ' '); } @@ -706,18 +721,18 @@ static proc_t* simple_readproc(PROCTAB *restrict const PT, proc_t *restrict cons p->egid = sb.st_gid; /* need a way to get real gid */ if (flags & PROC_FILLSTAT) { // read /proc/#/stat - if (unlikely( file2str(path, "stat", sbuf, sizeof sbuf) == -1 )) + if (unlikely(file2str(path, "stat", sbuf, sizeof sbuf) == -1)) goto next_proc; stat2proc(sbuf, p); } if (flags & PROC_FILLMEM) { // read /proc/#/statm - if (likely(file2str(path, "statm", sbuf, sizeof sbuf) != -1 )) + if (likely(file2str(path, "statm", sbuf, sizeof sbuf) != -1)) statm2proc(sbuf, p); } if (flags & PROC_FILLSTATUS) { // read /proc/#/status - if (likely( file2str(path, "status", sbuf, sizeof sbuf) != -1 )){ + if (likely(file2str(path, "status", sbuf, sizeof sbuf) != -1)){ status2proc(sbuf, p, 1); if (flags & PROC_FILLSUPGRP) supgrps_from_supgids(p); @@ -756,7 +771,7 @@ static proc_t* simple_readproc(PROCTAB *restrict const PT, proc_t *restrict cons if (flags & (PROC_FILLCOM|PROC_FILLARG)) { // read /proc/#/cmdline if (flags & PROC_EDITCMDLCVT) - fill_cmdline_cvt(p); + fill_cmdline_cvt(path, p); else p->cmdline = file2strvec(path, "cmdline"); } else @@ -765,7 +780,7 @@ static proc_t* simple_readproc(PROCTAB *restrict const PT, proc_t *restrict cons if ((flags & PROC_FILLCGROUP) // read /proc/#/cgroup && linux_version_code >= LINUX_VERSION(2,6,24)) { if (flags & PROC_EDITCGRPCVT) - fill_cgroup_cvt(p); + fill_cgroup_cvt(path, p); else p->cgroup = file2strvec(path, "cgroup"); } else @@ -773,9 +788,9 @@ static proc_t* simple_readproc(PROCTAB *restrict const PT, proc_t *restrict cons #ifdef OOMEM_ENABLE if (unlikely(flags & PROC_FILLOOM)) { - if (likely( file2str(path, "oom_score", sbuf, sizeof sbuf) != -1 )) + if (likely(file2str(path, "oom_score", sbuf, sizeof sbuf) != -1)) oomscore2proc(sbuf, p); - if (likely( file2str(path, "oom_adj", sbuf, sizeof sbuf) != -1 )) + if (likely(file2str(path, "oom_adj", sbuf, sizeof sbuf) != -1)) oomadj2proc(sbuf, p); } #endif @@ -787,7 +802,11 @@ next_proc: ////////////////////////////////////////////////////////////////////////////////// // This reads /proc/*/task/* data, for one task. +#ifdef QUICK_THREADS +// p is the POSIX process (task group summary) & source for some copies if !NULL +#else // p is the POSIX process (task group summary) (not needed by THIS implementation) +#endif // t is the POSIX thread (task group member, generally not the leader) // path is a path to the task, with some room to spare. static proc_t* simple_readtask(PROCTAB *restrict const PT, const proc_t *restrict const p, proc_t *restrict const t, char *restrict const path) { @@ -798,39 +817,32 @@ static proc_t* simple_readtask(PROCTAB *restrict const PT, const proc_t *restric if (unlikely(stat(path, &sb) == -1)) /* no such dirent (anymore) */ goto next_task; -// if ((flags & PROC_UID) && !XinLN(uid_t, sb.st_uid, PT->uids, PT->nuid)) -// goto next_task; /* not one of the requested uids */ +// if ((flags & PROC_UID) && !XinLN(uid_t, sb.st_uid, PT->uids, PT->nuid)) +// goto next_task; /* not one of the requested uids */ t->euid = sb.st_uid; /* need a way to get real uid */ t->egid = sb.st_gid; /* need a way to get real gid */ - if (flags & PROC_FILLSTAT) { // read /proc/#/task/#/stat - if (unlikely( file2str(path, "stat", sbuf, sizeof sbuf) == -1 )) + if (flags & PROC_FILLSTAT) { // read /proc/#/task/#/stat + if (unlikely(file2str(path, "stat", sbuf, sizeof sbuf) == -1)) goto next_task; stat2proc(sbuf, t); } - if (flags & PROC_FILLMEM) { // read /proc/#/task/#statm -#if 1 - if (likely(file2str(path, "statm", sbuf, sizeof sbuf) != -1 )) +#ifndef QUICK_THREADS + if (flags & PROC_FILLMEM) // read /proc/#/task/#statm + if (likely(file2str(path, "statm", sbuf, sizeof sbuf) != -1)) statm2proc(sbuf, t); -#else - t->size = p->size; - t->resident = p->resident; - t->share = p->share; - t->trs = p->trs; - t->lrs = p->lrs; - t->drs = p->drs; - t->dt = p->dt; #endif - } - if (flags & PROC_FILLSTATUS) { // read /proc/#/task/#/status - if (likely( file2str(path, "status", sbuf, sizeof sbuf) != -1 )){ - status2proc(sbuf, t, 0); - if (flags & PROC_FILLSUPGRP) - supgrps_from_supgids(t); - } + if (flags & PROC_FILLSTATUS) { // read /proc/#/task/#/status + if (likely(file2str(path, "status", sbuf, sizeof sbuf) != -1)) { + status2proc(sbuf, t, 0); +#ifndef QUICK_THREADS + if (flags & PROC_FILLSUPGRP) + supgrps_from_supgids(t); +#endif + } } /* some number->text resolving which is time consuming */ @@ -853,47 +865,71 @@ static proc_t* simple_readtask(PROCTAB *restrict const PT, const proc_t *restric } } -#if 1 // begin active ------------------------ - if (unlikely(flags & PROC_FILLENV)) // read /proc/#/task/#/environ - t->environ = file2strvec(path, "environ"); - else - t->environ = NULL; +#ifdef QUICK_THREADS + if (!p) { + if (flags & PROC_FILLMEM) + if (likely(file2str(path, "statm", sbuf, sizeof sbuf) != -1)) + statm2proc(sbuf, t); - if (flags & (PROC_FILLCOM|PROC_FILLARG)) { // read /proc/#/task/#/cmdline - if (flags & PROC_EDITCMDLCVT) - fill_cmdline_cvt(t); + if (flags & PROC_FILLSUPGRP) + supgrps_from_supgids(t); +#endif + if (unlikely(flags & PROC_FILLENV)) // read /proc/#/task/#/environ + t->environ = file2strvec(path, "environ"); else - t->cmdline = file2strvec(path, "cmdline"); - } else - t->cmdline = NULL; + t->environ = NULL; - if ((flags & PROC_FILLCGROUP) // read /proc/#/task/#/cgroup - && linux_version_code >= LINUX_VERSION(2,6,24)) { - if (flags & PROC_EDITCGRPCVT) - fill_cgroup_cvt(t); - else - t->cgroup = file2strvec(path, "cgroup"); - } else - t->cgroup = NULL; -#else // end active -------------------------- - t->cmdline = p->cmdline; // better not free these until done with all threads! - t->environ = p->environ; - t->cgroup = p->cgroup; - t->supgid = p->supgid; - t->supgrp = p->supgrp; -#error we DO NOT BURDEN library users with the above insanity ANYMORE ! -#endif // end inactive ------------------------ + if (flags & (PROC_FILLCOM|PROC_FILLARG)) { // read /proc/#/task/#/cmdline + if (flags & PROC_EDITCMDLCVT) + fill_cmdline_cvt(path, t); + else + t->cmdline = file2strvec(path, "cmdline"); + } else + t->cmdline = NULL; -#ifdef OOMEM_ENABLE - t->oom_score = p->oom_score; - t->oom_adj = p->oom_adj; + if ((flags & PROC_FILLCGROUP) // read /proc/#/task/#/cgroup + && linux_version_code >= LINUX_VERSION(2,6,24)) { + if (flags & PROC_EDITCGRPCVT) + fill_cgroup_cvt(path, t); + else + t->cgroup = file2strvec(path, "cgroup"); + } else + t->cgroup = NULL; + +#ifdef QUICK_THREADS + } else { + t->size = p->size; + t->resident = p->resident; + t->share = p->share; + t->trs = p->trs; + t->lrs = p->lrs; + t->drs = p->drs; + t->dt = p->dt; + t->cmdline = p->cmdline; // better not free these until done with all threads! + t->environ = p->environ; + t->cgroup = p->cgroup; + if (t->supgid) free(t->supgid); + t->supgid = p->supgid; + t->supgrp = p->supgrp; + MK_THREAD(t); + } #endif - t->ppid = p->ppid; // ought to put the per-task ppid somewhere +#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 t; next_task: return NULL; +#ifndef QUICK_THREADS + (void)p; +#endif } ////////////////////////////////////////////////////////////////////////////////// @@ -905,7 +941,7 @@ static int simple_nextpid(PROCTAB *restrict const PT, proc_t *restrict const p) for (;;) { ent = readdir(PT->procfs); if(unlikely(unlikely(!ent) || unlikely(!ent->d_name))) return 0; - if(likely( likely(*ent->d_name > '0') && likely(*ent->d_name <= '9') )) break; + if(likely(likely(*ent->d_name > '0') && likely(*ent->d_name <= '9'))) break; } p->tgid = strtoul(ent->d_name, NULL, 10); p->tid = p->tgid; @@ -932,11 +968,11 @@ static int simple_nexttid(PROCTAB *restrict const PT, const proc_t *restrict con for (;;) { ent = readdir(PT->taskdir); if(unlikely(unlikely(!ent) || unlikely(!ent->d_name))) return 0; - if(likely( likely(*ent->d_name > '0') && likely(*ent->d_name <= '9') )) break; + if(likely(likely(*ent->d_name > '0') && likely(*ent->d_name <= '9'))) break; } t->tid = strtoul(ent->d_name, NULL, 10); t->tgid = p->tgid; - t->ppid = p->ppid; // cover for kernel behavior? we want both actually...? +//t->ppid = p->ppid; // cover for kernel behavior? we want both actually...? snprintf(path, PROCPATHLEN, "/proc/%d/task/%s", p->tgid, ent->d_name); return 1; } @@ -947,7 +983,7 @@ static int simple_nexttid(PROCTAB *restrict const PT, const proc_t *restrict con static int listed_nextpid(PROCTAB *restrict const PT, proc_t *restrict const p) { char *restrict const path = PT->path; pid_t tgid = *(PT->pids)++; - if(likely( tgid )){ + if(likely(tgid)){ snprintf(path, PROCPATHLEN, "/proc/%d", tgid); p->tgid = tgid; p->tid = tgid; // they match for leaders @@ -983,7 +1019,7 @@ proc_t* readproc(PROCTAB *restrict const PT, proc_t *restrict p) { for(;;){ // fills in the path, plus p->tid and p->tgid - if (unlikely(! PT->finder(PT,p) )) goto out; + if (unlikely(!PT->finder(PT,p))) goto out; // go read the process data ret = PT->reader(PT,p); @@ -1002,7 +1038,7 @@ out: // pointer (boolean false). Use the passed buffer instead of allocating // space if it is non-NULL. proc_t* readtask(PROCTAB *restrict const PT, const proc_t *restrict const p, proc_t *restrict t) { - static char path[PROCPATHLEN]; // must hold /proc/2000222000/task/2000222000/cmdline + char path[PROCPATHLEN]; // must hold /proc/2000222000/task/2000222000/cmdline proc_t *ret; proc_t *saved_t; @@ -1011,27 +1047,36 @@ proc_t* readtask(PROCTAB *restrict const PT, const proc_t *restrict const p, pro else free_acquired(t, 1); // 1. got to fake a thread for old kernels - if(task_dir_missing) { +#ifdef QUICK_THREADS + // 2. for single-threaded processes, this is faster (but must patch up stuff that differs!) + if(task_dir_missing || p->nlwp < 2){ +#else + if(task_dir_missing){ +#endif if(PT->did_fake) goto out; PT->did_fake=1; memcpy(t,p,sizeof(proc_t)); // use the per-task pending, not per-tgid pending #ifdef SIGNAL_STRING - memcpy(&t->signal, &t->_sigpnd, sizeof t->signal); + memcpy(&t->signal, &t->_sigpnd, sizeof t->signal); #else - t->signal = t->_sigpnd; + t->signal = t->_sigpnd; #endif +#ifdef QUICK_THREADS + MK_THREAD(t); +#else t->environ = NULL; t->cmdline = vectorize_this_str("n/a"); t->cgroup = NULL; t->supgid = NULL; t->supgrp = NULL; +#endif return t; } for(;;){ // fills in the path, plus t->tid and t->tgid - if (unlikely(! PT->taskfinder(PT,p,t,path) )) goto out; // simple_nexttid + if (unlikely(!PT->taskfinder(PT,p,t,path))) goto out; // simple_nexttid // go read the task data ret = PT->taskreader(PT,p,t,path); // simple_readtask @@ -1043,6 +1088,48 @@ out: return NULL; } +////////////////////////////////////////////////////////////////////////////////// +// readeither: return a pointer to a proc_t filled with requested info about +// the next unique process or task available. If no more are available, +// return a null pointer (boolean false). Use the passed buffer instead +// of allocating space if it is non-NULL. +extern proc_t* readeither (PROCTAB *restrict const PT, proc_t *restrict x) { + static proc_t skel_p; // skeleton proc_t, only uses tid + tgid + static proc_t *new_p; // for process/task transitions + char path[PROCPATHLEN]; + proc_t *saved_x, *ret; + + saved_x = x; + if (!x) x = xcalloc(NULL, sizeof(*x)); + else free_acquired(x,1); + if (new_p) goto next_task; + +next_proc: + new_p = NULL; + for (;;) { + // fills in the PT->path, plus skel_p.tid and skel_p.tgid + if (!PT->finder(PT,&skel_p)) goto end_procs; // simple_nextpid + if (!task_dir_missing) break; + if ((ret = PT->reader(PT,x))) return ret; // simple_readproc + } + +next_task: + for (;;) { + // fills in our path, plus x->tid and x->tgid + if ((!(PT->taskfinder(PT,&skel_p,x,path))) // simple_nexttid + || (!(ret = PT->taskreader(PT,new_p,x,path)))) { // simple_readtask + goto next_proc; + } + if (!new_p) new_p = ret; + return ret; + } + +end_procs: + if (!saved_x) free(x); + return NULL; +} + + ////////////////////////////////////////////////////////////////////////////////// // initiate a process table scan @@ -1116,6 +1203,7 @@ void look_up_our_self(proc_t *p) { HIDDEN_ALIAS(readproc); HIDDEN_ALIAS(readtask); +HIDDEN_ALIAS(readeither); /* Convenient wrapper around openproc and readproc to slurp in the whole process * table subset satisfying the constraints of flags and the optional PID list. @@ -1155,6 +1243,7 @@ proc_t** readproctab(int flags, ...) { // Try again, this time with threads and selection. proc_data_t *readproctab2(int(*want_proc)(proc_t *buf), int(*want_task)(proc_t *buf), PROCTAB *restrict const PT) { + static proc_data_t pd; proc_t** ptab = NULL; unsigned n_proc_alloc = 0; unsigned n_proc = 0; @@ -1167,8 +1256,6 @@ proc_data_t *readproctab2(int(*want_proc)(proc_t *buf), int(*want_task)(proc_t * unsigned n_alloc = 0; unsigned long n_used = 0; - proc_data_t *pd; - for(;;){ proc_t *tmp; if(n_alloc == n_used){ @@ -1198,6 +1285,7 @@ proc_data_t *readproctab2(int(*want_proc)(proc_t *buf), int(*want_task)(proc_t * // have to move tmp too tmp = data+(tmp-old); //if(!data) return NULL; + memset(data+n_used+1, 0, sizeof(proc_t)*(n_alloc-(n_used+1))); } if(n_task_alloc == n_task){ //proc_t **old = ttab; @@ -1212,23 +1300,49 @@ proc_data_t *readproctab2(int(*want_proc)(proc_t *buf), int(*want_task)(proc_t * } } - pd = malloc(sizeof(proc_data_t)); - pd->proc = ptab; - pd->task = ttab; - pd->nproc = n_proc; - pd->ntask = n_task; + pd.proc = ptab; + pd.task = ttab; + pd.nproc = n_proc; + pd.ntask = n_task; if(PT->flags & PROC_LOOSE_TASKS){ - pd->tab = ttab; - pd->n = n_task; + pd.tab = ttab; + pd.n = n_task; }else{ - pd->tab = ptab; - pd->n = n_proc; + pd.tab = ptab; + pd.n = n_proc; } // change array indexes to pointers while(n_proc--) ptab[n_proc] = data+(long)(ptab[n_proc]); while(n_task--) ttab[n_task] = data+(long)(ttab[n_task]); - return pd; + return &pd; +} + +// Try try yet again, this time treating processes and threads the same... +proc_data_t *readproctab3 (int(*want_task)(proc_t *buf), PROCTAB *restrict const PT) { + static proc_data_t pd; + proc_t **tab = NULL; + unsigned n_alloc = 0; + unsigned n_used = 0; + proc_t *p = NULL; + + for (;;) { + if (n_alloc == n_used) { + n_alloc = n_alloc*5/4+30; // grow by over 25% + tab = realloc(tab,sizeof(proc_t*)*n_alloc); + } + // let this next guy allocate the necessary proc_t storage + // (or recycle it) since he can't tolerate realloc relocations + if (!(p = readeither_direct(PT,p))) break; + if (want_task(p)) { + tab[n_used++] = p; + p = NULL; + } + } + + pd.tab = tab; + pd.n = n_used; + return &pd; } /* @@ -1256,3 +1370,6 @@ proc_t * get_proc_stats(pid_t pid, proc_t *p) { return p; } + +#undef MK_THREAD +#undef IS_THREAD diff --git a/proc/readproc.h b/proc/readproc.h index 2d94afaa..5ba1463b 100644 --- a/proc/readproc.h +++ b/proc/readproc.h @@ -14,6 +14,7 @@ #include "pwcache.h" #define SIGNAL_STRING +#define QUICK_THREADS /* copy (vs. read) some thread info from parent proc_t */ EXTERN_C_BEGIN @@ -44,7 +45,11 @@ typedef struct proc_t { pcpu; // stat (special) %CPU usage (is not filled in by readproc!!!) char state, // stat,status single-char code for process state (S=sleeping) - pad_1, // n/a padding +#ifdef QUICK_THREADS + pad_1, // n/a padding (psst, also used if multi-threaded) +#else + pad_1, // n/a padding +#endif pad_2, // n/a padding pad_3; // n/a padding // 2nd 16 bytes @@ -185,16 +190,17 @@ typedef struct PROCTAB { // Initialize a PROCTAB structure holding needed call-to-call persistent data extern PROCTAB* openproc(int flags, ... /* pid_t*|uid_t*|dev_t*|char* [, int n] */ ); -typedef struct proc_data_t { - proc_t **tab; - proc_t **proc; - proc_t **task; - int n; - int nproc; - int ntask; -} proc_data_t; +typedef struct proc_data_t { // valued by: (else zero) + proc_t **tab; // readproctab2, readproctab3 + proc_t **proc; // readproctab2 + proc_t **task; // * readproctab2 + int n; // readproctab2, readproctab3 + int nproc; // readproctab2 + int ntask; // * readproctab2 +} proc_data_t; // * when PROC_LOOSE_TASKS set extern proc_data_t *readproctab2(int(*want_proc)(proc_t *buf), int(*want_task)(proc_t *buf), PROCTAB *restrict const PT); +extern proc_data_t *readproctab3(int(*want_task)(proc_t *buf), PROCTAB *restrict const PT); // Convenient wrapper around openproc and readproc to slurp in the whole process // table subset satisfying the constraints of flags and the optional PID list. @@ -207,16 +213,17 @@ extern void closeproc(PROCTAB* PT); // Retrieve the next process or task matching the criteria set by the openproc(). // -// Note: When NULL is used as the readproc 'p' or readtask 't' parameter, -// the library will allocate the necessary proc_t storage. +// Note: When NULL is used as the readproc 'p', readtask 't' or readeither 'x' +// parameter, the library will allocate the necessary proc_t storage. // -// Alternately, you may provide your own reuseable buffer address +// Alternatively, you may provide your own reuseable buffer address // in which case that buffer *MUST* be initialized to zero one time // only before first use. Thereafter, the library will manage such // a passed proc_t, freeing any additional acquired memory associated // with the previous process or thread. extern proc_t* readproc(PROCTAB *restrict const PT, proc_t *restrict p); extern proc_t* readtask(PROCTAB *restrict const PT, const proc_t *restrict const p, proc_t *restrict t); +extern proc_t* readeither(PROCTAB *restrict const PT, proc_t *restrict x); // warning: interface may change extern int read_cmdline(char *restrict const dst, unsigned sz, unsigned pid); @@ -254,9 +261,9 @@ extern proc_t * get_proc_stats(pid_t pid, proc_t *p); #define PROC_FILLARG 0x0100 // alloc and fill in `cmdline' #define PROC_FILLCGROUP 0x0200 // alloc and fill in `cgroup` #define PROC_FILLSUPGRP 0x0400 // resolve supplementary group id -> group name -#define PROC_FILLOOM 0x0800 // alloc and fill in oom_score, oom_adj +#define PROC_FILLOOM 0x0800 // fill in proc_t oom_score and oom_adj -#define PROC_LOOSE_TASKS 0x2000 // threat threads as if they were processes +#define PROC_LOOSE_TASKS 0x2000 // treat threads as if they were processes // Obsolete, consider only processes with one of the passed: #define PROC_PID 0x1000 // process id numbers ( 0 terminated) diff --git a/ps/display.c b/ps/display.c index acec0e00..8c22deaa 100644 --- a/ps/display.c +++ b/ps/display.c @@ -7,7 +7,7 @@ * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Library General Public License for more details. - */ + */ #include #include @@ -284,7 +284,6 @@ static void lists_and_needs(void){ } if(!unix_f_option){ proc_format_needs &= ~PROC_FILLCOM; - proc_format_needs |= PROC_EDITCMDLCVT; needs_for_sort &= ~PROC_FILLCOM; } // convert ARG to COM as a standard @@ -295,7 +294,7 @@ static void lists_and_needs(void){ if(bsd_e_option){ if(proc_format_needs&PROC_FILLCOM) proc_format_needs |= PROC_FILLENV; } - + /* FIXME broken filthy hack -- got to unify some stuff here */ if( ( (proc_format_needs|task_format_needs|needs_for_sort) & PROC_FILLWCHAN) && !wchan_is_number) if (open_psdb(namelist_file)) wchan_is_number = 1; @@ -508,7 +507,7 @@ static void fancy_spew(void){ } if(thread_flags & TF_loose_tasks){ - pd = readproctab2(want_this_proc_nop, want_this_proc_pcpu, ptp); + pd = readproctab3(want_this_proc_pcpu, ptp); }else{ pd = readproctab2(want_this_proc_pcpu, (void*)0xdeadbeaful, ptp); } diff --git a/ps/output.c b/ps/output.c index 46baa08a..6267e699 100644 --- a/ps/output.c +++ b/ps/output.c @@ -334,8 +334,8 @@ Modifications to the arguments are not shown. /* * "args", "cmd", "command" are all the same: long unless c * "comm", "ucmd", "ucomm" are all the same: short unless -f - * ( determinations are made in display.c, we just deal with results ) */ -static int pr_argcom(char *restrict const outbuf, const proc_t *restrict const pp){ + * ( determinations are made in display.c, we mostly deal with results ) */ +static int pr_args(char *restrict const outbuf, const proc_t *restrict const pp){ char *endp = outbuf; unsigned flags; int rightward=max_rightward; @@ -345,7 +345,33 @@ static int pr_argcom(char *restrict const outbuf, const proc_t *restrict const p endp += fh; rightward -= fh; } - if(pp->cmdline) + if(pp->cmdline && !bsd_c_option) + endp += escaped_copy(endp, *pp->cmdline, OUTBUF_SIZE, &rightward); + else + endp += escape_command(endp, pp, OUTBUF_SIZE, &rightward, ESC_DEFUNCT); + + if(bsd_e_option && rightward>1) { + if(pp->environ && *pp->environ) + endp += escape_strlist(endp, pp->environ, OUTBUF_SIZE, &rightward); + } + return max_rightward-rightward; +} + +/* + * "args", "cmd", "command" are all the same: long unless c + * "comm", "ucmd", "ucomm" are all the same: short unless -f + * ( determinations are made in display.c, we mostly deal with results ) */ +static int pr_comm(char *restrict const outbuf, const proc_t *restrict const pp){ + char *endp = outbuf; + unsigned flags; + int rightward=max_rightward; + + if(forest_prefix){ + int fh = forest_helper(outbuf); + endp += fh; + rightward -= fh; + } + if(pp->cmdline && unix_f_option) endp += escaped_copy(endp, *pp->cmdline, OUTBUF_SIZE, &rightward); else endp += escape_command(endp, pp, OUTBUF_SIZE, &rightward, ESC_DEFUNCT); @@ -354,7 +380,6 @@ static int pr_argcom(char *restrict const outbuf, const proc_t *restrict const p if(pp->environ && *pp->environ) endp += escape_strlist(endp, pp->environ, OUTBUF_SIZE, &rightward); } - //return endp - outbuf; return max_rightward-rightward; } @@ -1283,7 +1308,7 @@ static const format_struct format_array[] = { {"addr_1", "ADDR", pr_nop, sr_nop, 1, 0, LNX, AN|LEFT}, {"alarm", "ALARM", pr_alarm, sr_alarm, 5, 0, LNX, AN|RIGHT}, {"argc", "ARGC", pr_nop, sr_nop, 4, 0, LNX, PO|RIGHT}, -{"args", "COMMAND", pr_argcom, sr_cmd, 27, ARG, U98, PO|UNLIMITED}, /*command*/ +{"args", "COMMAND", pr_args, sr_cmd, 27, ARG, U98, PO|UNLIMITED}, /*command*/ {"atime", "TIME", pr_time, sr_nop, 8, 0, SOE, ET|RIGHT}, /*cputime*/ /* was 6 wide */ {"blocked", "BLOCKED", pr_sigmask, sr_nop, 9, 0, BSD, TO|SIGNAL}, /*sigmask*/ {"bnd", "BND", pr_nop, sr_nop, 1, 0, AIX, TO|RIGHT}, @@ -1295,11 +1320,11 @@ static const format_struct format_array[] = { {"class", "CLS", pr_class, sr_sched, 3, 0, XXX, TO|LEFT}, {"cls", "CLS", pr_class, sr_sched, 3, 0, HPU, TO|RIGHT}, /*says HPUX or RT*/ {"cmaj_flt", "-", pr_nop, sr_cmaj_flt, 1, 0, LNX, AN|RIGHT}, -{"cmd", "CMD", pr_argcom, sr_cmd, 27, ARG, DEC, PO|UNLIMITED}, /*ucomm*/ +{"cmd", "CMD", pr_args, sr_cmd, 27, ARG, DEC, PO|UNLIMITED}, /*ucomm*/ {"cmin_flt", "-", pr_nop, sr_cmin_flt, 1, 0, LNX, AN|RIGHT}, {"cnswap", "-", pr_nop, sr_nop, 1, 0, LNX, AN|RIGHT}, -{"comm", "COMMAND", pr_argcom, sr_cmd, 15, COM, U98, PO|UNLIMITED}, /*ucomm*/ -{"command", "COMMAND", pr_argcom, sr_cmd, 27, ARG, XXX, PO|UNLIMITED}, /*args*/ +{"comm", "COMMAND", pr_comm, sr_cmd, 15, COM, U98, PO|UNLIMITED}, /*ucomm*/ +{"command", "COMMAND", pr_args, sr_cmd, 27, ARG, XXX, PO|UNLIMITED}, /*args*/ {"context", "CONTEXT", pr_context, sr_nop, 31, 0, LNX, ET|LEFT}, {"cp", "CP", pr_cp, sr_pcpu, 3, 0, DEC, ET|RIGHT}, /*cpu*/ {"cpu", "CPU", pr_nop, sr_nop, 3, 0, BSD, AN|RIGHT}, /* FIXME ... HP-UX wants this as the CPU number for SMP? */ @@ -1481,8 +1506,8 @@ static const format_struct format_array[] = { {"tty4", "TTY", pr_tty4, sr_tty, 4, 0, LNX, PO|LEFT}, {"tty8", "TTY", pr_tty8, sr_tty, 8, 0, LNX, PO|LEFT}, {"u_procp", "UPROCP", pr_nop, sr_nop, 6, 0, DEC, AN|RIGHT}, -{"ucmd", "CMD", pr_argcom, sr_cmd, 15, COM, DEC, PO|UNLIMITED}, /*ucomm*/ -{"ucomm", "COMMAND", pr_argcom, sr_cmd, 15, COM, XXX, PO|UNLIMITED}, /*comm*/ +{"ucmd", "CMD", pr_comm, sr_cmd, 15, COM, DEC, PO|UNLIMITED}, /*ucomm*/ +{"ucomm", "COMMAND", pr_comm, sr_cmd, 15, COM, XXX, PO|UNLIMITED}, /*comm*/ {"uid", "UID", pr_euid, sr_euid, 5, 0, XXX, ET|RIGHT}, {"uid_hack", "UID", pr_euser, sr_euser, 8, USR, XXX, ET|USER}, {"umask", "UMASK", pr_nop, sr_nop, 5, 0, DEC, AN|RIGHT}, diff --git a/top.c b/top.c index 9487ed7b..89d5677d 100644 --- a/top.c +++ b/top.c @@ -104,7 +104,7 @@ static int No_ksyms = -1, // set to '0' if ksym avail, '1' otherwise Batch = 0, // batch mode, collect no input, dumb output Loops = -1, // number of iterations, -1 loops forever Secure_mode = 0, // set if some functionality restricted - Thread_mode = 0, // set w/ 'H' - show threads via readtask() + Thread_mode = 0, // set w/ 'H' - show threads via readeither() Width_mode = 0; // set w/ 'w' - potential output override /* Unchangeable cap's stuff built just once (if at all) and @@ -1913,57 +1913,31 @@ static proc_t **procs_refresh (proc_t **ppt) { proc_t *ptask = (proc_t *)-1; // first time, Force: (ii) unsigned curmax = 0; // every time (jeeze) PROCTAB* PT; - proc_t *pthrd; // for thread hack + proc_t*(*read_something)(PROCTAB*, proc_t*); prochlp(NULL); // prep for a new frame if (NULL == (PT = openproc(Frames_libflags, Monpids))) error_exit(fmtmk("failed openproc: %s", strerror(errno))); + read_something = Thread_mode ? readeither : readproc; // i) Allocated Chunks: *Existing* table; refresh + reuse - if (!Thread_mode) { - while (curmax < savmax) { - if (!(ptask = readproc(PT, ppt[curmax]))) break; - prochlp(ptask); // tally & complete this proc_t - ++curmax; - } - } - else { // acquire as separate threads - while (curmax < savmax) { - if (!(ptask = readproc(PT, NULL))) break; - while (curmax < savmax) { - if (!(pthrd = readtask(PT, ptask, ppt[curmax]))) break; - prochlp(pthrd); // tally & complete this thread - ++curmax; - } - freeproc(ptask); // readproc's proc_t not needed - } + while (curmax < savmax) { + if (!(ptask = read_something(PT, ppt[curmax]))) break; + prochlp(ptask); // tally & complete this proc_t + ++curmax; } // ii) Unallocated Chunks: *New* or *Existing* table; extend + fill - if (!Thread_mode) { - while (ptask) { - // realloc as we go, keeping 'ppt' ahead of 'currmax++' - ppt = alloc_r(ppt, (curmax + 1) * PTRsz); - // here, readproc will allocate the underlying proc_t stg - if ((ptask = readproc(PT, NULL))) { - prochlp(ptask); // tally & complete this proc_t - ppt[curmax++] = ptask; - } - } - } - else { // acquire as separate threads - while (ptask) { - if ((ptask = readproc(PT, NULL))) { - for (;;) { - ppt = alloc_r(ppt, (curmax + 1) * PTRsz); - if (!(pthrd = readtask(PT, ptask, NULL))) break; - prochlp(pthrd); // tally & complete this thread - ppt[curmax++] = pthrd; - } - freeproc(ptask); // readproc's proc_t not needed - } + while (ptask) { + // realloc as we go, keeping 'ppt' ahead of 'currmax++' + ppt = alloc_r(ppt, (curmax + 1) * PTRsz); + // here, the library will allocate the underlying proc_t stg + if ((ptask = read_something(PT, NULL))) { + prochlp(ptask); // tally & complete this proc_t + ppt[curmax++] = ptask; } } + closeproc(PT); // iii) Chunkless: make 'eot' entry, after ensuring proc_t exists