#include <ctype.h>
#include <dirent.h>
#include <fcntl.h>
#include <inttypes.h>
#include <pwd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <cutils/sched_policy.h>
static char *nexttoksep(char **strp, char *sep)
{
char *p = strsep(strp,sep);
return (p == 0) ? "" : p;
}
static char *nexttok(char **strp)
{
return nexttoksep(strp, " ");
}
#define SHOW_PRIO 1
#define SHOW_TIME 2
#define SHOW_POLICY 4
#define SHOW_CPU 8
#define SHOW_MACLABEL 16
#define SHOW_NUMERIC_UID 32
#define SHOW_ABI 64
#if __LP64__
#define PC_WIDTH 10 /* Realistically, the top bits will be 0, so don't waste space. */
#else
#define PC_WIDTH (2*sizeof(uintptr_t))
#endif
static int display_flags = 0;
static int ppid_filter = 0;
static void print_exe_abi(int pid);
static int ps_line(int pid, int tid)
{
char statline[1024];
char cmdline[1024];
char macline[1024];
char user[32];
struct stat stats;
int r;
char *ptr, *name, *state;
int ppid;
unsigned rss, vss;
uintptr_t eip;
unsigned utime, stime;
int prio, nice, rtprio, sched, psr;
struct passwd *pw;
sprintf(statline, "/proc/%d", tid ? tid : pid);
stat(statline, &stats);
if(tid) {
sprintf(statline, "/proc/%d/task/%d/stat", pid, tid);
cmdline[0] = 0;
snprintf(macline, sizeof(macline), "/proc/%d/task/%d/attr/current", pid, tid);
} else {
sprintf(statline, "/proc/%d/stat", pid);
sprintf(cmdline, "/proc/%d/cmdline", pid);
snprintf(macline, sizeof(macline), "/proc/%d/attr/current", pid);
int fd = open(cmdline, O_RDONLY);
if(fd == 0) {
r = 0;
} else {
r = read(fd, cmdline, 1023);
close(fd);
if(r < 0) r = 0;
}
cmdline[r] = 0;
}
int fd = open(statline, O_RDONLY);
if(fd == 0) return -1;
r = read(fd, statline, 1023);
close(fd);
if(r < 0) return -1;
statline[r] = 0;
ptr = statline;
nexttok(&ptr); // skip pid
ptr++; // skip "("
name = ptr;
ptr = strrchr(ptr, ')'); // Skip to *last* occurence of ')',
*ptr++ = '\0'; // and null-terminate name.
ptr++; // skip " "
state = nexttok(&ptr);
ppid = atoi(nexttok(&ptr));
nexttok(&ptr); // pgrp
nexttok(&ptr); // sid
nexttok(&ptr); // tty
nexttok(&ptr); // tpgid
nexttok(&ptr); // flags
nexttok(&ptr); // minflt
nexttok(&ptr); // cminflt
nexttok(&ptr); // majflt
nexttok(&ptr); // cmajflt
#if 1
utime = atoi(nexttok(&ptr));
stime = atoi(nexttok(&ptr));
#else
nexttok(&ptr); // utime
nexttok(&ptr); // stime
#endif
nexttok(&ptr); // cutime
nexttok(&ptr); // cstime
prio = atoi(nexttok(&ptr));
nice = atoi(nexttok(&ptr));
nexttok(&ptr); // threads
nexttok(&ptr); // itrealvalue
nexttok(&ptr); // starttime
vss = strtoul(nexttok(&ptr), 0, 10); // vsize
rss = strtoul(nexttok(&ptr), 0, 10); // rss
nexttok(&ptr); // rlim
nexttok(&ptr); // startcode
nexttok(&ptr); // endcode
nexttok(&ptr); // startstack
nexttok(&ptr); // kstkesp
eip = strtoul(nexttok(&ptr), 0, 10); // kstkeip
nexttok(&ptr); // signal
nexttok(&ptr); // blocked
nexttok(&ptr); // sigignore
nexttok(&ptr); // sigcatch
nexttok(&ptr); // wchan
nexttok(&ptr); // nswap
nexttok(&ptr); // cnswap
nexttok(&ptr); // exit signal
psr = atoi(nexttok(&ptr)); // processor
rtprio = atoi(nexttok(&ptr)); // rt_priority
sched = atoi(nexttok(&ptr)); // scheduling policy
nexttok(&ptr); // tty
if(tid != 0) {
ppid = pid;
pid = tid;
}
pw = getpwuid(stats.st_uid);
if(pw == 0 || (display_flags & SHOW_NUMERIC_UID)) {
sprintf(user,"%d",(int)stats.st_uid);
} else {
strcpy(user,pw->pw_name);
}
if(ppid_filter != 0 && ppid != ppid_filter) {
return 0;
}
if (display_flags & SHOW_MACLABEL) {
fd = open(macline, O_RDONLY);
strcpy(macline, "-");
if (fd >= 0) {
r = read(fd, macline, sizeof(macline)-1);
close(fd);
if (r > 0)
macline[r] = 0;
}
printf("%-30s ", macline);
}
printf("%-9s %-5d %-5d %-6d %-5d", user, pid, ppid, vss / 1024, rss * 4);
if (display_flags & SHOW_CPU)
printf(" %-2d", psr);
if (display_flags & SHOW_PRIO)
printf(" %-5d %-5d %-5d %-5d", prio, nice, rtprio, sched);
if (display_flags & SHOW_POLICY) {
SchedPolicy p;
if (get_sched_policy(pid, &p) < 0)
printf(" un ");
else
printf(" %.2s ", get_sched_policy_name(p));
}
char path[PATH_MAX];
snprintf(path, sizeof(path), "/proc/%d/wchan", pid);
char wchan[10];
fd = open(path, O_RDONLY);
ssize_t wchan_len = read(fd, wchan, sizeof(wchan));
if (wchan_len == -1) {
wchan[wchan_len = 0] = '\0';
}
close(fd);
printf(" %10.*s %0*" PRIxPTR " %s ", (int) wchan_len, wchan, (int) PC_WIDTH, eip, state);
if (display_flags & SHOW_ABI) {
print_exe_abi(pid);
}
printf("%s", cmdline[0] ? cmdline : name);
if(display_flags&SHOW_TIME)
printf(" (u:%d, s:%d)", utime, stime);
printf("\n");
return 0;
}
static void print_exe_abi(int pid)
{
int fd, r;
char exeline[1024];
sprintf(exeline, "/proc/%d/exe", pid);
fd = open(exeline, O_RDONLY);
if(fd == 0) {
printf(" ");
return;
}
r = read(fd, exeline, 5 /* 4 byte ELFMAG + 1 byte EI_CLASS */);
close(fd);
if(r < 0) {
printf(" ");
return;
}
if (memcmp("\177ELF", exeline, 4) != 0) {
printf("?? ");
return;
}
switch (exeline[4]) {
case 1:
printf("32 ");
return;
case 2:
printf("64 ");
return;
default:
printf("?? ");
return;
}
}
void ps_threads(int pid)
{
char tmp[128];
DIR *d;
struct dirent *de;
sprintf(tmp,"/proc/%d/task",pid);
d = opendir(tmp);
if(d == 0) return;
while((de = readdir(d)) != 0){
if(isdigit(de->d_name[0])){
int tid = atoi(de->d_name);
if(tid == pid) continue;
ps_line(pid, tid);
}
}
closedir(d);
}
int ps_main(int argc, char **argv)
{
DIR *d;
struct dirent *de;
int pidfilter = 0;
int threads = 0;
while(argc > 1){
if(!strcmp(argv[1],"-t")) {
threads = 1;
} else if(!strcmp(argv[1],"-n")) {
display_flags |= SHOW_NUMERIC_UID;
} else if(!strcmp(argv[1],"-x")) {
display_flags |= SHOW_TIME;
} else if(!strcmp(argv[1], "-Z")) {
display_flags |= SHOW_MACLABEL;
} else if(!strcmp(argv[1],"-P")) {
display_flags |= SHOW_POLICY;
} else if(!strcmp(argv[1],"-p")) {
display_flags |= SHOW_PRIO;
} else if(!strcmp(argv[1],"-c")) {
display_flags |= SHOW_CPU;
} else if(!strcmp(argv[1],"--abi")) {
display_flags |= SHOW_ABI;
} else if(!strcmp(argv[1],"--ppid")) {
ppid_filter = atoi(argv[2]);
if (ppid_filter == 0) {
/* Bug 26554285: Use printf because some apps require at least
* one line of output to stdout even for errors.
*/
printf("bad ppid '%s'\n", argv[2]);
return 1;
}
argc--;
argv++;
} else {
pidfilter = atoi(argv[1]);
if (pidfilter == 0) {
/* Bug 26554285: Use printf because some apps require at least
* one line of output to stdout even for errors.
*/
printf("bad pid '%s'\n", argv[1]);
return 1;
}
}
argc--;
argv++;
}
if (display_flags & SHOW_MACLABEL) {
printf("LABEL ");
}
printf("USER PID PPID VSIZE RSS %s%s %sWCHAN %*s %sNAME\n",
(display_flags&SHOW_CPU)?"CPU ":"",
(display_flags&SHOW_PRIO)?"PRIO NICE RTPRI SCHED ":"",
(display_flags&SHOW_POLICY)?"PCY " : "",
(int) PC_WIDTH, "PC",
(display_flags&SHOW_ABI)?"ABI " : "");
d = opendir("/proc");
if(d == 0) return -1;
while((de = readdir(d)) != 0){
if(isdigit(de->d_name[0])){
int pid = atoi(de->d_name);
if(!pidfilter || (pidfilter == pid)) {
ps_line(pid, 0);
if(threads) ps_threads(pid);
}
}
}
closedir(d);
return 0;
}