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
This commit is contained in:
Craig Small 2011-08-11 07:42:14 +10:00
parent 3ef4823f90
commit bb4f08ba29
6 changed files with 292 additions and 170 deletions

View File

@ -7,7 +7,7 @@ global:
readproc; readtask; readproctab; readproctab2; look_up_our_self; escape_command; readproc; readtask; readproctab; readproctab2; look_up_our_self; escape_command;
escape_str; escape_strlist; escaped_copy; read_cmdline; 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; tty_to_dev; dev_to_tty; open_psdb_message; open_psdb; lookup_wchan;
display_version; procps_version; linux_version_code; display_version; procps_version; linux_version_code;
Hertz; smp_num_cpus; have_privs; getbtime; Hertz; smp_num_cpus; have_privs; getbtime;

View File

@ -38,6 +38,12 @@ extern void __cyg_profile_func_enter(void*,void*);
#define LEAVE(x) #define LEAVE(x)
#endif #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 #ifndef SIGNAL_STRING
// convert hex string to unsigned long long // convert hex string to unsigned long long
static unsigned long long unhex(const char *restrict cp){ 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 // free any additional dynamically acquired storage associated with a proc_t
// ( and if it's to be reused, refresh it otherwise destroy it ) // ( and if it's to be reused, refresh it otherwise destroy it )
static inline void free_acquired (proc_t *p, int reuse) { static inline void free_acquired (proc_t *p, int reuse) {
#ifdef QUICK_THREADS
if (!IS_THREAD(p)) {
#endif
if (p->environ) free((void*)*p->environ); if (p->environ) free((void*)*p->environ);
if (p->cmdline) free((void*)*p->cmdline); if (p->cmdline) free((void*)*p->cmdline);
if (p->cgroup) free((void*)*p->cgroup); if (p->cgroup) free((void*)*p->cgroup);
if (p->supgid) free(p->supgid); if (p->supgid) free(p->supgid);
if (p->supgrp) free(p->supgrp); if (p->supgrp) free(p->supgrp);
#ifdef QUICK_THREADS
}
#endif
memset(p, reuse ? '\0' : '\xff', sizeof(*p)); 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 // this is the former under utilized 'read_cmdline', which has been
// generalized in support of these new libproc flags: // generalized in support of these new libproc flags:
// PROC_EDITCGRPCVT, PROC_EDITCMDLCVT // PROC_EDITCGRPCVT, PROC_EDITCMDLCVT
static int read_unvectored(char *restrict const dst, unsigned sz, unsigned pid, const char *what, char sep) { static int read_unvectored(char *restrict const dst, unsigned sz, const char* whom, const char *what, char sep) {
char name[32]; char path[PROCPATHLEN];
int fd; int fd;
unsigned n = 0; unsigned n = 0;
snprintf(name, sizeof name, "/proc/%u/%s", pid, what); snprintf(path, sizeof(path), "%s/%s", whom, what);
fd = open(name, O_RDONLY); fd = open(path, O_RDONLY);
if(fd==-1) return 0; if(fd==-1) return 0;
for(;;){ for(;;){
ssize_t r = read(fd,dst+n,sz-n); ssize_t r = read(fd,dst+n,sz-n);
if(r==-1){ if(r==-1){
@ -618,17 +631,17 @@ static char** vectorize_this_str (const char* src) {
#undef pSZ #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 // It is similar to file2strvec except we filter and concatenate
// the data into a single string represented as a single vector. // 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) ) #define vMAX ( sizeof(dbuf) - (int)(dst - dbuf) )
char sbuf[1024], dbuf[1024]; char sbuf[1024], dbuf[1024];
char *src, *dst, *grp, *eob; char *src, *dst, *grp, *eob;
int tot, x, whackable_int = sizeof(dbuf); int tot, x, whackable_int = sizeof(dbuf);
*(dst = dbuf) = '\0'; // empty destination *(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) { for (src = sbuf, eob = sbuf + tot; src < eob; src += x) {
x = 1; // loop assist x = 1; // loop assist
if (!*src) continue; if (!*src) continue;
@ -644,15 +657,15 @@ static void fill_cgroup_cvt (proc_t *restrict p) {
#undef vMAX #undef vMAX
} }
// This routine reads /proc/#/cmdline for the designated task, "escapes" // This routine reads a 'cmdline' for the designated proc_t, "escapes"
// the result into a single string represented as a single vector and // the result into a single string represented as a single vector
// guarantees the caller a valid proc_t.cmdline pointer. // and guarantees the caller a valid proc_t.cmdline pointer.
static void fill_cmdline_cvt (proc_t *restrict p) { static void fill_cmdline_cvt (const char* directory, proc_t *restrict p) {
#define uFLG ( ESC_BRACKETS | ESC_DEFUNCT ) #define uFLG ( ESC_BRACKETS | ESC_DEFUNCT )
char sbuf[2048], dbuf[2048]; char sbuf[2048], dbuf[2048];
int whackable_int = sizeof(dbuf); 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); escape_str(dbuf, sbuf, sizeof(dbuf), &whackable_int);
else else
escape_command(dbuf, p, sizeof(dbuf), &whackable_int, uFLG); 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 // warning: interface may change
int read_cmdline(char *restrict const dst, unsigned sz, unsigned pid) { 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 */ p->egid = sb.st_gid; /* need a way to get real gid */
if (flags & PROC_FILLSTAT) { // read /proc/#/stat 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; goto next_proc;
stat2proc(sbuf, p); stat2proc(sbuf, p);
} }
if (flags & PROC_FILLMEM) { // read /proc/#/statm 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); statm2proc(sbuf, p);
} }
if (flags & PROC_FILLSTATUS) { // read /proc/#/status 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); status2proc(sbuf, p, 1);
if (flags & PROC_FILLSUPGRP) if (flags & PROC_FILLSUPGRP)
supgrps_from_supgids(p); 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_FILLCOM|PROC_FILLARG)) { // read /proc/#/cmdline
if (flags & PROC_EDITCMDLCVT) if (flags & PROC_EDITCMDLCVT)
fill_cmdline_cvt(p); fill_cmdline_cvt(path, p);
else else
p->cmdline = file2strvec(path, "cmdline"); p->cmdline = file2strvec(path, "cmdline");
} else } 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 if ((flags & PROC_FILLCGROUP) // read /proc/#/cgroup
&& linux_version_code >= LINUX_VERSION(2,6,24)) { && linux_version_code >= LINUX_VERSION(2,6,24)) {
if (flags & PROC_EDITCGRPCVT) if (flags & PROC_EDITCGRPCVT)
fill_cgroup_cvt(p); fill_cgroup_cvt(path, p);
else else
p->cgroup = file2strvec(path, "cgroup"); p->cgroup = file2strvec(path, "cgroup");
} else } else
@ -773,9 +788,9 @@ static proc_t* simple_readproc(PROCTAB *restrict const PT, proc_t *restrict cons
#ifdef OOMEM_ENABLE #ifdef OOMEM_ENABLE
if (unlikely(flags & PROC_FILLOOM)) { 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); 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); oomadj2proc(sbuf, p);
} }
#endif #endif
@ -787,7 +802,11 @@ next_proc:
////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////
// This reads /proc/*/task/* data, for one task. // 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) // 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) // t is the POSIX thread (task group member, generally not the leader)
// path is a path to the task, with some room to spare. // 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) { static proc_t* simple_readtask(PROCTAB *restrict const PT, const proc_t *restrict const p, proc_t *restrict const t, char *restrict const path) {
@ -805,31 +824,24 @@ static proc_t* simple_readtask(PROCTAB *restrict const PT, const proc_t *restric
t->egid = sb.st_gid; /* need a way to get real gid */ t->egid = sb.st_gid; /* need a way to get real gid */
if (flags & PROC_FILLSTAT) { // read /proc/#/task/#/stat if (flags & PROC_FILLSTAT) { // read /proc/#/task/#/stat
if (unlikely( file2str(path, "stat", sbuf, sizeof sbuf) == -1 )) if (unlikely(file2str(path, "stat", sbuf, sizeof sbuf) == -1))
goto next_task; goto next_task;
stat2proc(sbuf, t); stat2proc(sbuf, t);
} }
if (flags & PROC_FILLMEM) { // read /proc/#/task/#statm #ifndef QUICK_THREADS
#if 1 if (flags & PROC_FILLMEM) // read /proc/#/task/#statm
if (likely(file2str(path, "statm", sbuf, sizeof sbuf) != -1 )) if (likely(file2str(path, "statm", sbuf, sizeof sbuf) != -1))
statm2proc(sbuf, t); 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 #endif
}
if (flags & PROC_FILLSTATUS) { // read /proc/#/task/#/status if (flags & PROC_FILLSTATUS) { // read /proc/#/task/#/status
if (likely( file2str(path, "status", sbuf, sizeof sbuf) != -1 )){ if (likely(file2str(path, "status", sbuf, sizeof sbuf) != -1)) {
status2proc(sbuf, t, 0); status2proc(sbuf, t, 0);
#ifndef QUICK_THREADS
if (flags & PROC_FILLSUPGRP) if (flags & PROC_FILLSUPGRP)
supgrps_from_supgids(t); supgrps_from_supgids(t);
#endif
} }
} }
@ -853,7 +865,15 @@ static proc_t* simple_readtask(PROCTAB *restrict const PT, const proc_t *restric
} }
} }
#if 1 // begin active ------------------------ #ifdef QUICK_THREADS
if (!p) {
if (flags & PROC_FILLMEM)
if (likely(file2str(path, "statm", sbuf, sizeof sbuf) != -1))
statm2proc(sbuf, t);
if (flags & PROC_FILLSUPGRP)
supgrps_from_supgids(t);
#endif
if (unlikely(flags & PROC_FILLENV)) // read /proc/#/task/#/environ if (unlikely(flags & PROC_FILLENV)) // read /proc/#/task/#/environ
t->environ = file2strvec(path, "environ"); t->environ = file2strvec(path, "environ");
else else
@ -861,7 +881,7 @@ static proc_t* simple_readtask(PROCTAB *restrict const PT, const proc_t *restric
if (flags & (PROC_FILLCOM|PROC_FILLARG)) { // read /proc/#/task/#/cmdline if (flags & (PROC_FILLCOM|PROC_FILLARG)) { // read /proc/#/task/#/cmdline
if (flags & PROC_EDITCMDLCVT) if (flags & PROC_EDITCMDLCVT)
fill_cmdline_cvt(t); fill_cmdline_cvt(path, t);
else else
t->cmdline = file2strvec(path, "cmdline"); t->cmdline = file2strvec(path, "cmdline");
} else } else
@ -870,30 +890,46 @@ static proc_t* simple_readtask(PROCTAB *restrict const PT, const proc_t *restric
if ((flags & PROC_FILLCGROUP) // read /proc/#/task/#/cgroup if ((flags & PROC_FILLCGROUP) // read /proc/#/task/#/cgroup
&& linux_version_code >= LINUX_VERSION(2,6,24)) { && linux_version_code >= LINUX_VERSION(2,6,24)) {
if (flags & PROC_EDITCGRPCVT) if (flags & PROC_EDITCGRPCVT)
fill_cgroup_cvt(t); fill_cgroup_cvt(path, t);
else else
t->cgroup = file2strvec(path, "cgroup"); t->cgroup = file2strvec(path, "cgroup");
} else } else
t->cgroup = NULL; t->cgroup = NULL;
#else // end active --------------------------
#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->cmdline = p->cmdline; // better not free these until done with all threads!
t->environ = p->environ; t->environ = p->environ;
t->cgroup = p->cgroup; t->cgroup = p->cgroup;
if (t->supgid) free(t->supgid);
t->supgid = p->supgid; t->supgid = p->supgid;
t->supgrp = p->supgrp; t->supgrp = p->supgrp;
#error we DO NOT BURDEN library users with the above insanity ANYMORE ! MK_THREAD(t);
#endif // end inactive ------------------------ }
#ifdef OOMEM_ENABLE
t->oom_score = p->oom_score;
t->oom_adj = p->oom_adj;
#endif #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; return t;
next_task: next_task:
return NULL; 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 (;;) { for (;;) {
ent = readdir(PT->procfs); ent = readdir(PT->procfs);
if(unlikely(unlikely(!ent) || unlikely(!ent->d_name))) return 0; 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->tgid = strtoul(ent->d_name, NULL, 10);
p->tid = p->tgid; p->tid = p->tgid;
@ -932,11 +968,11 @@ static int simple_nexttid(PROCTAB *restrict const PT, const proc_t *restrict con
for (;;) { for (;;) {
ent = readdir(PT->taskdir); ent = readdir(PT->taskdir);
if(unlikely(unlikely(!ent) || unlikely(!ent->d_name))) return 0; 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->tid = strtoul(ent->d_name, NULL, 10);
t->tgid = p->tgid; 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); snprintf(path, PROCPATHLEN, "/proc/%d/task/%s", p->tgid, ent->d_name);
return 1; 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) { static int listed_nextpid(PROCTAB *restrict const PT, proc_t *restrict const p) {
char *restrict const path = PT->path; char *restrict const path = PT->path;
pid_t tgid = *(PT->pids)++; pid_t tgid = *(PT->pids)++;
if(likely( tgid )){ if(likely(tgid)){
snprintf(path, PROCPATHLEN, "/proc/%d", tgid); snprintf(path, PROCPATHLEN, "/proc/%d", tgid);
p->tgid = tgid; p->tgid = tgid;
p->tid = tgid; // they match for leaders p->tid = tgid; // they match for leaders
@ -983,7 +1019,7 @@ proc_t* readproc(PROCTAB *restrict const PT, proc_t *restrict p) {
for(;;){ for(;;){
// fills in the path, plus p->tid and p->tgid // 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 // go read the process data
ret = PT->reader(PT,p); ret = PT->reader(PT,p);
@ -1002,7 +1038,7 @@ out:
// pointer (boolean false). Use the passed buffer instead of allocating // pointer (boolean false). Use the passed buffer instead of allocating
// space if it is non-NULL. // space if it is non-NULL.
proc_t* readtask(PROCTAB *restrict const PT, const proc_t *restrict const p, proc_t *restrict t) { 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 *ret;
proc_t *saved_t; proc_t *saved_t;
@ -1011,7 +1047,12 @@ proc_t* readtask(PROCTAB *restrict const PT, const proc_t *restrict const p, pro
else free_acquired(t, 1); else free_acquired(t, 1);
// 1. got to fake a thread for old kernels // 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; if(PT->did_fake) goto out;
PT->did_fake=1; PT->did_fake=1;
memcpy(t,p,sizeof(proc_t)); memcpy(t,p,sizeof(proc_t));
@ -1021,17 +1062,21 @@ proc_t* readtask(PROCTAB *restrict const PT, const proc_t *restrict const p, pro
#else #else
t->signal = t->_sigpnd; t->signal = t->_sigpnd;
#endif #endif
#ifdef QUICK_THREADS
MK_THREAD(t);
#else
t->environ = NULL; t->environ = NULL;
t->cmdline = vectorize_this_str("n/a"); t->cmdline = vectorize_this_str("n/a");
t->cgroup = NULL; t->cgroup = NULL;
t->supgid = NULL; t->supgid = NULL;
t->supgrp = NULL; t->supgrp = NULL;
#endif
return t; return t;
} }
for(;;){ for(;;){
// fills in the path, plus t->tid and t->tgid // 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 // go read the task data
ret = PT->taskreader(PT,p,t,path); // simple_readtask ret = PT->taskreader(PT,p,t,path); // simple_readtask
@ -1043,6 +1088,48 @@ out:
return NULL; 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 // initiate a process table scan
@ -1116,6 +1203,7 @@ void look_up_our_self(proc_t *p) {
HIDDEN_ALIAS(readproc); HIDDEN_ALIAS(readproc);
HIDDEN_ALIAS(readtask); HIDDEN_ALIAS(readtask);
HIDDEN_ALIAS(readeither);
/* Convenient wrapper around openproc and readproc to slurp in the whole process /* Convenient wrapper around openproc and readproc to slurp in the whole process
* table subset satisfying the constraints of flags and the optional PID list. * 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. // 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) { 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; proc_t** ptab = NULL;
unsigned n_proc_alloc = 0; unsigned n_proc_alloc = 0;
unsigned n_proc = 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 n_alloc = 0;
unsigned long n_used = 0; unsigned long n_used = 0;
proc_data_t *pd;
for(;;){ for(;;){
proc_t *tmp; proc_t *tmp;
if(n_alloc == n_used){ 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 // have to move tmp too
tmp = data+(tmp-old); tmp = data+(tmp-old);
//if(!data) return NULL; //if(!data) return NULL;
memset(data+n_used+1, 0, sizeof(proc_t)*(n_alloc-(n_used+1)));
} }
if(n_task_alloc == n_task){ if(n_task_alloc == n_task){
//proc_t **old = ttab; //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->proc = ptab; pd.task = ttab;
pd->task = ttab; pd.nproc = n_proc;
pd->nproc = n_proc; pd.ntask = n_task;
pd->ntask = n_task;
if(PT->flags & PROC_LOOSE_TASKS){ if(PT->flags & PROC_LOOSE_TASKS){
pd->tab = ttab; pd.tab = ttab;
pd->n = n_task; pd.n = n_task;
}else{ }else{
pd->tab = ptab; pd.tab = ptab;
pd->n = n_proc; pd.n = n_proc;
} }
// change array indexes to pointers // change array indexes to pointers
while(n_proc--) ptab[n_proc] = data+(long)(ptab[n_proc]); while(n_proc--) ptab[n_proc] = data+(long)(ptab[n_proc]);
while(n_task--) ttab[n_task] = data+(long)(ttab[n_task]); 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; return p;
} }
#undef MK_THREAD
#undef IS_THREAD

View File

@ -14,6 +14,7 @@
#include "pwcache.h" #include "pwcache.h"
#define SIGNAL_STRING #define SIGNAL_STRING
#define QUICK_THREADS /* copy (vs. read) some thread info from parent proc_t */
EXTERN_C_BEGIN EXTERN_C_BEGIN
@ -44,7 +45,11 @@ typedef struct proc_t {
pcpu; // stat (special) %CPU usage (is not filled in by readproc!!!) pcpu; // stat (special) %CPU usage (is not filled in by readproc!!!)
char char
state, // stat,status single-char code for process state (S=sleeping) state, // stat,status single-char code for process state (S=sleeping)
#ifdef QUICK_THREADS
pad_1, // n/a padding (psst, also used if multi-threaded)
#else
pad_1, // n/a padding pad_1, // n/a padding
#endif
pad_2, // n/a padding pad_2, // n/a padding
pad_3; // n/a padding pad_3; // n/a padding
// 2nd 16 bytes // 2nd 16 bytes
@ -185,16 +190,17 @@ typedef struct PROCTAB {
// Initialize a PROCTAB structure holding needed call-to-call persistent data // 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] */ ); extern PROCTAB* openproc(int flags, ... /* pid_t*|uid_t*|dev_t*|char* [, int n] */ );
typedef struct proc_data_t { typedef struct proc_data_t { // valued by: (else zero)
proc_t **tab; proc_t **tab; // readproctab2, readproctab3
proc_t **proc; proc_t **proc; // readproctab2
proc_t **task; proc_t **task; // * readproctab2
int n; int n; // readproctab2, readproctab3
int nproc; int nproc; // readproctab2
int ntask; int ntask; // * readproctab2
} proc_data_t; } 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 *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 // Convenient wrapper around openproc and readproc to slurp in the whole process
// table subset satisfying the constraints of flags and the optional PID list. // 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(). // 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, // Note: When NULL is used as the readproc 'p', readtask 't' or readeither 'x'
// the library will allocate the necessary proc_t storage. // 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 // in which case that buffer *MUST* be initialized to zero one time
// only before first use. Thereafter, the library will manage such // only before first use. Thereafter, the library will manage such
// a passed proc_t, freeing any additional acquired memory associated // a passed proc_t, freeing any additional acquired memory associated
// with the previous process or thread. // with the previous process or thread.
extern proc_t* readproc(PROCTAB *restrict const PT, proc_t *restrict p); 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* 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 // warning: interface may change
extern int read_cmdline(char *restrict const dst, unsigned sz, unsigned pid); 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_FILLARG 0x0100 // alloc and fill in `cmdline'
#define PROC_FILLCGROUP 0x0200 // alloc and fill in `cgroup` #define PROC_FILLCGROUP 0x0200 // alloc and fill in `cgroup`
#define PROC_FILLSUPGRP 0x0400 // resolve supplementary group id -> group name #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: // Obsolete, consider only processes with one of the passed:
#define PROC_PID 0x1000 // process id numbers ( 0 terminated) #define PROC_PID 0x1000 // process id numbers ( 0 terminated)

View File

@ -284,7 +284,6 @@ static void lists_and_needs(void){
} }
if(!unix_f_option){ if(!unix_f_option){
proc_format_needs &= ~PROC_FILLCOM; proc_format_needs &= ~PROC_FILLCOM;
proc_format_needs |= PROC_EDITCMDLCVT;
needs_for_sort &= ~PROC_FILLCOM; needs_for_sort &= ~PROC_FILLCOM;
} }
// convert ARG to COM as a standard // convert ARG to COM as a standard
@ -508,7 +507,7 @@ static void fancy_spew(void){
} }
if(thread_flags & TF_loose_tasks){ 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{ }else{
pd = readproctab2(want_this_proc_pcpu, (void*)0xdeadbeaful, ptp); pd = readproctab2(want_this_proc_pcpu, (void*)0xdeadbeaful, ptp);
} }

View File

@ -334,8 +334,8 @@ Modifications to the arguments are not shown.
/* /*
* "args", "cmd", "command" are all the same: long unless c * "args", "cmd", "command" are all the same: long unless c
* "comm", "ucmd", "ucomm" are all the same: short unless -f * "comm", "ucmd", "ucomm" are all the same: short unless -f
* ( determinations are made in display.c, we just deal with results ) */ * ( determinations are made in display.c, we mostly deal with results ) */
static int pr_argcom(char *restrict const outbuf, const proc_t *restrict const pp){ static int pr_args(char *restrict const outbuf, const proc_t *restrict const pp){
char *endp = outbuf; char *endp = outbuf;
unsigned flags; unsigned flags;
int rightward=max_rightward; int rightward=max_rightward;
@ -345,7 +345,33 @@ static int pr_argcom(char *restrict const outbuf, const proc_t *restrict const p
endp += fh; endp += fh;
rightward -= 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); endp += escaped_copy(endp, *pp->cmdline, OUTBUF_SIZE, &rightward);
else else
endp += escape_command(endp, pp, OUTBUF_SIZE, &rightward, ESC_DEFUNCT); 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) if(pp->environ && *pp->environ)
endp += escape_strlist(endp, pp->environ, OUTBUF_SIZE, &rightward); endp += escape_strlist(endp, pp->environ, OUTBUF_SIZE, &rightward);
} }
//return endp - outbuf;
return max_rightward-rightward; 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}, {"addr_1", "ADDR", pr_nop, sr_nop, 1, 0, LNX, AN|LEFT},
{"alarm", "ALARM", pr_alarm, sr_alarm, 5, 0, LNX, AN|RIGHT}, {"alarm", "ALARM", pr_alarm, sr_alarm, 5, 0, LNX, AN|RIGHT},
{"argc", "ARGC", pr_nop, sr_nop, 4, 0, LNX, PO|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 */ {"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*/ {"blocked", "BLOCKED", pr_sigmask, sr_nop, 9, 0, BSD, TO|SIGNAL}, /*sigmask*/
{"bnd", "BND", pr_nop, sr_nop, 1, 0, AIX, TO|RIGHT}, {"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}, {"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*/ {"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}, {"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}, {"cmin_flt", "-", pr_nop, sr_cmin_flt, 1, 0, LNX, AN|RIGHT},
{"cnswap", "-", pr_nop, sr_nop, 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*/ {"comm", "COMMAND", pr_comm, sr_cmd, 15, COM, U98, PO|UNLIMITED}, /*ucomm*/
{"command", "COMMAND", pr_argcom, sr_cmd, 27, ARG, XXX, PO|UNLIMITED}, /*args*/ {"command", "COMMAND", pr_args, sr_cmd, 27, ARG, XXX, PO|UNLIMITED}, /*args*/
{"context", "CONTEXT", pr_context, sr_nop, 31, 0, LNX, ET|LEFT}, {"context", "CONTEXT", pr_context, sr_nop, 31, 0, LNX, ET|LEFT},
{"cp", "CP", pr_cp, sr_pcpu, 3, 0, DEC, ET|RIGHT}, /*cpu*/ {"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? */ {"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}, {"tty4", "TTY", pr_tty4, sr_tty, 4, 0, LNX, PO|LEFT},
{"tty8", "TTY", pr_tty8, sr_tty, 8, 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}, {"u_procp", "UPROCP", pr_nop, sr_nop, 6, 0, DEC, AN|RIGHT},
{"ucmd", "CMD", pr_argcom, sr_cmd, 15, COM, DEC, PO|UNLIMITED}, /*ucomm*/ {"ucmd", "CMD", pr_comm, sr_cmd, 15, COM, DEC, PO|UNLIMITED}, /*ucomm*/
{"ucomm", "COMMAND", pr_argcom, sr_cmd, 15, COM, XXX, PO|UNLIMITED}, /*comm*/ {"ucomm", "COMMAND", pr_comm, sr_cmd, 15, COM, XXX, PO|UNLIMITED}, /*comm*/
{"uid", "UID", pr_euid, sr_euid, 5, 0, XXX, ET|RIGHT}, {"uid", "UID", pr_euid, sr_euid, 5, 0, XXX, ET|RIGHT},
{"uid_hack", "UID", pr_euser, sr_euser, 8, USR, XXX, ET|USER}, {"uid_hack", "UID", pr_euser, sr_euser, 8, USR, XXX, ET|USER},
{"umask", "UMASK", pr_nop, sr_nop, 5, 0, DEC, AN|RIGHT}, {"umask", "UMASK", pr_nop, sr_nop, 5, 0, DEC, AN|RIGHT},

40
top.c
View File

@ -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 Batch = 0, // batch mode, collect no input, dumb output
Loops = -1, // number of iterations, -1 loops forever Loops = -1, // number of iterations, -1 loops forever
Secure_mode = 0, // set if some functionality restricted 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 Width_mode = 0; // set w/ 'w' - potential output override
/* Unchangeable cap's stuff built just once (if at all) and /* 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) proc_t *ptask = (proc_t *)-1; // first time, Force: (ii)
unsigned curmax = 0; // every time (jeeze) unsigned curmax = 0; // every time (jeeze)
PROCTAB* PT; PROCTAB* PT;
proc_t *pthrd; // for thread hack proc_t*(*read_something)(PROCTAB*, proc_t*);
prochlp(NULL); // prep for a new frame prochlp(NULL); // prep for a new frame
if (NULL == (PT = openproc(Frames_libflags, Monpids))) if (NULL == (PT = openproc(Frames_libflags, Monpids)))
error_exit(fmtmk("failed openproc: %s", strerror(errno))); error_exit(fmtmk("failed openproc: %s", strerror(errno)));
read_something = Thread_mode ? readeither : readproc;
// i) Allocated Chunks: *Existing* table; refresh + reuse // i) Allocated Chunks: *Existing* table; refresh + reuse
if (!Thread_mode) {
while (curmax < savmax) { while (curmax < savmax) {
if (!(ptask = readproc(PT, ppt[curmax]))) break; if (!(ptask = read_something(PT, ppt[curmax]))) break;
prochlp(ptask); // tally & complete this proc_t prochlp(ptask); // tally & complete this proc_t
++curmax; ++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
}
}
// ii) Unallocated Chunks: *New* or *Existing* table; extend + fill // ii) Unallocated Chunks: *New* or *Existing* table; extend + fill
if (!Thread_mode) {
while (ptask) { while (ptask) {
// realloc as we go, keeping 'ppt' ahead of 'currmax++' // realloc as we go, keeping 'ppt' ahead of 'currmax++'
ppt = alloc_r(ppt, (curmax + 1) * PTRsz); ppt = alloc_r(ppt, (curmax + 1) * PTRsz);
// here, readproc will allocate the underlying proc_t stg // here, the library will allocate the underlying proc_t stg
if ((ptask = readproc(PT, NULL))) { if ((ptask = read_something(PT, NULL))) {
prochlp(ptask); // tally & complete this proc_t prochlp(ptask); // tally & complete this proc_t
ppt[curmax++] = ptask; 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
}
}
}
closeproc(PT); closeproc(PT);
// iii) Chunkless: make 'eot' entry, after ensuring proc_t exists // iii) Chunkless: make 'eot' entry, after ensuring proc_t exists