Imported Upstream version 0.12 upstream upstream/0.12
authorSven Hoexter <sven@timegate.de>
Sun, 7 Aug 2011 09:18:08 +0000 (11:18 +0200)
committerSven Hoexter <sven@timegate.de>
Sun, 7 Aug 2011 09:18:08 +0000 (11:18 +0200)
Makefile [new file with mode: 0644]
README [new file with mode: 0644]
defines.h [new file with mode: 0644]
psinfo.c [new file with mode: 0644]
psinfo.h [new file with mode: 0644]

diff --git a/Makefile b/Makefile
new file mode 100644 (file)
index 0000000..2964607
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,18 @@
+CC=gcc
+CFLAGS=-Wall -Werror -Wstrict-prototypes -Wmissing-prototypes -Wpointer-arith -Wreturn-type -Wcast-qual -Wswitch -Wshadow -Wcast-align -Wno-unused-parameter -Wchar-subscripts -Winline -Wnested-externs -Wredundant-decls
+INSTALL=install
+LDFLAGS=
+SOURCES=psinfo.c
+EXECUTABLE=psinfo
+TARGETDIR=/usr/local/bin
+
+all: $(EXECUTABLE)
+
+clean:
+       rm -f $(EXECUTABLE)     
+
+install: $(EXECUTABLE)
+       $(INSTALL) -m 755 psinfo $(TARGETDIR)
+
+$(EXECUTABLE): $(SOURCES)
+       $(CC) $(CFLAGS) -o $@ $(SOURCES) $(LDFLAGS)
diff --git a/README b/README
new file mode 100644 (file)
index 0000000..15e7f25
--- /dev/null
+++ b/README
@@ -0,0 +1,26 @@
+INTRODUCTION
+
+psinfo shows process information and statistics using the kernel /proc interface. This information includes:
+-Process state, environment, arguments and flags
+-CPU usage
+-Scheduling
+-I/O usage
+-Virtual memory status
+-Pagefaults
+-Capabilities
+-Signals
+psinfo is useful for providing a detailed view of the current state of an application when diagnosing issues or performance problems.
+
+INSTALLATION
+
+psinfo requires Linux kernel 2.6.0 or greater. Some features will not work in earlier versions and are marked by n/a in output.
+compile: make
+install: make install
+
+psinfo can be run as an orindary user but certain features (environment, executable, current working directory and root directory) require root rights and are marked n/a in output otherwise.
+
+CONTACT
+
+Bug reports and patches can be sent by e-mail to Ward van Wanrooij <ward@ward.nu>
+
+psinfo - v0.12 - 20100704
diff --git a/defines.h b/defines.h
new file mode 100644 (file)
index 0000000..db9f705
--- /dev/null
+++ b/defines.h
@@ -0,0 +1,312 @@
+/***********************************************************************
+*
+* defines.h
+*
+* psinfo: process info
+*
+* Copyright (C) 2008 Ward van Wanrooij <ward@ward.nu>
+*
+* This software may be distributed according to the terms of the GNU
+* General Public License, version 2 or (at your option) any later
+* version.
+*
+***********************************************************************/
+
+//policies from <bits/sched.h>
+#ifndef SCHED_OTHER
+#define SCHED_OTHER 0
+#endif
+#ifndef SCHED_FIFO
+#define SCHED_FIFO 1
+#endif
+#ifndef SCHED_RR
+#define SCHED_RR 2
+#endif
+#ifndef SCHED_BATCH
+#define SCHED_BATCH 3
+#endif
+
+//signals from <bits/signum.h>
+#ifndef SIGHUP
+#define SIGHUP 1
+#endif
+#ifndef SIGINT
+#define SIGINT 2
+#endif
+#ifndef SIGQUIT
+#define SIGQUIT 3
+#endif
+#ifndef SIGILL
+#define SIGILL 4
+#endif
+#ifndef SIGTRAP
+#define SIGTRAP 5
+#endif
+#ifndef SIGABRT
+#define SIGABRT 6
+#endif
+#ifndef SIGIOT
+#define SIGIOT 6
+#endif
+#ifndef SIGBUS
+#define SIGBUS 7
+#endif
+#ifndef SIGFPE
+#define SIGFPE 8
+#endif
+#ifndef SIGKILL
+#define SIGKILL 9
+#endif
+#ifndef SIGUSR1
+#define SIGUSR1 10
+#endif
+#ifndef SIGSEGV
+#define SIGSEGV 11
+#endif
+#ifndef SIGUSR2
+#define SIGUSR2 12
+#endif
+#ifndef SIGPIPE
+#define SIGPIPE 13
+#endif
+#ifndef SIGALRM
+#define SIGALRM 14
+#endif
+#ifndef SIGTERM
+#define SIGTERM 15
+#endif
+#ifndef SIGSTKFLT
+#define SIGSTKFLT 16
+#endif
+#ifndef SIGCLD
+#define SIGCLD SIGCHLD
+#endif
+#ifndef SIGCHLD
+#define SIGCHLD 17
+#endif
+#ifndef SIGCONT
+#define SIGCONT 18
+#endif
+#ifndef SIGSTOP
+#define SIGSTOP 19
+#endif
+#ifndef SIGTSTP
+#define SIGTSTP 20
+#endif
+#ifndef SIGTTIN
+#define SIGTTIN 21
+#endif
+#ifndef SIGTTOU
+#define SIGTTOU 22
+#endif
+#ifndef SIGURG
+#define SIGURG 23
+#endif
+#ifndef SIGXCPU
+#define SIGXCPU 24
+#endif
+#ifndef SIGXFSZ
+#define SIGXFSZ 25
+#endif
+#ifndef SIGVTALRM
+#define SIGVTALRM 26
+#endif
+#ifndef SIGPROF
+#define SIGPROF 27
+#endif
+#ifndef SIGWINCH
+#define SIGWINCH 28
+#endif
+#ifndef SIGPOLL
+#define SIGPOLL SIGIO
+#endif
+#ifndef SIGIO
+#define SIGIO 29
+#endif
+#ifndef SIGPWR
+#define SIGPWR 30
+#endif
+#ifndef SIGSYS
+#define SIGSYS 31
+#endif
+#ifndef SIGUNUSED
+#define SIGUNUSED 31
+#endif
+
+//signals from <linux/capability.h>
+#ifndef CAP_CHOWN
+#define CAP_CHOWN 0
+#endif
+#ifndef CAP_DAC_OVERRIDE
+#define CAP_DAC_OVERRIDE 1
+#endif
+#ifndef CAP_DAC_READ_SEARCH
+#define CAP_DAC_READ_SEARCH 2
+#endif
+#ifndef CAP_FOWNER
+#define CAP_FOWNER 3
+#endif
+#ifndef CAP_FSETID
+#define CAP_FSETID 4
+#endif
+#ifndef CAP_FS_MASK
+#define CAP_FS_MASK 0x1f
+#endif
+#ifndef CAP_KILL
+#define CAP_KILL 5
+#endif
+#ifndef CAP_SETGID
+#define CAP_SETGID 6
+#endif
+#ifndef CAP_SETUID
+#define CAP_SETUID 7
+#endif
+#ifndef CAP_SETPCAP
+#define CAP_SETPCAP 8
+#endif
+#ifndef CAP_LINUX_IMMUTABLE
+#define CAP_LINUX_IMMUTABLE 9
+#endif
+#ifndef CAP_NET_BIND_SERVICE
+#define CAP_NET_BIND_SERVICE 10
+#endif
+#ifndef CAP_NET_BROADCAST
+#define CAP_NET_BROADCAST 11
+#endif
+#ifndef CAP_NET_ADMIN
+#define CAP_NET_ADMIN 12
+#endif
+#ifndef CAP_NET_RAW
+#define CAP_NET_RAW 13
+#endif
+#ifndef CAP_IPC_LOCK
+#define CAP_IPC_LOCK 14
+#endif
+#ifndef CAP_IPC_OWNER
+#define CAP_IPC_OWNER 15
+#endif
+#ifndef CAP_SYS_MODULE
+#define CAP_SYS_MODULE 16
+#endif
+#ifndef CAP_SYS_RAWIO
+#define CAP_SYS_RAWIO 17
+#endif
+#ifndef CAP_SYS_CHROOT
+#define CAP_SYS_CHROOT 18
+#endif
+#ifndef CAP_SYS_PTRACE
+#define CAP_SYS_PTRACE 19
+#endif
+#ifndef CAP_SYS_PACCT
+#define CAP_SYS_PACCT 20
+#endif
+#ifndef CAP_SYS_ADMIN
+#define CAP_SYS_ADMIN 21
+#endif
+#ifndef CAP_SYS_BOOT
+#define CAP_SYS_BOOT 22
+#endif
+#ifndef CAP_SYS_NICE
+#define CAP_SYS_NICE 23
+#endif
+#ifndef CAP_SYS_RESOURCE
+#define CAP_SYS_RESOURCE 24
+#endif
+#ifndef CAP_SYS_TIME
+#define CAP_SYS_TIME 25
+#endif
+#ifndef CAP_SYS_TTY_CONFIG
+#define CAP_SYS_TTY_CONFIG 26
+#endif
+#ifndef CAP_MKNOD
+#define CAP_MKNOD 27
+#endif
+#ifndef CAP_LEASE
+#define CAP_LEASE 28
+#endif
+#ifndef CAP_AUDIT_WRITE
+#define CAP_AUDIT_WRITE 29
+#endif
+#ifndef CAP_AUDIT_CONTROL
+#define CAP_AUDIT_CONTROL 30
+#endif
+
+//process flags from <linux/sched.h>
+#ifndef PF_ALIGNWARN
+#define PF_ALIGNWARN 0x00000001
+#endif
+#ifndef PF_STARTING
+#define PF_STARTING 0x00000002
+#endif
+#ifndef PF_EXITING
+#define PF_EXITING 0x00000004
+#endif
+#ifndef PF_EXITPIDONE
+#define PF_EXITPIDONE 0x00000008
+#endif
+#ifndef PF_VCPU
+#define PF_VCPU 0x00000010
+#endif
+#ifndef PF_FORKNOEXEC
+#define PF_FORKNOEXEC 0x00000040
+#endif
+#ifndef PF_SUPERPRIV
+#define PF_SUPERPRIV 0x00000100
+#endif
+#ifndef PF_DUMPCORE
+#define PF_DUMPCORE 0x00000200
+#endif
+#ifndef PF_SIGNALED
+#define PF_SIGNALED 0x00000400
+#endif
+#ifndef PF_MEMALLOC
+#define PF_MEMALLOC 0x00000800
+#endif
+#ifndef PF_FLUSHER
+#define PF_FLUSHER 0x00001000
+#endif
+#ifndef PF_USED_MATH
+#define PF_USED_MATH 0x00002000
+#endif
+#ifndef PF_NOFREEZE
+#define PF_NOFREEZE 0x00008000
+#endif
+#ifndef PF_FROZEN
+#define PF_FROZEN 0x00010000
+#endif
+#ifndef PF_FSTRANS
+#define PF_FSTRANS 0x00020000
+#endif
+#ifndef PF_KSWAPD
+#define PF_KSWAPD 0x00040000
+#endif
+#ifndef PF_SWAPOFF
+#define PF_SWAPOFF 0x00080000
+#endif
+#ifndef PF_LESS_THROTTLE
+#define PF_LESS_THROTTLE 0x0010000
+#endif
+#ifndef PF_BORROWED_MM
+#define PF_BORROWED_MM 0x00200000
+#endif
+#ifndef PF_RANDOMIZE
+#define PF_RANDOMIZE 0x00400000
+#endif
+#ifndef PF_SWAPWRITE
+#define PF_SWAPWRITE 0x00800000
+#endif
+#ifndef PF_SPREAD_PAGE
+#define PF_SPREAD_PAGE 0x01000000
+#endif
+#ifndef PF_SPREAD_SLAB
+#define PF_SPREAD_SLAB 0x02000000
+#endif
+#ifndef PF_MEMPOLICY
+#define PF_MEMPOLICY 0x10000000
+#endif
+#ifndef PF_MUTEX_TESTER
+#define PF_MUTEX_TESTER 0x20000000
+#endif
+#ifndef PF_FREEZER_SKIP
+#define PF_FREEZER_SKIP 0x40000000
+#endif
diff --git a/psinfo.c b/psinfo.c
new file mode 100644 (file)
index 0000000..90a6d8f
--- /dev/null
+++ b/psinfo.c
@@ -0,0 +1,757 @@
+/***********************************************************************
+*
+* psinfo.c
+*
+* psinfo: process info
+*
+* Copyright (C) 2008-2010 Ward van Wanrooij <ward@ward.nu>
+*
+* This software may be distributed according to the terms of the GNU
+* General Public License, version 2 or (at your option) any later
+* version.
+*
+***********************************************************************/
+
+#include <stdio.h>
+#include <sys/utsname.h>
+#include <sys/sysinfo.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <time.h>
+#include <getopt.h>
+#include <errno.h>
+#include <dirent.h>
+#include <limits.h>
+
+#include "defines.h"
+#include "psinfo.h"
+
+#define CHECK_ERROR(x) { if (x) fprintf(stderr, "psinfo:%s:%d: error at %s\n", __FILE__, __LINE__, #x); }
+
+int parse_proc_io(FILE * f, struct process_info *p)
+{
+       char key[1024], value[1024];
+
+       while (fscanf(f, "%1023[^:]:%1023[^\n]\n", key, value) == 2) {
+               if (!strcasecmp(key, "rchar")) {
+                       p->has_io = 1;
+                       p->rchar = atoll(value);
+               } else if (!strcasecmp(key, "wchar")) {
+                       p->wchar = atoll(value);
+               } else if (!strcasecmp(key, "syscr")) {
+                       p->syscr = atoll(value);
+               } else if (!strcasecmp(key, "syscw")) {
+                       p->syscw = atoll(value);
+               } else if (!strcasecmp(key, "read_bytes")) {
+                       p->read_bytes = atoll(value);
+               } else if (!strcasecmp(key, "write_bytes")) {
+                       p->write_bytes = atoll(value);
+               } else if (!strcasecmp(key, "cancelled_write_bytes")) {
+                       p->cancelled_write_bytes = atoll(value);
+               }
+       }
+       return 0;
+}
+
+int parse_proc_oomscore(FILE * f, struct process_info *p)
+{
+       p->has_oom_score = (fscanf(f, "%d", &p->oom_score) == 1);
+       return !p->has_oom_score;
+}
+
+int parse_proc_oomadj(FILE * f, struct process_info *p)
+{
+       p->has_oom_adj = (fscanf(f, "%d", &p->oom_adj) == 1);
+       return !p->has_oom_adj;
+}
+
+
+int parse_proc_schedstat(FILE * f, struct process_info *p)
+{
+       p->has_schedstat = (fscanf(f, "%llu %llu %llu", &p->run_ticks, &p->wait_ticks, &p->nran) == 3);
+       return !p->has_schedstat;
+}
+
+int parse_proc_stat(FILE * f, struct process_info *p)
+{
+       int m;
+
+       if ((m = fscanf(f, "%d (%1023[^)]) %c %d %d %d %d %d %u %lu %lu %lu %lu %lu %lu %ld %ld %ld %ld %d %*d %llu %lu %ld %lu %lu %lu %lu %lu %lu %*u %*u %*u %*u %lu %*u %*u %d %d %u %u %llu %lu %ld", &p->pid, p->tcomm, &p->state, &p->ppid, &p->pgrp, &p->sid, &p->tty_nr, &p->tty_pgrp, &p->flags, &p->min_flt, &p->cmin_flt, &p->maj_flt, &p->cmaj_flt, &p->utime, &p->stime, &p->cutime, &p->cstime, &p->priority, &p->nice, &p->num_threads, &p->start_time, &p->vsize, &p->rss, &p->rlim, &p->start_code, &p->end_code, &p->start_stack, &p->esp, &p->eip, &p->wchan, &p->exit_signal, &p->task_cpu, &p->rt_priority, &p->policy, &p->blkio_ticks, &p->gtime, &p->cgtime)) >= 32) {
+               p->has_blkio_ticks = (m >= 33);
+               p->has_gtime = (m >= 35);
+               p->utime -= p->gtime;
+               p->cutime -= p->cgtime;
+               return 0;
+       } else {
+               return 1;
+       }
+}
+
+int parse_proc_statm(FILE * f, struct process_info *p)
+{
+       return (fscanf(f, "%d %d %d", &p->vmsizep, &p->vmresidentp, &p->vmsharedp) != 3);
+}
+
+int parse_proc_capbnd(FILE * f, struct process_info *p)
+{
+       p->has_capbnd = (fscanf(f, "%ld", &p->capbnd) == 1);
+       return !p->has_capbnd;
+}
+
+int parse_proc_status(FILE * f, struct process_info *p)
+{
+       char key[1024], value[1024];
+
+       while (fscanf(f, "%1023[^:]:%1023[^\n]\n", key, value) == 2) {
+               if (!strcasecmp(key, "sleepavg")) {
+                       p->sleepavg = atoi(value);
+               } else if (!strcasecmp(key, "tracerpid")) {
+                       p->tracerpid = atoi(value);
+               } else if (!strcasecmp(key, "uid")) {
+                       CHECK_ERROR(sscanf(value, "%d %d %d %d", &p->uid, &p->euid, &p->suid, &p->fsuid) != 4);
+               } else if (!strcasecmp(key, "gid")) {
+                       CHECK_ERROR(sscanf(value, "%d %d %d %d", &p->gid, &p->egid, &p->sgid, &p->fsgid) != 4);
+               } else if (!strcasecmp(key, "fdsize")) {
+                       p->fdsize = atoi(value);
+               } else if (!strcasecmp(key, "vmpeak")) {
+                       p->has_vmpeak = 1;
+                       p->vmpeak = atol(value);
+               } else if (!strcasecmp(key, "vmsize")) {
+                       p->vmsize = atol(value);
+               } else if (!strcasecmp(key, "vmlck")) {
+                       p->vmlck = atol(value);
+               } else if (!strcasecmp(key, "vmhwm")) {
+                       p->has_vmhwm = 1;
+                       p->vmhwm = atol(value);
+               } else if (!strcasecmp(key, "vmrss")) {
+                       p->vmrss = atol(value);
+               } else if (!strcasecmp(key, "vmdata")) {
+                       p->vmdata = atol(value);
+               } else if (!strcasecmp(key, "vmstk")) {
+                       p->vmstk = atol(value);
+               } else if (!strcasecmp(key, "vmexe")) {
+                       p->vmexe = atol(value);
+               } else if (!strcasecmp(key, "vmlib")) {
+                       p->vmlib = atol(value);
+               } else if (!strcasecmp(key, "vmpte")) {
+                       p->has_vmpte = 1;
+                       p->vmpte = atol(value);
+               } else if (!strcasecmp(key, "sigq")) {
+                       p->has_sigq = 1;
+                       CHECK_ERROR(sscanf(value, "%lu/%lu", &p->sigqsize, &p->sigqlim) != 2);
+               } else if (!strcasecmp(key, "sigpnd")) {
+                       CHECK_ERROR(sscanf(value, "%lx", &p->sigpending) != 1);
+               } else if (!strcasecmp(key, "shapnd")) {
+                       CHECK_ERROR(sscanf(value, "%lx", &p->sigshpending) != 1);
+               } else if (!strcasecmp(key, "sigblk")) {
+                       CHECK_ERROR(sscanf(value, "%lx", &p->sigblocked) != 1);
+               } else if (!strcasecmp(key, "sigign")) {
+                       CHECK_ERROR(sscanf(value, "%lx", &p->sigignored) != 1);
+               } else if (!strcasecmp(key, "sigcgt")) {
+                       CHECK_ERROR(sscanf(value, "%lx", &p->sigcaught) != 1);
+               } else if (!strcasecmp(key, "capinh")) {
+                       CHECK_ERROR(sscanf(value, "%lx", &p->capinh) != 1);
+               } else if (!strcasecmp(key, "capprm")) {
+                       CHECK_ERROR(sscanf(value, "%lx", &p->capprm) != 1);
+               } else if (!strcasecmp(key, "capeff")) {
+                       CHECK_ERROR(sscanf(value, "%lx", &p->capeff) != 1);
+               } else if (!strcasecmp(key, "capbnd")) {
+                       p->has_capbnd = 1;
+                       CHECK_ERROR(sscanf(value, "%lx", &p->capbnd) != 1);
+               }
+       }
+       return 0;
+}
+
+int parse_proc_wchan(FILE * f, struct process_info *p)
+{
+       return (fscanf(f, "%1023s", p->wchan_decoded) != 1);
+}
+
+int parse_proc_arrayfile(char *name, char ***s)
+{
+       FILE *f;
+       int res = 1;
+
+       if ((f = fopen(name, "r")) != NULL) {
+               char *buf;
+               int len = 0, bufsize = 1024, count = 0;
+
+               buf = malloc(bufsize);
+               while ((buf[len++] = fgetc(f)) != EOF) {
+                       if (buf[len - 1] == 0) {
+                               *s = realloc(*s, (count + 2) * sizeof(char *));
+                               (*s)[count] = malloc(len);
+                               strncpy((*s)[count], buf, len);
+                               count++;
+                               len = 0;
+                       }
+                       if (len == bufsize) {
+                               bufsize <<= 2;
+                               buf = realloc(buf, bufsize);
+                       }
+               }
+               if (*s != NULL)
+                       (*s)[count] = NULL;
+               res = 0;
+               fclose(f);
+       }
+       return res;
+}
+
+int parse_proc_file(char *name, struct process_info *p, int (*process) (FILE * f, struct process_info * p))
+{
+       FILE *f;
+
+       if ((f = fopen(name, "r")) != NULL) {
+               int res;
+
+               res = process(f, p);
+               fclose(f);
+               return res;
+       } else {
+               return 1;
+       }
+}
+
+int parse_proc_symlink(char *name, char **s)
+{
+       int count, res = 1;
+       char buf[1024];
+
+       if ((count = readlink(name, buf, sizeof(buf) - 1)) != -1) {
+               *s = realloc(*s, count + 1);
+               memcpy(*s, buf, count);
+               (*s)[count] = 0;
+               res = 0;
+       }
+       return res;
+}
+
+int parse_proc_task(char *name, int **s) {
+       struct dirent **files;
+       int cnt;
+
+       if ((cnt = scandir(name, &files, NULL, NULL)) > 0) {
+               int sz;
+
+               sz = 0;
+               *s = realloc(*s, sizeof(int) * cnt);
+               memset(*s, 0, sizeof(int) * cnt);
+               while (cnt--) {
+                       if (files[cnt]->d_name[0] != '.') {
+                               int tpid;
+
+                               tpid = atoi(files[cnt]->d_name);
+                               if (tpid != 0)
+                                       (*s)[sz++] = tpid;
+                       }
+                       free(files[cnt]);
+               }
+               free(files);
+       }
+       return 0;
+}
+
+int tty_dev_to_name(unsigned int major, unsigned int minor, char *buf, int bufsize)
+{
+       FILE *f;
+
+       if ((f = fopen("/proc/tty/drivers", "r")) != NULL) {
+               int res;
+
+               res = 1;
+               while (!feof(f)) {
+                       unsigned int matches, lmajor, lminorstart, lminorend;
+                       char lname[32];
+
+                       lminorend = 0;
+                       if ((matches = fscanf(f, "%*s %31s %u %u-%u", lname, &lmajor, &lminorstart, &lminorend)) >= 3)
+                               if ((lmajor == major) && (((matches == 3) && (lminorstart == minor)) || ((matches == 4) && (lminorstart <= minor) && (lminorend >= minor)))) {
+                                       snprintf(buf, bufsize, "%s%u", lname, minor - lminorstart);
+                                       res = 0;
+                                       break;
+                               }
+                       fscanf(f, "%*[^\n]\n");
+               }
+               fclose(f);
+               return res;
+       } else {
+               return 1;
+       }
+}
+
+void print_general(struct process_info *p)
+{
+       int i;
+       time_t start_time;
+       struct sysinfo info;
+       char tty_name[32];
+
+       start_time = time(NULL);
+       if (!sysinfo(&info))
+               start_time -= info.uptime;
+       start_time += p->start_time / sysconf(_SC_CLK_TCK);
+
+       if (p->tty_nr == 0) {
+               snprintf(tty_name, sizeof(tty_name), "(none)");
+       } else {
+               unsigned int majordev, minordev;
+
+               majordev = (p->tty_nr & 0xfff00) >> 8;
+               minordev = (p->tty_nr & 0xff) | ((p->tty_nr >> 12) & 0xfff00);
+               if (tty_dev_to_name(majordev, minordev, tty_name, sizeof(tty_name)))
+                       snprintf(tty_name, sizeof(tty_name), "%u:%u", majordev, minordev);
+       }
+
+       printf("General\n");
+       printf("\tProcess id: %d\n", p->pid);
+       printf("\tParent process id: %d\n", p->ppid);
+       printf("\tProcess group: %d\n", p->pgrp);
+       printf("\tSession id: %d\n", p->sid);
+       printf("\tThreads: %d (", p->num_threads);
+       for (i = 0; i < p->num_threads; i++)
+               printf("%d%c", p->threads[i], (i == (p->num_threads - 1)) ? ')' : ' ');
+       printf("\n");
+       printf("\tState: ");
+       switch (p->state) {
+       case 'R':
+               printf("R running at cpu %d\n", p->task_cpu);
+               break;
+       case 'S':
+               printf("S interruptable sleeping %s\n", ((p->wchan == 0) || (p->wchan == (unsigned long)-1)) ? "" : p->wchan_decoded);
+               break;
+       case 'D':
+               printf("D uninterruptable sleeping %s\n", ((p->wchan == 0) || (p->wchan == (unsigned long)-1)) ? "" : p->wchan_decoded);
+               break;
+       case 'T':
+               printf("T tracing by pid %d\n", p->tracerpid);
+               break;
+       case 'Z':
+               printf("Z zombie\n");
+               break;
+       case 'X':
+               printf("X dead\n");
+               break;
+       default:
+               printf("%c n/a\n", p->state);
+               break;
+       }
+       printf("\tSleeping average: %d %%\n", p->sleepavg);
+       printf("\tTTY: %s\n", tty_name);
+       printf("\tStart time: %s", ctime(&start_time));
+       printf("\tProcess name: %s\n", p->tcomm == NULL ? "n/a" : p->tcomm);
+       printf("\tExecutable: %s\n", p->exe == NULL ? "n/a" : p->exe);
+       printf("\tRoot directory: %s\n", p->root == NULL ? "n/a" : p->root);
+       printf("\tWorking directory: %s\n", p->cwd == NULL ? "n/a" : p->cwd);
+       printf("\tArguments: ");
+       if (p->argv == NULL)
+               printf("n/a\n");
+       else
+               for (i = 0; p->argv[i] != NULL; i++)
+                       printf("%s%d - %s\n", i == 0 ? "" : "\t           ", i, p->argv[i]);
+       printf("\tEnvironment: ");
+       if (p->env == NULL)
+               printf("n/a\n");
+       else
+               for (i = 0; p->env[i] != NULL; i++)
+                       printf("%s%d - %s\n", i == 0 ? "" : "\t             ", i, p->env[i]);
+       print_processflags("Flags:", p->flags);
+}
+
+void print_processflags(char *desc, long unsigned flags)
+{
+#define TEST_FLAG(x) if (flags & x) printf("\t       %016lx - %s\n", (long unsigned)x, #x);
+       printf("\t%s %016lx\n", desc, flags);
+       TEST_FLAG(PF_ALIGNWARN);
+       TEST_FLAG(PF_STARTING);
+       TEST_FLAG(PF_EXITING);
+       TEST_FLAG(PF_EXITPIDONE);
+       TEST_FLAG(PF_VCPU);
+       TEST_FLAG(PF_FORKNOEXEC);
+       TEST_FLAG(PF_SUPERPRIV);
+       TEST_FLAG(PF_DUMPCORE);
+       TEST_FLAG(PF_SIGNALED);
+       TEST_FLAG(PF_MEMALLOC);
+       TEST_FLAG(PF_FLUSHER);
+       TEST_FLAG(PF_USED_MATH);
+       TEST_FLAG(PF_NOFREEZE);
+       TEST_FLAG(PF_FROZEN);
+       TEST_FLAG(PF_FSTRANS);
+       TEST_FLAG(PF_KSWAPD);
+       TEST_FLAG(PF_SWAPOFF);
+       TEST_FLAG(PF_LESS_THROTTLE);
+       TEST_FLAG(PF_BORROWED_MM);
+       TEST_FLAG(PF_RANDOMIZE);
+       TEST_FLAG(PF_SWAPWRITE);
+       TEST_FLAG(PF_SPREAD_PAGE);
+       TEST_FLAG(PF_SPREAD_SLAB);
+       TEST_FLAG(PF_MEMPOLICY);
+       TEST_FLAG(PF_MUTEX_TESTER);
+       TEST_FLAG(PF_FREEZER_SKIP);
+}
+
+void print_cpu(struct process_info *p)
+{
+       printf("CPU usage\n");
+       printf("\tNice: %ld\n", p->nice);
+       printf("\tPriority level: %ld\n", p->priority);
+       printf("\tRealtime priority: %u\n", p->rt_priority);
+       printf("\tScheduling policy: ");
+       switch (p->policy) {
+       case SCHED_OTHER:
+               printf("default Linux time-sharing\n");
+               break;
+       case SCHED_FIFO:
+               printf("first in-first out\n");
+               break;
+       case SCHED_RR:
+               printf("round robin\n");
+               break;
+       case SCHED_BATCH:
+               printf("batch\n");
+               break;
+       default:
+               printf("n/a\n");
+       }
+       printf("\tRuns:");
+       if (p->has_schedstat)
+               printf(" %llu\n", p->nran);
+       else
+               printf(" n/a\n");
+       printf("\tSeconds:\n");
+       printf("\t              user    system     guest     total\n");
+       printf("\tprocess   %8.2f  %8.2f", (double) p->utime / KERNEL_HZ, (double) p->stime / KERNEL_HZ);
+       if (p->has_gtime)
+               printf("  %8.2f", (double) p->gtime / KERNEL_HZ);
+       else
+               printf("       n/a");
+       printf("  %8.2f\n", (double) (p->utime + p->stime + p->gtime) / KERNEL_HZ);
+       printf("\tchildren  %8.2f  %8.2f", (double) p->cutime / KERNEL_HZ, (double) p->cstime / KERNEL_HZ);
+       if (p->has_gtime)
+               printf("  %8.2f", (double) p->cgtime / KERNEL_HZ);
+       else
+               printf("       n/a");
+       printf("  %8.2f\n", (double) (p->cutime + p->cstime + p->cgtime) / KERNEL_HZ);
+       printf("\ttotal     %8.2f  %8.2f", (double) (p->utime + p->cutime) / KERNEL_HZ, (double) (p->stime + p->cstime) / KERNEL_HZ);
+       if (p->has_gtime)
+               printf("  %8.2f", (double) (p->gtime + p->cgtime) / KERNEL_HZ);
+       else
+               printf("       n/a");
+       printf("  %8.2f\n", (double) (p->utime + p->stime + p->gtime + p->cutime + p->cstime + p->cgtime) / KERNEL_HZ);
+}
+
+void print_signal(struct process_info *p)
+{
+       printf("Signals\n");
+       printf("\tUser signal queue:");
+       if (p->has_sigq)
+               printf(" %lu/%lu\n", p->sigqsize, p->sigqlim);
+       else
+               printf(" n/a\n");
+       print_sigset("Pending private:", p->sigpending);
+       print_sigset("Pending shared: ", p->sigshpending);
+       print_sigset("Blocked:        ", p->sigblocked);
+       print_sigset("Ignored:        ", p->sigignored);
+       print_sigset("Caught:         ", p->sigcaught);
+}
+
+void print_sigset(char *desc, long unsigned sig)
+{
+#define TEST_SIGNAL(x) if (sig & (1 << (x - 1))) printf("\t                 %d - %s\n", x, #x);
+       printf("\t%s %016lx\n", desc, sig);
+       TEST_SIGNAL(SIGHUP);
+       TEST_SIGNAL(SIGINT);
+       TEST_SIGNAL(SIGQUIT);
+       TEST_SIGNAL(SIGILL);
+       TEST_SIGNAL(SIGTRAP);
+       TEST_SIGNAL(SIGABRT);
+       TEST_SIGNAL(SIGIOT);
+       TEST_SIGNAL(SIGBUS);
+       TEST_SIGNAL(SIGFPE);
+       TEST_SIGNAL(SIGKILL);
+       TEST_SIGNAL(SIGUSR1);
+       TEST_SIGNAL(SIGSEGV);
+       TEST_SIGNAL(SIGUSR2);
+       TEST_SIGNAL(SIGPIPE);
+       TEST_SIGNAL(SIGALRM);
+       TEST_SIGNAL(SIGTERM);
+       TEST_SIGNAL(SIGSTKFLT);
+       TEST_SIGNAL(SIGCHLD);
+       TEST_SIGNAL(SIGCONT);
+       TEST_SIGNAL(SIGSTOP);
+       TEST_SIGNAL(SIGTSTP);
+       TEST_SIGNAL(SIGTTIN);
+       TEST_SIGNAL(SIGTTOU);
+       TEST_SIGNAL(SIGURG);
+       TEST_SIGNAL(SIGXCPU);
+       TEST_SIGNAL(SIGXFSZ);
+       TEST_SIGNAL(SIGVTALRM);
+       TEST_SIGNAL(SIGPROF);
+       TEST_SIGNAL(SIGWINCH);
+       TEST_SIGNAL(SIGIO);
+       TEST_SIGNAL(SIGPOLL);
+       TEST_SIGNAL(SIGPWR);
+       TEST_SIGNAL(SIGSYS);
+       TEST_SIGNAL(SIGUNUSED);
+}
+
+void print_memory(struct process_info *p)
+{
+       printf("Memory\n");
+       printf("\tOut-of-memory killer score:");
+       if (p->has_oom_score)
+               printf(" %d\n", p->oom_score);
+       else
+               printf(" n/a\n");
+       printf("\tOut-of-memory killer adjustment:");
+       if (p->has_oom_adj)
+               printf(" %d\n", p->oom_adj);
+       else
+               printf(" n/a\n");
+       printf("\tVM size: %lu kB", p->vmsize);
+       if (p->has_vmpeak)
+               printf(" (peak %lu kB)\n", p->vmpeak);
+       else
+               printf(" (peak n/a)\n");
+       printf("\tVM locked size: %lu kB\n", p->vmlck);
+       printf("\tVM resident set size: %lu kB", p->vmrss);
+       if (p->has_vmhwm)
+               printf(" (peak %lu kB)\n", p->vmhwm);
+       else
+               printf(" (peak n/a)\n");
+       printf("\tVM data segment size: %lu kB\n", p->vmdata);
+       printf("\tVM stack segment size: %lu kB\n", p->vmstk);
+       printf("\tVM text segment size: %lu kB\n", p->vmexe);
+       printf("\tVM shared library size: %lu kB\n", p->vmlib);
+       printf("\tVM page table entries size:");
+       if (p->has_vmpte)
+               printf(" %lu kB\n", p->vmpte);
+       else
+               printf(" n/a\n");
+       printf("\tPagefaults:\n");
+       printf("\t             minor     major\n");
+       printf("\tprocess   %8lu  %8lu\n", p->min_flt, p->maj_flt);
+       printf("\tchildren  %8lu  %8lu\n", p->cmin_flt, p->cmaj_flt);
+       printf("\ttotal     %8lu  %8lu\n", p->min_flt + p->cmin_flt, p->maj_flt + p->cmaj_flt);
+}
+
+void print_privilege(struct process_info *p)
+{
+       printf("Privilege\n");
+       printf("\tIdentity:\n");
+       printf("\t              real effective     saved        fs\n");
+       printf("\tuser      %8d  %8d  %8d  %8d\n", p->uid, p->euid, p->suid, p->fsuid);
+       printf("\tgroup     %8d  %8d  %8d  %8d\n", p->gid, p->egid, p->sgid, p->fsgid);
+       print_capability("Cap inheritable: ", p->capinh);
+       print_capability("Cap permitted:   ", p->capprm);
+       print_capability("Cap effective:   ", p->capeff);
+       if (p->has_capbnd)
+               print_capability("Cap bounding set:", p->capbnd);
+       else
+               printf("\tCap bounding set: n/a\n");
+}
+
+void print_capability(char *desc, long unsigned cap)
+{
+#define TEST_CAPABILITY(x) if (cap & (1 << x)) printf("\t                  %d - %s\n", x, #x);
+       printf("\t%s %016lx\n", desc, cap);
+       TEST_CAPABILITY(CAP_CHOWN);
+       TEST_CAPABILITY(CAP_DAC_OVERRIDE);
+       TEST_CAPABILITY(CAP_DAC_READ_SEARCH);
+       TEST_CAPABILITY(CAP_FOWNER);
+       TEST_CAPABILITY(CAP_FSETID);
+       TEST_CAPABILITY(CAP_KILL);
+       TEST_CAPABILITY(CAP_SETGID);
+       TEST_CAPABILITY(CAP_SETUID);
+       TEST_CAPABILITY(CAP_SETPCAP);
+       TEST_CAPABILITY(CAP_LINUX_IMMUTABLE);
+       TEST_CAPABILITY(CAP_NET_BIND_SERVICE);
+       TEST_CAPABILITY(CAP_NET_BROADCAST);
+       TEST_CAPABILITY(CAP_NET_ADMIN);
+       TEST_CAPABILITY(CAP_NET_RAW);
+       TEST_CAPABILITY(CAP_IPC_LOCK);
+       TEST_CAPABILITY(CAP_IPC_OWNER);
+       TEST_CAPABILITY(CAP_SYS_MODULE);
+       TEST_CAPABILITY(CAP_SYS_RAWIO);
+       TEST_CAPABILITY(CAP_SYS_CHROOT);
+       TEST_CAPABILITY(CAP_SYS_PTRACE);
+       TEST_CAPABILITY(CAP_SYS_PACCT);
+       TEST_CAPABILITY(CAP_SYS_ADMIN);
+       TEST_CAPABILITY(CAP_SYS_BOOT);
+       TEST_CAPABILITY(CAP_SYS_NICE);
+       TEST_CAPABILITY(CAP_SYS_RESOURCE);
+       TEST_CAPABILITY(CAP_SYS_TIME);
+       TEST_CAPABILITY(CAP_SYS_TTY_CONFIG);
+       TEST_CAPABILITY(CAP_MKNOD);
+       TEST_CAPABILITY(CAP_LEASE);
+       TEST_CAPABILITY(CAP_AUDIT_WRITE);
+       TEST_CAPABILITY(CAP_AUDIT_CONTROL);
+       TEST_CAPABILITY(CAP_FS_MASK);
+}
+
+void print_io(struct process_info *p)
+{
+       printf("I/O\n");
+       printf("\tFile descriptor table size: %d\n", p->fdsize);
+       printf("\tTime spent waiting for blocking I/O:");
+       if (p->has_blkio_ticks)
+               printf(" %.2f s\n", (double) p->blkio_ticks / KERNEL_HZ);
+       else
+               printf(" n/a\n");
+       printf("\tStatistics:");
+       if (p->has_io) {
+               printf("\n");
+               printf("\t          syscalls   tput kB   disk kB\n");
+               printf("\tread      %8llu  %8llu  %8llu\n", p->syscr, p->rchar / 1024, p->read_bytes / 1024);
+               printf("\twritten   %8llu  %8llu  %8llu\n", p->syscw, p->wchar / 1024, p->write_bytes / 1024);
+               printf("\tcancelled        -         -  %8llu\n", p->cancelled_write_bytes / 1024);
+       } else {
+               printf(" n/a\n");
+       }
+}
+
+int parse_proc(char *base, int pid, struct process_info *p)
+{
+       char filename[FILENAME_MAX];
+
+       snprintf(filename, sizeof(filename), "%s/%d/stat", base, pid);
+       if (parse_proc_file(filename, p, &parse_proc_stat)) {
+               fprintf(stderr, "psinfo: process %d does not exist\n", pid);
+               return 1;
+       }
+       snprintf(filename, sizeof(filename), "%s/%d/statm", base, pid);
+       CHECK_ERROR(parse_proc_file(filename, p, &parse_proc_statm));
+       parse_proc_file("/proc/sys/kernel/cap-bound", p, &parse_proc_capbnd);
+       snprintf(filename, sizeof(filename), "%s/%d/status", base, pid);
+       CHECK_ERROR(parse_proc_file(filename, p, &parse_proc_status));
+       snprintf(filename, sizeof(filename), "%s/%d/cmdline", base, pid);
+       CHECK_ERROR(parse_proc_arrayfile(filename, &p->argv));
+       snprintf(filename, sizeof(filename), "%s/%d/task", base, pid);
+       CHECK_ERROR(parse_proc_task(filename, &p->threads));
+       snprintf(filename, sizeof(filename), "%s/%d/cwd", base, pid);
+       parse_proc_symlink(filename, &p->cwd);
+       snprintf(filename, sizeof(filename), "%s/%d/environ", base, pid);
+       parse_proc_arrayfile(filename, &p->env);
+       snprintf(filename, sizeof(filename), "%s/%d/root", base, pid);
+       parse_proc_symlink(filename, &p->root);
+       snprintf(filename, sizeof(filename), "%s/%d/exe", base, pid);
+       parse_proc_symlink(filename, &p->exe);
+       snprintf(filename, sizeof(filename), "%s/%d/io", base, pid);
+       parse_proc_file(filename, p, &parse_proc_io);
+       snprintf(filename, sizeof(filename), "%s/%d/oom_adj", base, pid);
+       parse_proc_file(filename, p, &parse_proc_oomadj);
+       snprintf(filename, sizeof(filename), "%s/%d/oom_score", base, pid);
+       parse_proc_file(filename, p, &parse_proc_oomscore);
+       snprintf(filename, sizeof(filename), "%s/%d/schedstat", base, pid);
+       parse_proc_file(filename, p, &parse_proc_schedstat);
+       snprintf(filename, sizeof(filename), "%s/%d/wchan", base, pid);
+       parse_proc_file(filename, p, &parse_proc_wchan);
+       return 0;
+}
+
+void print_help()
+{
+       printf("psinfo 0.12\n");
+       printf("Copyright (C) 2008-2010 Ward van Wanrooij <ward@ward.nu>\n");
+       printf("This software may be distributed according to the terms of the GNU\n");
+       printf("General Public License, version 2 or (at your option) any later version.\n");
+       printf("\n");
+       printf("Syntax: psinfo -agcimpsHV pid\n");
+       printf("\t-a show all information (default)\n");
+       printf("\t-g show general information\n");
+       printf("\t-c show cpu information\n");
+       printf("\t-i show i/o information\n");
+       printf("\t-m show memory information\n");
+       printf("\t-p show privilege information\n");
+       printf("\t-s show signal information\n");
+       printf("\t-H show help\n");
+       printf("\t-V show version\n");
+}
+
+int main(int argc, char **argv)
+{
+       struct process_info *p;
+       int pid, output_general = 0, output_cpu = 0, output_io = 0, output_memory = 0, output_privilege = 0, output_signal = 0, result = 0;
+       char c;
+
+       opterr = 0;
+       if (argc <= 1) {
+               print_help();
+               return 0;
+       }
+       while ((c = getopt(argc, argv, "agcimpsHV")) != -1) {
+               switch (c) {
+               case 'H':
+               case 'V':
+                       print_help();
+                       return 0;
+               case 'a':
+                       break;
+               case 'g':
+                       output_general = 1;
+                       break;
+               case 'c':
+                       output_cpu = 1;
+                       break;
+               case 'i':
+                       output_io = 1;
+                       break;
+               case 'm':
+                       output_memory = 1;
+                       break;
+               case 'p':
+                       output_privilege = 1;
+                       break;
+               case 's':
+                       output_signal = 1;
+                       break;
+               default:
+                       fprintf(stderr, "psinfo: invalid command line option '%c'\n", c);
+                       return 1;
+               }
+       }
+       if (optind < argc) {
+               char *end;
+
+               errno = 0;
+               pid = strtol(argv[optind], &end, 10);
+               if (errno || (argv[optind] == end)) {
+                       fprintf(stderr, "psinfo: invalid process identifier '%s'\n", argv[optind]);
+                       return 1;
+               }
+       } else {
+               fprintf(stderr, "psinfo: no process identifier specified\n");
+               return 1;
+       }
+       if ((output_general | output_cpu | output_io | output_memory | output_privilege | output_signal) == 0) {
+               output_general = 1;
+               output_cpu = 1;
+               output_io = 1;
+               output_memory = 1;
+               output_privilege = 1;
+               output_signal = 1;
+       }
+       if ((p = malloc(sizeof(struct process_info))) != NULL) {
+               tzset();
+               memset(p, 0, sizeof(struct process_info));
+               if (!(result = (parse_proc("/proc", pid, p)))) {
+                       if (output_general)
+                               print_general(p);
+                       if (output_cpu)
+                               print_cpu(p);
+                       if (output_io)
+                               print_io(p);
+                       if (output_memory)
+                               print_memory(p);
+                       if (output_privilege)
+                               print_privilege(p);
+                       if (output_signal)
+                               print_signal(p);
+               }
+               free(p->threads);
+               free(p);
+       }
+       return result;
+}
diff --git a/psinfo.h b/psinfo.h
new file mode 100644 (file)
index 0000000..9a7583c
--- /dev/null
+++ b/psinfo.h
@@ -0,0 +1,163 @@
+/***********************************************************************
+*
+* psinfo.h
+*
+* psinfo: process info
+*
+* Copyright (C) 2008 Ward van Wanrooij <ward@ward.nu>
+*
+* This software may be distributed according to the terms of the GNU
+* General Public License, version 2 or (at your option) any later
+* version.
+*
+***********************************************************************/
+
+/*
+g general
+m memory usage
+p cpu usage
+s signals
+c privilege
+i io
+_ skipped
+
+? = might not be defined in all kernels
+*/
+
+struct process_info {
+       int pid;                        //g stat: process id
+       int ppid;                       //g stat: process id of the parent process
+       int pgrp;                       //g stat: pgrp of the process
+       int sid;                        //g stat: session id
+       int tty_nr;                     //g stat: tty the process uses
+       int tty_pgrp;                   //_ stat: pgrp of the tty
+       int num_threads;                //g stat: number of threads
+       int exit_signal;                //_ stat: signal to send to parent thread on exit 
+       int task_cpu;                   //g stat: which CPU the task is scheduled on
+       char tcomm[1024];               //g stat: filename of the executable
+       char state;                     //g stat: state (R is running, S is sleeping, D is sleeping in an uninterruptible wait, Z is zombie, T is traced or stopped)
+       long unsigned min_flt;          //m stat: number of minor faults
+       long unsigned cmin_flt;         //m stat: number of minor faults with child's
+       long unsigned maj_flt;          //m stat: number of major faults
+       long unsigned cmaj_flt;         //m stat: number of major faults with child's
+       long unsigned utime;            //p rrstat: user mode jiffies
+       long unsigned stime;            //p stat: kernel mode jiffies
+       long unsigned gtime;            //p? stat: guest
+       int has_gtime;
+       long unsigned vsize;            //_ stat: virtual memory size
+       long unsigned rlim;             //_ stat: current limit in bytes on the rss
+       long unsigned start_code;       //_ stat: address above which program text can run
+       long unsigned end_code;         //_ stat: address below which program text can run
+       long unsigned start_stack;      //_ stat: address of the start of the stack
+       long unsigned esp;              //_ stat: current value of ESP
+       long unsigned eip;              //_ stat: current value of EIP
+       long unsigned wchan;            //g stat: address where process went to sleep
+       char wchan_decoded[1024];       //g? wchan: name of the kernel function in which the process is sleeping
+       long nice;                      //p stat:
+       long cutime;                    //p stat: user mode jiffies with child's
+       long cstime;                    //p stat: kernel mode jiffies with child's
+       long cgtime;                    //p? stat: guest
+       long priority;                  //p stat: priority level
+       long rss;                       //m stat: resident set memory size
+       long long unsigned start_time;  //g stat: time the process started after system boot
+       long long unsigned blkio_ticks; //i? stat: time spent waiting for block IO
+       int has_blkio_ticks;
+       unsigned int rt_priority;       //p stat: realtime priority
+       unsigned int policy;            //p stat: scheduling policy (man sched_setscheduler)
+       unsigned int flags;             //g stat: task flags
+       int vmsizep;                    //_ statm: total program size (pages)
+       int vmresidentp;                //_ statm: size of memory portions (pages)
+       int vmsharedp;                  //_ statm: number of pages that are shared
+       int sleepavg;                   //g status
+       int tracerpid;                  //g status
+       int uid;                        //c status: real user id
+       int euid;                       //c status: effective user id
+       int suid;                       //c status: saved user id
+       int fsuid;                      //c status: file system user id
+       int gid;                        //c status: real group id
+       int egid;                       //c status: effective group id
+       int sgid;                       //c status: saved group id
+       int fsgid;                      //c status: file system group id
+       int fdsize;                     //i status: size of file descriptor table
+       long unsigned vmpeak;           //m? status: 
+       int has_vmpeak;
+       long unsigned vmsize;           //m status:
+       long unsigned vmlck;            //m status:
+       long unsigned vmhwm;            //m? status:
+       int has_vmhwm;
+       long unsigned vmrss;            //m status:
+       long unsigned vmdata;           //m status:
+       long unsigned vmstk;            //m status:
+       long unsigned vmexe;            //m status:
+       long unsigned vmlib;            //m status:
+       long unsigned vmpte;            //m? status:
+       int has_vmpte;
+       long unsigned sigqsize;         //s? status: signal queue size
+       int has_sigq;
+       long unsigned sigqlim;          //s? status: signal queue limit
+       long unsigned sigpending;       //s status: pending signals
+       long unsigned sigshpending;     //s status: pending shared signals
+       long unsigned sigblocked;       //s status: blocked signals
+       long unsigned sigignored;       //s status: ignored signals
+       long unsigned sigcaught;        //s status: caught signals        
+       long unsigned capinh;           //c status: inheritable capabilities
+       long unsigned capprm;           //c status: permitted capabilities
+       long unsigned capeff;           //c status: effective capabilities
+       long unsigned capbnd;           //c? status: capability bounding set
+       int has_capbnd;
+       char **argv;                    //g cmdline: command line arguments
+       char *cwd;                      //g? cwd: current working directory
+       char **env;                     //g? environ: environment
+       char *root;                     //g? root: (ch)root directory
+       char *exe;                      //g? exe: executable
+       int has_io;
+       long long unsigned rchar;       //i? io: chars read
+       long long unsigned wchar;       //i? io: chars written
+       long long unsigned syscr;       //i? io: number of read syscalls
+       long long unsigned syscw;       //i? io: number of write syscalls
+       long long unsigned read_bytes;  //i? io: bytes read
+       long long unsigned write_bytes; //i? io: bytes written
+       long long unsigned cancelled_write_bytes;       //i? io: bytes written cancelled
+       int has_oom_score;
+       int oom_score;                          //m? oom_score: out-of-memory killer score
+       int has_oom_adj;
+       int oom_adj;                            //m? oom_adj: out-ouf-memory killer adjustment
+       int has_schedstat;
+       long long unsigned run_ticks;   //_? schedstat: time spent on the cpu
+       long long unsigned wait_ticks;  //_? schedstat: time spent waiting on a runqueue
+       long long unsigned nran;        //p? schedstat: # of times run on this cpu
+       int *threads;                   //g task: pids of threads
+};
+
+int main(int argc, char **argv);
+
+int parse_proc_io(FILE * f, struct process_info *p);
+int parse_proc_oomscore(FILE * f, struct process_info *p);
+int parse_proc_oomadj(FILE * f, struct process_info *p);
+int parse_proc_schedstat(FILE * f, struct process_info *p);
+int parse_proc_stat(FILE * f, struct process_info *p);
+int parse_proc_statm(FILE * f, struct process_info *p);
+int parse_proc_capbnd(FILE * f, struct process_info *p);
+int parse_proc_status(FILE * f, struct process_info *p);
+int parse_proc_wchan(FILE * f, struct process_info *p);
+
+int parse_proc_arrayfile(char *name, char ***s);
+int parse_proc_file(char *name, struct process_info *p, int (*process) (FILE * f, struct process_info * p));
+int parse_proc_symlink(char *name, char **s);
+int parse_proc_task(char *name, int **s);
+int parse_proc(char *base, int pid, struct process_info *p);
+
+int tty_dev_to_name(unsigned int major, unsigned int minor, char *buf, int bufsize);
+
+void print_general(struct process_info *p);
+void print_processflags(char *desc, long unsigned flags);
+void print_cpu(struct process_info *p);
+void print_io(struct process_info *p);
+void print_memory(struct process_info *p);
+void print_privilege(struct process_info *p);
+void print_capability(char *desc, long unsigned cap);
+void print_signal(struct process_info *p);
+void print_sigset(char *desc, long unsigned sigset);
+void print_help(void);
+
+#define KERNEL_HZ sysconf(_SC_CLK_TCK)