platform_system_core/toolbox/ps.c
Christopher Ferris 78e08066f2 Always have ps output to stdout at least one line.
There is at least one app that assumes that you will always get at least
one line of output in stdout from ps.

To fix this, move error output to stdout, and move the check of whether
/proc can be opened until after the ps header is printed.

Bug: 26554285
Change-Id: I6d9342aafd5c6f728735507cdd87a48a8e0373ac
2016-01-28 16:08:08 -08:00

334 lines
8.6 KiB
C

#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;
}