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:
parent
3ef4823f90
commit
bb4f08ba29
@ -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;
|
||||
|
317
proc/readproc.c
317
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
|
||||
|
@ -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)
|
||||
|
@ -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 <stdlib.h>
|
||||
#include <stdio.h>
|
||||
@ -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);
|
||||
}
|
||||
|
45
ps/output.c
45
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},
|
||||
|
56
top.c
56
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
|
||||
|
Loading…
Reference in New Issue
Block a user