David van Moolenbroek b89261ba01 Rename top(1) to mtop(1), import NetBSD top(1)
Due to differences in (mainly) measuring and accumulating CPU times,
the two top programs end up serving different purposes: the NetBSD
top is a system administration tool, while the MINIX3 top (now mtop)
is a performance debugging tool.  Therefore, we keep both.

The newly imported BSD top has a few MINIX3-specific changes.  CPU
statistics separate system time from kernel time, rather than kernel
time from time spent on handling interrupts.  Memory statistics show
numbers that are currently relevant for MINIX3.  Swap statistics are
disabled entirely.  All of these changes effectively bring it closer
to how mtop already worked as well.

Change-Id: I9611917cb03e164ddf012c5def6da0e7fede826d
2016-01-13 20:32:53 +01:00

1356 lines
32 KiB
C

/*
* Copyright (c) 1984 through 2008, William LeFebvre
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
*
* * Neither the name of William LeFebvre nor the names of other
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/*
* top - a top users display for Unix
*
* SYNOPSIS: Linux 1.2.x, 1.3.x, 2.x, using the /proc filesystem
*
* DESCRIPTION:
* This is the machine-dependent module for Linux 1.2.x, 1.3.x or 2.x.
*
* LIBS:
*
* CFLAGS: -DHAVE_GETOPT -DHAVE_STRERROR -DORDER
*
* TERMCAP: -lcurses
*
* AUTHOR: Richard Henderson <rth@tamu.edu>
* Order support added by Alexey Klimkin <kad@klon.tme.mcst.ru>
* Ported to 2.4 by William LeFebvre
* Additions for 2.6 by William LeFebvre
*/
#include "config.h"
#include <sys/types.h>
#include <time.h>
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
#include <errno.h>
#include <dirent.h>
#include <string.h>
#include <math.h>
#include <ctype.h>
#include <sys/time.h>
#include <sys/stat.h>
#include <sys/vfs.h>
#include <sys/param.h> /* for HZ */
#include <asm/page.h> /* for PAGE_SHIFT */
#if 0
#include <linux/proc_fs.h> /* for PROC_SUPER_MAGIC */
#else
#define PROC_SUPER_MAGIC 0x9fa0
#endif
#include "top.h"
#include "hash.h"
#include "machine.h"
#include "utils.h"
#include "username.h"
#define PROCFS "/proc"
extern char *myname;
/*=PROCESS INFORMATION==================================================*/
struct top_proc
{
pid_t pid;
uid_t uid;
char *name;
int pri, nice, threads;
unsigned long size, rss, shared; /* in k */
int state;
unsigned long time;
unsigned long start_time;
double pcpu;
struct top_proc *next;
};
/*=STATE IDENT STRINGS==================================================*/
#define NPROCSTATES 7
static char *state_abbrev[NPROCSTATES+1] =
{
"", "run", "sleep", "disk", "zomb", "stop", "swap",
NULL
};
static char *procstatenames[NPROCSTATES+1] =
{
"", " running, ", " sleeping, ", " uninterruptable, ",
" zombie, ", " stopped, ", " swapping, ",
NULL
};
#define NCPUSTATES 5
static char *cpustatenames[NCPUSTATES+1] =
{
"user", "nice", "system", "idle", "iowait",
NULL
};
static int show_iowait = 0;
#define KERNELCTXT 0
#define KERNELFLT 1
#define KERNELINTR 2
#define KERNELNEWPROC 3
#define NKERNELSTATS 4
static char *kernelnames[NKERNELSTATS+1] =
{
" ctxsw, ", " flt, ", " intr, ", " newproc",
NULL
};
#define MEMUSED 0
#define MEMFREE 1
#define MEMSHARED 2
#define MEMBUFFERS 3
#define MEMCACHED 4
#define NMEMSTATS 5
static char *memorynames[NMEMSTATS+1] =
{
"K used, ", "K free, ", "K shared, ", "K buffers, ", "K cached",
NULL
};
#define SWAPUSED 0
#define SWAPFREE 1
#define SWAPCACHED 2
#define NSWAPSTATS 3
static char *swapnames[NSWAPSTATS+1] =
{
"K used, ", "K free, ", "K cached",
NULL
};
static char fmt_header[] =
" PID X THR PRI NICE SIZE RES STATE TIME CPU COMMAND";
static char proc_header_thr[] =
" PID %-9s THR PRI NICE SIZE RES SHR STATE TIME CPU COMMAND";
static char proc_header_nothr[] =
" PID %-9s PRI NICE SIZE RES SHR STATE TIME CPU COMMAND";
/* these are names given to allowed sorting orders -- first is default */
char *ordernames[] =
{"cpu", "size", "res", "time", "command", NULL};
/* forward definitions for comparison functions */
int compare_cpu();
int compare_size();
int compare_res();
int compare_time();
int compare_cmd();
int (*proc_compares[])() = {
compare_cpu,
compare_size,
compare_res,
compare_time,
compare_cmd,
NULL };
/*=SYSTEM STATE INFO====================================================*/
/* these are for calculating cpu state percentages */
static long cp_time[NCPUSTATES];
static long cp_old[NCPUSTATES];
static long cp_diff[NCPUSTATES];
/* for calculating the exponential average */
static struct timeval lasttime = { 0, 0 };
static struct timeval timediff = { 0, 0 };
static long elapsed_msecs;
/* these are for keeping track of processes and tasks */
#define HASH_SIZE (1003)
#define INITIAL_ACTIVE_SIZE (256)
#define PROCBLOCK_SIZE (32)
static hash_table *ptable;
static hash_table *tasktable;
static struct top_proc **pactive;
static struct top_proc **nextactive;
static unsigned int activesize = 0;
static time_t boottime = -1;
static int have_task = 0;
/* these are counters that need to be track */
static unsigned long last_ctxt = 0;
static unsigned long last_intr = 0;
static unsigned long last_newproc = 0;
static unsigned long last_flt = 0;
/* these are for passing data back to the machine independant portion */
static int cpu_states[NCPUSTATES];
static int process_states[NPROCSTATES];
static int kernel_stats[NKERNELSTATS];
static long memory_stats[NMEMSTATS];
static long swap_stats[NSWAPSTATS];
/* useful macros */
#define bytetok(x) (((x) + 512) >> 10)
#define pagetok(x) ((x) << (PAGE_SHIFT - 10))
#define HASH(x) (((x) * 1686629713U) % HASH_SIZE)
/* calculate a per-second rate using milliseconds */
#define per_second(n, msec) (((n) * 1000) / (msec))
/*======================================================================*/
static inline char *
skip_ws(const char *p)
{
while (isspace(*p)) p++;
return (char *)p;
}
static inline char *
skip_token(const char *p)
{
while (isspace(*p)) p++;
while (*p && !isspace(*p)) p++;
return (char *)p;
}
static void
xfrm_cmdline(char *p, int len)
{
while (--len > 0)
{
if (*p == '\0')
{
*p = ' ';
}
p++;
}
}
static void
update_procname(struct top_proc *proc, char *cmd)
{
printable(cmd);
if (proc->name == NULL)
{
proc->name = strdup(cmd);
}
else if (strcmp(proc->name, cmd) != 0)
{
free(proc->name);
proc->name = strdup(cmd);
}
}
/*
* Process structures are allocated and freed as needed. Here we
* keep big pools of them, adding more pool as needed. When a
* top_proc structure is freed, it is added to a freelist and reused.
*/
static struct top_proc *freelist = NULL;
static struct top_proc *procblock = NULL;
static struct top_proc *procmax = NULL;
static struct top_proc *
new_proc()
{
struct top_proc *p;
if (freelist)
{
p = freelist;
freelist = freelist->next;
}
else if (procblock)
{
p = procblock;
if (++procblock >= procmax)
{
procblock = NULL;
}
}
else
{
p = procblock = (struct top_proc *)calloc(PROCBLOCK_SIZE,
sizeof(struct top_proc));
procmax = procblock++ + PROCBLOCK_SIZE;
}
/* initialization */
if (p->name != NULL)
{
free(p->name);
p->name = NULL;
}
return p;
}
static void
free_proc(struct top_proc *proc)
{
proc->next = freelist;
freelist = proc;
}
int
machine_init(struct statics *statics)
{
/* make sure the proc filesystem is mounted */
{
struct statfs sb;
if (statfs(PROCFS, &sb) < 0 || sb.f_type != PROC_SUPER_MAGIC)
{
fprintf(stderr, "%s: proc filesystem not mounted on " PROCFS "\n",
myname);
return -1;
}
}
/* chdir to the proc filesystem to make things easier */
chdir(PROCFS);
/* a few preliminary checks */
{
int fd;
char buff[128];
char *p;
int cnt = 0;
unsigned long uptime;
struct timeval tv;
struct stat st;
/* get a boottime */
if ((fd = open("uptime", 0)) != -1)
{
if (read(fd, buff, sizeof(buff)) > 0)
{
uptime = strtoul(buff, &p, 10);
gettimeofday(&tv, 0);
boottime = tv.tv_sec - uptime;
}
close(fd);
}
/* see how many states we get from stat */
if ((fd = open("stat", 0)) != -1)
{
if (read(fd, buff, sizeof(buff)) > 0)
{
if ((p = strchr(buff, '\n')) != NULL)
{
*p = '\0';
p = buff;
while (*p != '\0')
{
if (*p++ == ' ')
{
cnt++;
}
}
}
}
close(fd);
}
if (cnt > 5)
{
/* we have iowait */
show_iowait = 1;
}
/* see if we have task subdirs */
if (stat("self/task", &st) != -1 && S_ISDIR(st.st_mode))
{
dprintf("we have task directories\n");
have_task = 1;
}
}
/* if we aren't showing iowait, then we have to tweak cpustatenames */
if (!show_iowait)
{
cpustatenames[4] = NULL;
}
/* fill in the statics information */
statics->procstate_names = procstatenames;
statics->cpustate_names = cpustatenames;
statics->kernel_names = kernelnames;
statics->memory_names = memorynames;
statics->swap_names = swapnames;
statics->order_names = ordernames;
statics->boottime = boottime;
statics->flags.fullcmds = 1;
statics->flags.warmup = 1;
statics->flags.threads = 1;
/* allocate needed space */
pactive = (struct top_proc **)malloc(sizeof(struct top_proc *) * INITIAL_ACTIVE_SIZE);
activesize = INITIAL_ACTIVE_SIZE;
/* create process and task hashes */
ptable = hash_create(HASH_SIZE);
tasktable = hash_create(HASH_SIZE);
/* all done! */
return 0;
}
void
get_system_info(struct system_info *info)
{
char buffer[4096+1];
int fd, len;
char *p;
struct timeval thistime;
unsigned long intr = 0;
unsigned long ctxt = 0;
unsigned long newproc = 0;
unsigned long flt = 0;
/* timestamp and time difference */
gettimeofday(&thistime, 0);
timersub(&thistime, &lasttime, &timediff);
elapsed_msecs = timediff.tv_sec * 1000 + timediff.tv_usec / 1000;
lasttime = thistime;
/* get load averages */
if ((fd = open("loadavg", O_RDONLY)) != -1)
{
if ((len = read(fd, buffer, sizeof(buffer)-1)) > 0)
{
buffer[len] = '\0';
info->load_avg[0] = strtod(buffer, &p);
info->load_avg[1] = strtod(p, &p);
info->load_avg[2] = strtod(p, &p);
p = skip_token(p); /* skip running/tasks */
p = skip_ws(p);
if (*p)
{
info->last_pid = atoi(p);
}
else
{
info->last_pid = -1;
}
}
close(fd);
}
/* get the cpu time info */
if ((fd = open("stat", O_RDONLY)) != -1)
{
if ((len = read(fd, buffer, sizeof(buffer)-1)) > 0)
{
buffer[len] = '\0';
p = skip_token(buffer); /* "cpu" */
cp_time[0] = strtoul(p, &p, 0);
cp_time[1] = strtoul(p, &p, 0);
cp_time[2] = strtoul(p, &p, 0);
cp_time[3] = strtoul(p, &p, 0);
if (show_iowait)
{
cp_time[4] = strtoul(p, &p, 0);
}
/* convert cp_time counts to percentages */
percentages(NCPUSTATES, cpu_states, cp_time, cp_old, cp_diff);
/* get the rest of it */
p = strchr(p, '\n');
while (p != NULL)
{
p++;
if (strncmp(p, "intr ", 5) == 0)
{
p = skip_token(p);
intr = strtoul(p, &p, 10);
}
else if (strncmp(p, "ctxt ", 5) == 0)
{
p = skip_token(p);
ctxt = strtoul(p, &p, 10);
}
else if (strncmp(p, "processes ", 10) == 0)
{
p = skip_token(p);
newproc = strtoul(p, &p, 10);
}
p = strchr(p, '\n');
}
kernel_stats[KERNELINTR] = per_second(intr - last_intr, elapsed_msecs);
kernel_stats[KERNELCTXT] = per_second(ctxt - last_ctxt, elapsed_msecs);
kernel_stats[KERNELNEWPROC] = per_second(newproc - last_newproc, elapsed_msecs);
last_intr = intr;
last_ctxt = ctxt;
last_newproc = newproc;
}
close(fd);
}
/* get system wide memory usage */
if ((fd = open("meminfo", O_RDONLY)) != -1)
{
char *p;
int mem = 0;
int swap = 0;
unsigned long memtotal = 0;
unsigned long memfree = 0;
unsigned long swaptotal = 0;
if ((len = read(fd, buffer, sizeof(buffer)-1)) > 0)
{
buffer[len] = '\0';
p = buffer-1;
/* iterate thru the lines */
while (p != NULL)
{
p++;
if (p[0] == ' ' || p[0] == '\t')
{
/* skip */
}
else if (strncmp(p, "Mem:", 4) == 0)
{
p = skip_token(p); /* "Mem:" */
p = skip_token(p); /* total memory */
memory_stats[MEMUSED] = strtoul(p, &p, 10);
memory_stats[MEMFREE] = strtoul(p, &p, 10);
memory_stats[MEMSHARED] = strtoul(p, &p, 10);
memory_stats[MEMBUFFERS] = strtoul(p, &p, 10);
memory_stats[MEMCACHED] = strtoul(p, &p, 10);
memory_stats[MEMUSED] = bytetok(memory_stats[MEMUSED]);
memory_stats[MEMFREE] = bytetok(memory_stats[MEMFREE]);
memory_stats[MEMSHARED] = bytetok(memory_stats[MEMSHARED]);
memory_stats[MEMBUFFERS] = bytetok(memory_stats[MEMBUFFERS]);
memory_stats[MEMCACHED] = bytetok(memory_stats[MEMCACHED]);
mem = 1;
}
else if (strncmp(p, "Swap:", 5) == 0)
{
p = skip_token(p); /* "Swap:" */
p = skip_token(p); /* total swap */
swap_stats[SWAPUSED] = strtoul(p, &p, 10);
swap_stats[SWAPFREE] = strtoul(p, &p, 10);
swap_stats[SWAPUSED] = bytetok(swap_stats[SWAPUSED]);
swap_stats[SWAPFREE] = bytetok(swap_stats[SWAPFREE]);
swap = 1;
}
else if (!mem && strncmp(p, "MemTotal:", 9) == 0)
{
p = skip_token(p);
memtotal = strtoul(p, &p, 10);
}
else if (!mem && memtotal > 0 && strncmp(p, "MemFree:", 8) == 0)
{
p = skip_token(p);
memfree = strtoul(p, &p, 10);
memory_stats[MEMUSED] = memtotal - memfree;
memory_stats[MEMFREE] = memfree;
}
else if (!mem && strncmp(p, "MemShared:", 10) == 0)
{
p = skip_token(p);
memory_stats[MEMSHARED] = strtoul(p, &p, 10);
}
else if (!mem && strncmp(p, "Buffers:", 8) == 0)
{
p = skip_token(p);
memory_stats[MEMBUFFERS] = strtoul(p, &p, 10);
}
else if (!mem && strncmp(p, "Cached:", 7) == 0)
{
p = skip_token(p);
memory_stats[MEMCACHED] = strtoul(p, &p, 10);
}
else if (!swap && strncmp(p, "SwapTotal:", 10) == 0)
{
p = skip_token(p);
swaptotal = strtoul(p, &p, 10);
}
else if (!swap && swaptotal > 0 && strncmp(p, "SwapFree:", 9) == 0)
{
p = skip_token(p);
memfree = strtoul(p, &p, 10);
swap_stats[SWAPUSED] = swaptotal - memfree;
swap_stats[SWAPFREE] = memfree;
}
else if (!mem && strncmp(p, "SwapCached:", 11) == 0)
{
p = skip_token(p);
swap_stats[SWAPCACHED] = strtoul(p, &p, 10);
}
/* move to the next line */
p = strchr(p, '\n');
}
}
close(fd);
}
/* get vm related stuff */
if ((fd = open("vmstat", O_RDONLY)) != -1)
{
char *p;
if ((len = read(fd, buffer, sizeof(buffer)-1)) > 0)
{
buffer[len] = '\0';
p = buffer;
/* iterate thru the lines */
while (p != NULL)
{
if (strncmp(p, "pgmajfault ", 11) == 0)
{
p = skip_token(p);
flt = strtoul(p, &p, 10);
kernel_stats[KERNELFLT] = per_second(flt - last_flt, elapsed_msecs);
last_flt = flt;
break;
}
/* move to the next line */
p = strchr(p, '\n');
p++;
}
}
close(fd);
}
/* set arrays and strings */
info->cpustates = cpu_states;
info->memory = memory_stats;
info->swap = swap_stats;
info->kernel = kernel_stats;
}
static void
read_one_proc_stat(pid_t pid, pid_t taskpid, struct top_proc *proc, struct process_select *sel)
{
char buffer[4096], *p, *q;
int fd, len;
int fullcmd;
dprintf("reading proc %d - %d\n", pid, taskpid);
/* if anything goes wrong, we return with proc->state == 0 */
proc->state = 0;
/* full cmd handling */
fullcmd = sel->fullcmd;
if (fullcmd)
{
if (taskpid == -1)
{
sprintf(buffer, "%d/cmdline", pid);
}
else
{
sprintf(buffer, "%d/task/%d/cmdline", pid, taskpid);
}
if ((fd = open(buffer, O_RDONLY)) != -1)
{
/* read command line data */
/* (theres no sense in reading more than we can fit) */
if ((len = read(fd, buffer, MAX_COLS)) > 1)
{
buffer[len] = '\0';
xfrm_cmdline(buffer, len);
update_procname(proc, buffer);
}
else
{
fullcmd = 0;
}
close(fd);
}
else
{
fullcmd = 0;
}
}
/* grab the shared memory size */
sprintf(buffer, "%d/statm", pid);
fd = open(buffer, O_RDONLY);
len = read(fd, buffer, sizeof(buffer)-1);
close(fd);
buffer[len] = '\0';
p = buffer;
p = skip_token(p); /* skip size */
p = skip_token(p); /* skip resident */
proc->shared = pagetok(strtoul(p, &p, 10));
/* grab the proc stat info in one go */
if (taskpid == -1)
{
sprintf(buffer, "%d/stat", pid);
}
else
{
sprintf(buffer, "%d/task/%d/stat", pid, taskpid);
}
fd = open(buffer, O_RDONLY);
len = read(fd, buffer, sizeof(buffer)-1);
close(fd);
buffer[len] = '\0';
proc->uid = (uid_t)proc_owner((int)pid);
/* parse out the status */
/* skip pid and locate command, which is in parentheses */
if ((p = strchr(buffer, '(')) == NULL)
{
return;
}
if ((q = strrchr(++p, ')')) == NULL)
{
return;
}
/* set the procname */
*q = '\0';
if (!fullcmd)
{
update_procname(proc, p);
}
/* scan the rest of the line */
p = q+1;
p = skip_ws(p);
switch (*p++) /* state */
{
case 'R': proc->state = 1; break;
case 'S': proc->state = 2; break;
case 'D': proc->state = 3; break;
case 'Z': proc->state = 4; break;
case 'T': proc->state = 5; break;
case 'W': proc->state = 6; break;
case '\0': return;
}
p = skip_token(p); /* skip ppid */
p = skip_token(p); /* skip pgrp */
p = skip_token(p); /* skip session */
p = skip_token(p); /* skip tty */
p = skip_token(p); /* skip tty pgrp */
p = skip_token(p); /* skip flags */
p = skip_token(p); /* skip min flt */
p = skip_token(p); /* skip cmin flt */
p = skip_token(p); /* skip maj flt */
p = skip_token(p); /* skip cmaj flt */
proc->time = strtoul(p, &p, 10); /* utime */
proc->time += strtoul(p, &p, 10); /* stime */
p = skip_token(p); /* skip cutime */
p = skip_token(p); /* skip cstime */
proc->pri = strtol(p, &p, 10); /* priority */
proc->nice = strtol(p, &p, 10); /* nice */
proc->threads = strtol(p, &p, 10); /* threads */
p = skip_token(p); /* skip it_real_val */
proc->start_time = strtoul(p, &p, 10); /* start_time */
proc->size = bytetok(strtoul(p, &p, 10)); /* vsize */
proc->rss = pagetok(strtoul(p, &p, 10)); /* rss */
#if 0
/* for the record, here are the rest of the fields */
p = skip_token(p); /* skip rlim */
p = skip_token(p); /* skip start_code */
p = skip_token(p); /* skip end_code */
p = skip_token(p); /* skip start_stack */
p = skip_token(p); /* skip sp */
p = skip_token(p); /* skip pc */
p = skip_token(p); /* skip signal */
p = skip_token(p); /* skip sigblocked */
p = skip_token(p); /* skip sigignore */
p = skip_token(p); /* skip sigcatch */
p = skip_token(p); /* skip wchan */
#endif
}
static int show_usernames;
static int show_threads;
caddr_t
get_process_info(struct system_info *si,
struct process_select *sel,
int compare_index)
{
struct top_proc *proc;
struct top_proc *taskproc;
pid_t pid;
pid_t taskpid;
unsigned long now;
unsigned long elapsed;
hash_item_pid *hi;
hash_pos pos;
/* round current time to a second */
now = (unsigned long)lasttime.tv_sec;
if (lasttime.tv_usec >= 500000)
{
now++;
}
/* calculate number of ticks since our last check */
elapsed = timediff.tv_sec * HZ + (timediff.tv_usec * HZ) / 1000000;
if (elapsed <= 0)
{
elapsed = 1;
}
dprintf("get_process_info: elapsed %d ticks\n", elapsed);
/* mark all hash table entries as not seen */
hi = hash_first_pid(ptable, &pos);
while (hi != NULL)
{
((struct top_proc *)(hi->value))->state = 0;
hi = hash_next_pid(&pos);
}
/* mark all hash table entries as not seen */
hi = hash_first_pid(tasktable, &pos);
while (hi != NULL)
{
((struct top_proc *)(hi->value))->state = 0;
hi = hash_next_pid(&pos);
}
/* read the process information */
{
DIR *dir = opendir(".");
DIR *taskdir;
struct dirent *ent;
struct dirent *taskent;
int total_procs = 0;
struct top_proc **active;
hash_item_pid *hi;
hash_pos pos;
char buffer[64];
int show_idle = sel->idle;
int show_uid = sel->uid != -1;
char *show_command = sel->command;
show_usernames = sel->usernames;
show_threads = sel->threads && have_task;
memset(process_states, 0, sizeof(process_states));
taskdir = NULL;
taskent = NULL;
taskpid = -1;
while ((ent = readdir(dir)) != NULL)
{
unsigned long otime;
if (!isdigit(ent->d_name[0]))
continue;
pid = atoi(ent->d_name);
/* look up hash table entry */
proc = hash_lookup_pid(ptable, pid);
/* if we came up empty, create a new entry */
if (proc == NULL)
{
proc = new_proc();
proc->pid = pid;
proc->time = 0;
hash_add_pid(ptable, pid, (void *)proc);
}
/* remember the previous cpu time */
otime = proc->time;
/* get current data */
read_one_proc_stat(pid, -1, proc, sel);
/* continue on if this isn't really a process */
if (proc->state == 0)
continue;
/* reset linked list (for threads) */
proc->next = NULL;
/* accumulate process state data */
total_procs++;
process_states[proc->state]++;
/* calculate pcpu */
if ((proc->pcpu = (proc->time - otime) / (double)elapsed) < 0.0001)
{
proc->pcpu = 0;
}
/* if we have task subdirs and this process has more than
one thread, collect data on each thread */
if (have_task && proc->threads > 1)
{
snprintf(buffer, sizeof(buffer), "%d/task", pid);
if ((taskdir = opendir(buffer)) != NULL)
{
while ((taskent = readdir(taskdir)) != NULL)
{
if (!isdigit(taskent->d_name[0]))
continue;
/* lookup entry in tasktable */
taskpid = atoi(taskent->d_name);
taskproc = hash_lookup_pid(tasktable, taskpid);
/* if we came up empty, create a new entry */
if (taskproc == NULL)
{
taskproc = new_proc();
taskproc->pid = taskpid;
taskproc->time = 0;
hash_add_pid(tasktable, taskpid, (void *)taskproc);
}
/* remember the previous cpu time */
otime = taskproc->time;
/* get current data */
read_one_proc_stat(pid, taskpid, taskproc, sel);
/* ignore if it isnt real */
if (taskproc->state == 0)
continue;
/* when showing threads, add this to the accumulated
process state data, but remember that the first
thread is already accounted for */
if (show_threads && pid != taskpid)
{
total_procs++;
process_states[taskproc->state]++;
}
/* calculate pcpu */
if ((taskproc->pcpu = (taskproc->time - otime) /
(double)elapsed) < 0.0)
{
taskproc->pcpu = 0;
}
/* link this in to the proc's list */
taskproc->next = proc->next;
proc->next = taskproc;
}
closedir(taskdir);
}
}
}
closedir(dir);
/* make sure we have enough slots for the active procs */
if (activesize < total_procs)
{
pactive = (struct top_proc **)realloc(pactive,
sizeof(struct top_proc *) * total_procs);
activesize = total_procs;
}
/* set up the active procs and flush dead entries */
active = pactive;
hi = hash_first_pid(ptable, &pos);
while (hi != NULL)
{
proc = (struct top_proc *)(hi->value);
if (proc->state == 0)
{
/* dead entry */
hash_remove_pos_pid(&pos);
free_proc(proc);
}
else
{
/* check to see if it qualifies as active */
if ((show_idle || proc->state == 1 || proc->pcpu) &&
(!show_uid || proc->uid == sel->uid) &&
(show_command == NULL ||
strstr(proc->name, show_command) != NULL))
{
/* are we showing threads and does this proc have any? */
if (show_threads && proc->threads > 1 && proc->next != NULL)
{
/* then add just the thread info -- the main process
info is included in the list */
proc = proc->next;
while (proc != NULL)
{
*active++ = proc;
proc = proc->next;
}
}
else
{
/* add the process */
*active++ = proc;
}
}
}
hi = hash_next_pid(&pos);
}
si->p_active = active - pactive;
si->p_total = total_procs;
si->procstates = process_states;
}
/* if requested, sort the "active" procs */
if (si->p_active)
qsort(pactive, si->p_active, sizeof(struct top_proc *),
proc_compares[compare_index]);
/* don't even pretend that the return value thing here isn't bogus */
nextactive = pactive;
return (caddr_t)0;
}
char *
format_header(char *uname_field)
{
int uname_len = strlen(uname_field);
if (uname_len > 8)
uname_len = 8;
memcpy(strchr(fmt_header, 'X'), uname_field, uname_len);
return fmt_header;
}
static char p_header[MAX_COLS];
char *
format_process_header(struct process_select *sel, caddr_t handle, int count)
{
char *h;
h = sel->threads ? proc_header_nothr : proc_header_thr;
snprintf(p_header, MAX_COLS, h, sel->usernames ? "USERNAME" : "UID");
return p_header;
}
char *
format_next_process(caddr_t handle, char *(*get_userid)(int))
{
static char fmt[MAX_COLS]; /* static area where result is built */
struct top_proc *p = *nextactive++;
char *userbuf;
userbuf = show_usernames ? username(p->uid) : itoa_w(p->uid, 7);
if (show_threads)
{
snprintf(fmt, sizeof(fmt),
"%5d %-8.8s %3d %4d %5s %5s %5s %-5s %6s %5s%% %s",
p->pid,
userbuf,
p->pri < -99 ? -99 : p->pri,
p->nice,
format_k(p->size),
format_k(p->rss),
format_k(p->shared),
state_abbrev[p->state],
format_time(p->time / HZ),
format_percent(p->pcpu * 100.0),
p->name);
}
else
{
snprintf(fmt, sizeof(fmt),
"%5d %-8.8s %4d %3d %4d %5s %5s %5s %-5s %6s %5s%% %s",
p->pid,
userbuf,
p->threads <= 9999 ? p->threads : 9999,
p->pri < -99 ? -99 : p->pri,
p->nice,
format_k(p->size),
format_k(p->rss),
format_k(p->shared),
state_abbrev[p->state],
format_time(p->time / HZ),
format_percent(p->pcpu * 100.0),
p->name);
}
/* return the result */
return (fmt);
}
/* comparison routines for qsort */
/*
* There are currently four possible comparison routines. main selects
* one of these by indexing in to the array proc_compares.
*
* Possible keys are defined as macros below. Currently these keys are
* defined: percent cpu, cpu ticks, process state, resident set size,
* total virtual memory usage. The process states are ordered as follows
* (from least to most important): WAIT, zombie, sleep, stop, start, run.
* The array declaration below maps a process state index into a number
* that reflects this ordering.
*/
/* First, the possible comparison keys. These are defined in such a way
that they can be merely listed in the source code to define the actual
desired ordering.
*/
#define ORDERKEY_PCTCPU if (dresult = p2->pcpu - p1->pcpu,\
(result = dresult > 0.0 ? 1 : dresult < 0.0 ? -1 : 0) == 0)
#define ORDERKEY_CPTICKS if ((result = (long)p2->time - (long)p1->time) == 0)
#define ORDERKEY_STATE if ((result = (sort_state[p2->state] - \
sort_state[p1->state])) == 0)
#define ORDERKEY_PRIO if ((result = p2->pri - p1->pri) == 0)
#define ORDERKEY_RSSIZE if ((result = p2->rss - p1->rss) == 0)
#define ORDERKEY_MEM if ((result = p2->size - p1->size) == 0)
#define ORDERKEY_NAME if ((result = strcmp(p1->name, p2->name)) == 0)
/* Now the array that maps process state to a weight */
unsigned char sort_state[] =
{
0, /* empty */
6, /* run */
3, /* sleep */
5, /* disk wait */
1, /* zombie */
2, /* stop */
4 /* swap */
};
/* compare_cpu - the comparison function for sorting by cpu percentage */
int
compare_cpu (
struct top_proc **pp1,
struct top_proc **pp2)
{
register struct top_proc *p1;
register struct top_proc *p2;
register long result;
double dresult;
/* remove one level of indirection */
p1 = *pp1;
p2 = *pp2;
ORDERKEY_PCTCPU
ORDERKEY_CPTICKS
ORDERKEY_STATE
ORDERKEY_PRIO
ORDERKEY_RSSIZE
ORDERKEY_MEM
;
return result == 0 ? 0 : result < 0 ? -1 : 1;
}
/* compare_size - the comparison function for sorting by total memory usage */
int
compare_size (
struct top_proc **pp1,
struct top_proc **pp2)
{
register struct top_proc *p1;
register struct top_proc *p2;
register long result;
double dresult;
/* remove one level of indirection */
p1 = *pp1;
p2 = *pp2;
ORDERKEY_MEM
ORDERKEY_RSSIZE
ORDERKEY_PCTCPU
ORDERKEY_CPTICKS
ORDERKEY_STATE
ORDERKEY_PRIO
;
return result == 0 ? 0 : result < 0 ? -1 : 1;
}
/* compare_res - the comparison function for sorting by resident set size */
int
compare_res (
struct top_proc **pp1,
struct top_proc **pp2)
{
register struct top_proc *p1;
register struct top_proc *p2;
register long result;
double dresult;
/* remove one level of indirection */
p1 = *pp1;
p2 = *pp2;
ORDERKEY_RSSIZE
ORDERKEY_MEM
ORDERKEY_PCTCPU
ORDERKEY_CPTICKS
ORDERKEY_STATE
ORDERKEY_PRIO
;
return result == 0 ? 0 : result < 0 ? -1 : 1;
}
/* compare_time - the comparison function for sorting by total cpu time */
int
compare_time (
struct top_proc **pp1,
struct top_proc **pp2)
{
register struct top_proc *p1;
register struct top_proc *p2;
register long result;
double dresult;
/* remove one level of indirection */
p1 = *pp1;
p2 = *pp2;
ORDERKEY_CPTICKS
ORDERKEY_PCTCPU
ORDERKEY_STATE
ORDERKEY_PRIO
ORDERKEY_MEM
ORDERKEY_RSSIZE
;
return result == 0 ? 0 : result < 0 ? -1 : 1;
}
/* compare_cmd - the comparison function for sorting by command name */
int
compare_cmd (
struct top_proc **pp1,
struct top_proc **pp2)
{
register struct top_proc *p1;
register struct top_proc *p2;
register long result;
double dresult;
/* remove one level of indirection */
p1 = *pp1;
p2 = *pp2;
ORDERKEY_NAME
ORDERKEY_PCTCPU
ORDERKEY_CPTICKS
ORDERKEY_STATE
ORDERKEY_PRIO
ORDERKEY_RSSIZE
ORDERKEY_MEM
;
return result == 0 ? 0 : result < 0 ? -1 : 1;
}
/*
* proc_owner(pid) - returns the uid that owns process "pid", or -1 if
* the process does not exist.
* It is EXTREMLY IMPORTANT that this function work correctly.
* If top runs setuid root (as in SVR4), then this function
* is the only thing that stands in the way of a serious
* security problem. It validates requests for the "kill"
* and "renice" commands.
*/
int
proc_owner(int pid)
{
struct stat sb;
char buffer[32];
sprintf(buffer, "%d", pid);
if (stat(buffer, &sb) < 0)
return -1;
else
return (int)sb.st_uid;
}