2 * New Interface to Process Table -- PROCTAB Stream (a la Directory streams)
3 * Copyright (C) 1996 Charles L. Blake.
4 * Copyright (C) 1998 Michael K. Johnson
5 * Copyright 1998-2003 Albert Cahalan
6 * May be distributed under the conditions of the
7 * GNU Library General Public License; a copy is in COPYING
27 #include <sys/types.h>
30 // sometimes it's easier to do this manually, w/o gcc helping
32 extern void __cyg_profile_func_enter(void*,void*);
33 #define ENTER(x) __cyg_profile_func_enter((void*)x,(void*)x)
34 #define LEAVE(x) __cyg_profile_func_exit((void*)x,(void*)x)
40 // convert hex string to unsigned long long
41 static unsigned long long unhex(const char *restrict cp){
42 unsigned long long ull = 0;
45 if(unlikely(c<0x30)) break;
46 ull = (ull<<4) | (c - (c>0x57) ? 0x57 : 0x30) ;
51 static int task_dir_missing;
53 ///////////////////////////////////////////////////////////////////////////
55 typedef struct status_table_struct {
56 unsigned char name[7]; // /proc/*/status field name
57 unsigned char len; // name length
59 long offset; // jump address offset
63 } status_table_struct;
66 #define F(x) {#x, sizeof(#x)-1, (long)(&&case_##x-&&base)},
68 #define F(x) {#x, sizeof(#x)-1, &&case_##x},
70 #define NUL {"", 0, 0},
73 // gperf -7 --language=ANSI-C --key-positions=1,3,4 -C -n -c sml.gperf
76 // Grep this file for "case_", then strip those down to the name.
77 // (leave the colon and newline) So "Pid:\n" and "Threads:\n"
78 // would be lines in the file. (no quote, no escape, etc.)
80 // Watch out for name size in the status_table_struct (grrr, expanding)
81 // and the number of entries (we mask with 63 for now). The table
82 // must be padded out to 64 entries, maybe 128 in the future.
84 static void status2proc(char *S, proc_t *restrict P, int is_proc){
89 static const unsigned char asso[] =
91 61, 61, 61, 61, 61, 61, 61, 61, 61, 61,
92 61, 61, 61, 61, 61, 61, 61, 61, 61, 61,
93 61, 61, 61, 61, 61, 61, 61, 61, 61, 61,
94 61, 61, 61, 61, 61, 61, 61, 61, 61, 61,
95 61, 61, 61, 61, 61, 61, 61, 61, 61, 61,
96 61, 61, 61, 61, 61, 61, 61, 61, 15, 61,
97 61, 61, 61, 61, 61, 61, 30, 3, 5, 5,
98 61, 5, 61, 8, 61, 61, 3, 61, 10, 61,
99 6, 61, 13, 0, 30, 25, 0, 61, 61, 61,
100 61, 61, 61, 61, 61, 61, 61, 3, 61, 13,
101 0, 0, 61, 30, 61, 25, 61, 61, 61, 0,
102 61, 61, 61, 61, 5, 61, 0, 61, 61, 61,
103 0, 61, 61, 61, 61, 61, 61, 61
106 static const status_table_struct table[] = {
139 NUL NUL NUL NUL NUL NUL NUL NUL NUL
160 P->signal[0] = '\0'; // so we can detect it as missing for very old kernels
166 status_table_struct entry;
168 // advance to next line
170 if(unlikely(!S)) break; // if no newline
173 // examine a field name (hash and compare)
175 if(unlikely(!*S)) break;
176 entry = table[63 & (asso[S[3]] + asso[S[2]] + asso[S[0]])];
177 colon = strchr(S, ':');
178 if(unlikely(!colon)) break;
179 if(unlikely(colon[1]!='\t')) break;
180 if(unlikely(colon-S != entry.len)) continue;
181 if(unlikely(memcmp(entry.name,S,colon-S))) continue;
183 S = colon+2; // past the '\t'
186 goto *(&&base + entry.offset);
193 while(u < sizeof P->cmd - 1u){
195 if(unlikely(c=='\n')) break;
196 if(unlikely(c=='\0')) break; // should never happen
197 if(unlikely(c=='\\')){
199 if(c=='\n') break; // should never happen
200 if(!c) break; // should never happen
201 if(c=='n') c='\n'; // else we assume it is '\\'
206 S--; // put back the '\n' or '\0'
211 memcpy(P->signal, S, 16);
212 P->signal[16] = '\0';
215 memcpy(P->blocked, S, 16);
216 P->blocked[16] = '\0';
219 memcpy(P->sigcatch, S, 16);
220 P->sigcatch[16] = '\0';
223 memcpy(P->sigignore, S, 16);
224 P->sigignore[16] = '\0';
227 memcpy(P->_sigpnd, S, 16);
228 P->_sigpnd[16] = '\0';
232 P->signal = unhex(S);
235 P->blocked = unhex(S);
238 P->sigcatch = unhex(S);
241 P->sigignore = unhex(S);
244 P->_sigpnd = unhex(S);
251 Tgid = strtol(S,&S,10);
254 Pid = strtol(S,&S,10);
257 P->ppid = strtol(S,&S,10);
260 Threads = strtol(S,&S,10);
263 P->ruid = strtol(S,&S,10);
264 P->euid = strtol(S,&S,10);
265 P->suid = strtol(S,&S,10);
266 P->fuid = strtol(S,&S,10);
269 P->rgid = strtol(S,&S,10);
270 P->egid = strtol(S,&S,10);
271 P->sgid = strtol(S,&S,10);
272 P->fgid = strtol(S,&S,10);
275 P->vm_data = strtol(S,&S,10);
278 P->vm_exe = strtol(S,&S,10);
281 P->vm_lock = strtol(S,&S,10);
284 P->vm_lib = strtol(S,&S,10);
287 P->vm_rss = strtol(S,&S,10);
290 P->vm_size = strtol(S,&S,10);
293 P->vm_stack = strtol(S,&S,10);
298 // recent kernels supply per-tgid pending signals
299 if(is_proc && *ShdPnd){
300 memcpy(P->signal, ShdPnd, 16);
301 P->signal[16] = '\0';
305 // recent kernels supply per-tgid pending signals
307 if(!is_proc || !P->signal[0]){
308 memcpy(P->signal, P->_sigpnd, 16);
309 P->signal[16] = '\0';
312 if(!is_proc || !have_process_pending){
313 P->signal = P->_sigpnd;
317 // Linux 2.4.13-pre1 to max 2.4.xx have a useless "Tgid"
318 // that is not initialized for built-in kernel tasks.
319 // Only 2.6.0 and above have "Threads" (nlwp) info.
323 P->tgid = Tgid; // the POSIX PID value
324 P->tid = Pid; // the thread ID
334 ///////////////////////////////////////////////////////////////////////
336 // Reads /proc/*/stat files, being careful not to trip over processes with
337 // names like ":-) 1 2 3 4 5 6".
338 static void stat2proc(const char* S, proc_t *restrict P) {
344 /* fill in default values for older kernels */
350 S = strchr(S, '(') + 1;
351 tmp = strrchr(S, ')');
353 if(unlikely(num >= sizeof P->cmd)) num = sizeof P->cmd - 1;
354 memcpy(P->cmd, S, num);
356 S = tmp + 2; // skip ") "
361 "%lu %lu %lu %lu %lu "
362 "%Lu %Lu %Lu %Lu " /* utime stime cutime cstime */
366 "%Lu " /* start_time */
369 "%lu %"KLF"u %"KLF"u %"KLF"u %"KLF"u %"KLF"u "
370 "%*s %*s %*s %*s " /* discard, no RT signals & Linux 2.1 used hex */
375 &P->ppid, &P->pgrp, &P->session, &P->tty, &P->tpgid,
376 &P->flags, &P->min_flt, &P->cmin_flt, &P->maj_flt, &P->cmaj_flt,
377 &P->utime, &P->stime, &P->cutime, &P->cstime,
378 &P->priority, &P->nice,
384 &P->rss_rlim, &P->start_code, &P->end_code, &P->start_stack, &P->kstk_esp, &P->kstk_eip,
385 /* P->signal, P->blocked, P->sigignore, P->sigcatch, */ /* can't use */
386 &P->wchan, /* &P->nswap, &P->cnswap, */ /* nswap and cnswap dead for 2.4.xx and up */
387 /* -- Linux 2.0.35 ends here -- */
388 &P->exit_signal, &P->processor, /* 2.2.1 ends with "exit_signal" */
389 /* -- Linux 2.2.8 to 2.5.17 end here -- */
390 &P->rtprio, &P->sched /* both added to 2.5.18 */
400 /////////////////////////////////////////////////////////////////////////
402 static void statm2proc(const char* s, proc_t *restrict P) {
404 num = sscanf(s, "%ld %ld %ld %ld %ld %ld %ld",
405 &P->size, &P->resident, &P->share,
406 &P->trs, &P->lrs, &P->drs, &P->dt);
407 /* fprintf(stderr, "statm2proc converted %d fields.\n",num); */
410 static int file2str(const char *directory, const char *what, char *ret, int cap) {
411 static char filename[80];
414 sprintf(filename, "%s/%s", directory, what);
415 fd = open(filename, O_RDONLY, 0);
416 if(unlikely(fd==-1)) return -1;
417 num_read = read(fd, ret, cap - 1);
419 if(unlikely(num_read<=0)) return -1;
420 ret[num_read] = '\0';
424 static char** file2strvec(const char* directory, const char* what) {
425 char buf[2048]; /* read buf bytes at a time */
426 char *p, *rbuf = 0, *endbuf, **q, **ret;
427 int fd, tot = 0, n, c, end_of_file = 0;
430 sprintf(buf, "%s/%s", directory, what);
431 fd = open(buf, O_RDONLY, 0);
432 if(fd==-1) return NULL;
434 /* read whole file into a memory buffer, allocating as we go */
435 while ((n = read(fd, buf, sizeof buf - 1)) > 0) {
436 if (n < (int)(sizeof buf - 1))
438 if (n == 0 && rbuf == 0)
439 return NULL; /* process died between our open and read */
443 return NULL; /* read error */
445 if (end_of_file && buf[n-1]) /* last read char not null */
446 buf[n++] = '\0'; /* so append null-terminator */
447 rbuf = xrealloc(rbuf, tot + n); /* allocate more memory */
448 memcpy(rbuf + tot, buf, n); /* copy buffer into it */
449 tot += n; /* increment total byte ctr */
454 if (n <= 0 && !end_of_file) {
455 if (rbuf) free(rbuf);
456 return NULL; /* read error */
458 endbuf = rbuf + tot; /* count space for pointers */
459 align = (sizeof(char*)-1) - ((tot + sizeof(char*)-1) & (sizeof(char*)-1));
460 for (c = 0, p = rbuf; p < endbuf; p++)
463 c += sizeof(char*); /* one extra for NULL term */
465 rbuf = xrealloc(rbuf, tot + c + align); /* make room for ptrs AT END */
466 endbuf = rbuf + tot; /* addr just past data buf */
467 q = ret = (char**) (endbuf+align); /* ==> free(*ret) to dealloc */
468 *q++ = p = rbuf; /* point ptrs to the strings */
469 endbuf--; /* do not traverse final NUL */
471 if (!*p) /* NUL char implies that */
472 *q++ = p+1; /* next string -> next char */
474 *q = 0; /* null ptr list terminator */
478 // warning: interface may change
479 int read_cmdline(char *restrict const dst, unsigned sz, unsigned pid){
484 snprintf(name, sizeof name, "/proc/%u/cmdline", pid);
485 fd = open(name, O_RDONLY);
488 ssize_t r = read(fd,dst+n,sz-n);
490 if(errno==EINTR) continue;
494 if(n==sz) break; // filled the buffer
495 if(r==0) break; // EOF
505 if(c<' ' || c>'~') dst[i]=' ';
511 /* These are some nice GNU C expression subscope "inline" functions.
512 * The can be used with arbitrary types and evaluate their arguments
516 /* Test if item X of type T is present in the 0 terminated list L */
517 # define XinL(T, X, L) ( { \
518 T x = (X), *l = (L); \
519 while (*l && *l != x) l++; \
523 /* Test if item X of type T is present in the list L of length N */
524 # define XinLN(T, X, L, N) ( { \
525 T x = (X), *l = (L); \
526 int i = 0, n = (N); \
527 while (i < n && l[i] != x) i++; \
528 i < n && l[i] == x; \
531 //////////////////////////////////////////////////////////////////////////////////
532 // This reads process info from /proc in the traditional way, for one process.
533 // The pid (tgid? tid?) is already in p, and a path to it in path, with some
535 static proc_t* simple_readproc(PROCTAB *restrict const PT, proc_t *restrict const p) {
536 static struct stat sb; // stat() buffer
537 static char sbuf[1024]; // buffer for stat,statm
538 char *restrict const path = PT->path;
539 unsigned flags = PT->flags;
541 if (unlikely(stat(path, &sb) == -1)) /* no such dirent (anymore) */
544 if ((flags & PROC_UID) && !XinLN(uid_t, sb.st_uid, PT->uids, PT->nuid))
545 goto next_proc; /* not one of the requested uids */
547 p->euid = sb.st_uid; /* need a way to get real uid */
548 p->egid = sb.st_gid; /* need a way to get real gid */
550 if (flags & PROC_FILLSTAT) { /* read, parse /proc/#/stat */
551 if (unlikely( file2str(path, "stat", sbuf, sizeof sbuf) == -1 ))
552 goto next_proc; /* error reading /proc/#/stat */
553 stat2proc(sbuf, p); /* parse /proc/#/stat */
556 if (unlikely(flags & PROC_FILLMEM)) { /* read, parse /proc/#/statm */
557 if (likely( file2str(path, "statm", sbuf, sizeof sbuf) != -1 ))
558 statm2proc(sbuf, p); /* ignore statm errors here */
559 } /* statm fields just zero */
561 if (flags & PROC_FILLSTATUS) { /* read, parse /proc/#/status */
562 if (likely( file2str(path, "status", sbuf, sizeof sbuf) != -1 )){
563 status2proc(sbuf, p, 1);
567 // if multithreaded, some values are crap
569 p->wchan = (KLONG)~0ull;
572 /* some number->text resolving which is time consuming and kind of insane */
573 if (flags & PROC_FILLUSR){
574 memcpy(p->euser, user_from_uid(p->euid), sizeof p->euser);
575 if(flags & PROC_FILLSTATUS) {
576 memcpy(p->ruser, user_from_uid(p->ruid), sizeof p->ruser);
577 memcpy(p->suser, user_from_uid(p->suid), sizeof p->suser);
578 memcpy(p->fuser, user_from_uid(p->fuid), sizeof p->fuser);
582 /* some number->text resolving which is time consuming and kind of insane */
583 if (flags & PROC_FILLGRP){
584 memcpy(p->egroup, group_from_gid(p->egid), sizeof p->egroup);
585 if(flags & PROC_FILLSTATUS) {
586 memcpy(p->rgroup, group_from_gid(p->rgid), sizeof p->rgroup);
587 memcpy(p->sgroup, group_from_gid(p->sgid), sizeof p->sgroup);
588 memcpy(p->fgroup, group_from_gid(p->fgid), sizeof p->fgroup);
592 if ((flags & PROC_FILLCOM) || (flags & PROC_FILLARG)) /* read+parse /proc/#/cmdline */
593 p->cmdline = file2strvec(path, "cmdline");
597 if (unlikely(flags & PROC_FILLENV)) /* read+parse /proc/#/environ */
598 p->environ = file2strvec(path, "environ");
607 //////////////////////////////////////////////////////////////////////////////////
608 // This reads /proc/*/task/* data, for one task.
609 // p is the POSIX process (task group summary) (not needed by THIS implementation)
610 // t is the POSIX thread (task group member, generally not the leader)
611 // path is a path to the task, with some room to spare.
612 static proc_t* simple_readtask(PROCTAB *restrict const PT, const proc_t *restrict const p, proc_t *restrict const t, char *restrict const path) {
613 static struct stat sb; // stat() buffer
614 static char sbuf[1024]; // buffer for stat,statm
615 unsigned flags = PT->flags;
618 if (unlikely(stat(path, &sb) == -1)) /* no such dirent (anymore) */
621 // if ((flags & PROC_UID) && !XinLN(uid_t, sb.st_uid, PT->uids, PT->nuid))
622 // goto next_task; /* not one of the requested uids */
624 t->euid = sb.st_uid; /* need a way to get real uid */
625 t->egid = sb.st_gid; /* need a way to get real gid */
628 if (flags & PROC_FILLSTAT) { /* read, parse /proc/#/stat */
629 if (unlikely( file2str(path, "stat", sbuf, sizeof sbuf) == -1 ))
630 goto next_task; /* error reading /proc/#/stat */
631 stat2proc(sbuf, t); /* parse /proc/#/stat */
634 if (unlikely(flags & PROC_FILLMEM)) { /* read, parse /proc/#/statm */
636 if (likely( file2str(path, "statm", sbuf, sizeof sbuf) != -1 ))
637 statm2proc(sbuf, t); /* ignore statm errors here */
640 t->resident = p->resident;
647 } /* statm fields just zero */
649 if (flags & PROC_FILLSTATUS) { /* read, parse /proc/#/status */
650 if (likely( file2str(path, "status", sbuf, sizeof sbuf) != -1 )){
651 status2proc(sbuf, t, 0);
655 /* some number->text resolving which is time consuming */
656 if (flags & PROC_FILLUSR){
657 memcpy(t->euser, user_from_uid(t->euid), sizeof t->euser);
658 if(flags & PROC_FILLSTATUS) {
659 memcpy(t->ruser, user_from_uid(t->ruid), sizeof t->ruser);
660 memcpy(t->suser, user_from_uid(t->suid), sizeof t->suser);
661 memcpy(t->fuser, user_from_uid(t->fuid), sizeof t->fuser);
665 /* some number->text resolving which is time consuming */
666 if (flags & PROC_FILLGRP){
667 memcpy(t->egroup, group_from_gid(t->egid), sizeof t->egroup);
668 if(flags & PROC_FILLSTATUS) {
669 memcpy(t->rgroup, group_from_gid(t->rgid), sizeof t->rgroup);
670 memcpy(t->sgroup, group_from_gid(t->sgid), sizeof t->sgroup);
671 memcpy(t->fgroup, group_from_gid(t->fgid), sizeof t->fgroup);
676 if ((flags & PROC_FILLCOM) || (flags & PROC_FILLARG)) /* read+parse /proc/#/cmdline */
677 t->cmdline = file2strvec(path, "cmdline");
681 if (unlikely(flags & PROC_FILLENV)) /* read+parse /proc/#/environ */
682 t->environ = file2strvec(path, "environ");
686 t->cmdline = p->cmdline; // better not free these until done with all threads!
687 t->environ = p->environ;
690 t->ppid = p->ppid; // ought to put the per-task ppid somewhere
697 //////////////////////////////////////////////////////////////////////////////////
698 // This finds processes in /proc in the traditional way.
699 // Return non-zero on success.
700 static int simple_nextpid(PROCTAB *restrict const PT, proc_t *restrict const p) {
701 static struct direct *ent; /* dirent handle */
702 char *restrict const path = PT->path;
704 ent = readdir(PT->procfs);
705 if(unlikely(unlikely(!ent) || unlikely(!ent->d_name))) return 0;
706 if(likely( likely(*ent->d_name > '0') && likely(*ent->d_name <= '9') )) break;
708 p->tgid = strtoul(ent->d_name, NULL, 10);
710 memcpy(path, "/proc/", 6);
711 strcpy(path+6, ent->d_name); // trust /proc to not contain evil top-level entries
715 //////////////////////////////////////////////////////////////////////////////////
716 // This finds tasks in /proc/*/task/ in the traditional way.
717 // Return non-zero on success.
718 static int simple_nexttid(PROCTAB *restrict const PT, const proc_t *restrict const p, proc_t *restrict const t, char *restrict const path) {
719 static struct direct *ent; /* dirent handle */
720 if(PT->taskdir_user != p->tgid){
722 closedir(PT->taskdir);
724 // use "path" as some tmp space
725 snprintf(path, PROCPATHLEN, "/proc/%d/task", p->tgid);
726 PT->taskdir = opendir(path);
727 if(!PT->taskdir) return 0;
728 PT->taskdir_user = p->tgid;
731 ent = readdir(PT->taskdir);
732 if(unlikely(unlikely(!ent) || unlikely(!ent->d_name))) return 0;
733 if(likely( likely(*ent->d_name > '0') && likely(*ent->d_name <= '9') )) break;
735 t->tid = strtoul(ent->d_name, NULL, 10);
737 t->ppid = p->ppid; // cover for kernel behavior? we want both actually...?
738 snprintf(path, PROCPATHLEN, "/proc/%d/task/%s", p->tgid, ent->d_name);
742 //////////////////////////////////////////////////////////////////////////////////
743 // This "finds" processes in a list that was given to openproc().
744 // Return non-zero on success. (tgid was handy)
745 static int listed_nextpid(PROCTAB *restrict const PT, proc_t *restrict const p) {
746 char *restrict const path = PT->path;
747 pid_t tgid = *(PT->pids)++;
749 snprintf(path, PROCPATHLEN, "/proc/%d", tgid);
751 p->tid = tgid; // they match for leaders
756 //////////////////////////////////////////////////////////////////////////////////
757 /* readproc: return a pointer to a proc_t filled with requested info about the
758 * next process available matching the restriction set. If no more such
759 * processes are available, return a null pointer (boolean false). Use the
760 * passed buffer instead of allocating space if it is non-NULL. */
762 /* This is optimized so that if a PID list is given, only those files are
763 * searched for in /proc. If other lists are given in addition to the PID list,
764 * the same logic can follow through as for the no-PID list case. This is
765 * fairly complex, but it does try to not to do any unnecessary work.
767 proc_t* readproc(PROCTAB *restrict const PT, proc_t *restrict p) {
772 // if (PT->taskdir) {
773 // closedir(PT->taskdir);
774 // PT->taskdir = NULL;
775 // PT->taskdir_user = -1;
779 if(!p) p = xcalloc(p, sizeof *p); /* passed buf or alloced mem */
782 // fills in the path, plus p->tid and p->tgid
783 if (unlikely(! PT->finder(PT,p) )) goto out;
785 // go read the process data
786 ret = PT->reader(PT,p);
791 if(!saved_p) free(p);
792 // FIXME: maybe set tid to -1 here, for "-" in display?
796 //////////////////////////////////////////////////////////////////////////////////
797 // readtask: return a pointer to a proc_t filled with requested info about the
798 // next task available. If no more such tasks are available, return a null
799 // pointer (boolean false). Use the passed buffer instead of allocating
800 // space if it is non-NULL.
801 proc_t* readtask(PROCTAB *restrict const PT, const proc_t *restrict const p, proc_t *restrict t) {
802 static char path[PROCPATHLEN]; // must hold /proc/2000222000/task/2000222000/cmdline
807 if(!t) t = xcalloc(t, sizeof *t); /* passed buf or alloced mem */
809 // 1. got to fake a thread for old kernels
810 // 2. for single-threaded processes, this is faster (but must patch up stuff that differs!)
811 if(task_dir_missing || p->nlwp < 2){
812 if(PT->did_fake) goto out;
814 memcpy(t,p,sizeof(proc_t));
815 // use the per-task pending, not per-tgid pending
817 memcpy(&t->signal, &t->_sigpnd, sizeof t->signal);
819 t->signal = t->_sigpnd;
825 // fills in the path, plus t->tid and t->tgid
826 if (unlikely(! PT->taskfinder(PT,p,t,path) )) goto out; // simple_nexttid
828 // go read the task data
829 ret = PT->taskreader(PT,p,t,path); // simple_readtask
834 if(!saved_t) free(t);
838 //////////////////////////////////////////////////////////////////////////////////
840 // initiate a process table scan
841 PROCTAB* openproc(int flags, ...) {
845 PROCTAB* PT = xmalloc(sizeof(PROCTAB));
848 task_dir_missing = stat("/proc/self/task", &sbuf);
852 PT->taskdir_user = -1;
853 PT->taskfinder = simple_nexttid;
854 PT->taskreader = simple_readtask;
856 PT->reader = simple_readproc;
857 if (flags & PROC_PID){
859 PT->finder = listed_nextpid;
861 PT->procfs = opendir("/proc");
862 if(!PT->procfs) return NULL;
863 PT->finder = simple_nextpid;
867 va_start(ap, flags); /* Init args list */
868 if (flags & PROC_PID)
869 PT->pids = va_arg(ap, pid_t*);
870 else if (flags & PROC_UID) {
871 PT->uids = va_arg(ap, uid_t*);
872 PT->nuid = va_arg(ap, int);
874 va_end(ap); /* Clean up args list */
879 // terminate a process table scan
880 void closeproc(PROCTAB* PT) {
882 if (PT->procfs) closedir(PT->procfs);
883 if (PT->taskdir) closedir(PT->taskdir);
884 memset(PT,'#',sizeof(PROCTAB));
889 // deallocate the space allocated by readproc if the passed rbuf was NULL
890 void freeproc(proc_t* p) {
891 if (!p) /* in case p is NULL */
893 /* ptrs are after strings to avoid copying memory when building them. */
894 /* so free is called on the address of the address of strvec[0]. */
896 free((void*)*p->cmdline);
898 free((void*)*p->environ);
903 //////////////////////////////////////////////////////////////////////////////////
904 void look_up_our_self(proc_t *p) {
907 if(file2str("/proc/self", "stat", sbuf, sizeof sbuf) == -1){
908 fprintf(stderr, "Error, do this: mount -t proc none /proc\n");
911 stat2proc(sbuf, p); // parse /proc/self/stat
914 HIDDEN_ALIAS(readproc);
915 HIDDEN_ALIAS(readtask);
917 /* Convenient wrapper around openproc and readproc to slurp in the whole process
918 * table subset satisfying the constraints of flags and the optional PID list.
919 * Free allocated memory with exit(). Access via tab[N]->member. The pointer
920 * list is NULL terminated.
922 proc_t** readproctab(int flags, ...) {
928 va_start(ap, flags); /* pass through args to openproc */
929 if (flags & PROC_UID) {
930 /* temporary variables to ensure that va_arg() instances
931 * are called in the right order
936 u = va_arg(ap, uid_t*);
938 PT = openproc(flags, u, i);
940 else if (flags & PROC_PID)
941 PT = openproc(flags, va_arg(ap, void*)); /* assume ptr sizes same */
943 PT = openproc(flags);
945 do { /* read table: */
946 tab = xrealloc(tab, (n+1)*sizeof(proc_t*));/* realloc as we go, using */
947 tab[n] = readproc_direct(PT, NULL); /* final null to terminate */
948 } while (tab[n++]); /* stop when NULL reached */
953 // Try again, this time with threads and selection.
954 proc_data_t *readproctab2(int(*want_proc)(proc_t *buf), int(*want_task)(proc_t *buf), PROCTAB *restrict const PT) {
955 proc_t** ptab = NULL;
956 unsigned n_proc_alloc = 0;
959 proc_t** ttab = NULL;
960 unsigned n_task_alloc = 0;
964 unsigned n_alloc = 0;
965 unsigned long n_used = 0;
971 if(n_alloc == n_used){
972 //proc_t *old = data;
973 n_alloc = n_alloc*5/4+30; // grow by over 25%
974 data = realloc(data,sizeof(proc_t)*n_alloc);
975 //if(!data) return NULL;
977 if(n_proc_alloc == n_proc){
978 //proc_t **old = ptab;
979 n_proc_alloc = n_proc_alloc*5/4+30; // grow by over 25%
980 ptab = realloc(ptab,sizeof(proc_t*)*n_proc_alloc);
981 //if(!ptab) return NULL;
983 tmp = readproc_direct(PT, data+n_used);
985 if(!want_proc(tmp)) continue;
986 ptab[n_proc++] = (proc_t*)(n_used++);
987 if(!( PT->flags & PROC_LOOSE_TASKS )) continue;
990 if(n_alloc == n_used){
992 n_alloc = n_alloc*5/4+30; // grow by over 25%
993 data = realloc(data,sizeof(proc_t)*n_alloc);
994 // have to move tmp too
995 tmp = data+(tmp-old);
996 //if(!data) return NULL;
998 if(n_task_alloc == n_task){
999 //proc_t **old = ttab;
1000 n_task_alloc = n_task_alloc*5/4+1; // grow by over 25%
1001 ttab = realloc(ttab,sizeof(proc_t*)*n_task_alloc);
1002 //if(!ttab) return NULL;
1004 t = readtask_direct(PT, tmp, data+n_used);
1006 if(!want_task(t)) continue;
1007 ttab[n_task++] = (proc_t*)(n_used++);
1011 pd = malloc(sizeof(proc_data_t));
1016 if(PT->flags & PROC_LOOSE_TASKS){
1023 // change array indexes to pointers
1024 while(n_proc--) ptab[n_proc] = data+(long)(ptab[n_proc]);
1025 while(n_task--) ttab[n_task] = data+(long)(ttab[n_task]);
1031 * get_proc_stats - lookup a single tasks information and fill out a proc_t
1033 * On failure, returns NULL. On success, returns 'p' and 'p' is a valid
1034 * and filled out proc_t structure.
1036 proc_t * get_proc_stats(pid_t pid, proc_t *p) {
1037 static char path[PATH_MAX], sbuf[1024];
1038 struct stat statbuf;
1040 sprintf(path, "/proc/%d", pid);
1041 if (stat(path, &statbuf)) {
1046 if (file2str(path, "stat", sbuf, sizeof sbuf) >= 0)
1047 stat2proc(sbuf, p); /* parse /proc/#/stat */
1048 if (file2str(path, "statm", sbuf, sizeof sbuf) >= 0)
1049 statm2proc(sbuf, p); /* ignore statm errors here */
1050 if (file2str(path, "status", sbuf, sizeof sbuf) >= 0)
1051 status2proc(sbuf, p, 0 /*FIXME*/);