/*
* 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: Any SGI machine running IRIX 6.2 and up
*
* DESCRIPTION:
* This is the machine-dependent module for IRIX as supplied by
* engineers at SGI.
*
* CFLAGS: -DHAVE_GETOPT -D_OLD_TERMIOS -DORDER
*
* AUTHOR: Sandeep Cariapa <cariapa@sgi.com>
* AUTHOR: Larry McVoy <lm@sgi.com>
* Sandeep did all the hard work; I ported to 6.2 and fixed up some formats.
* AUTHOR: John Schimmel <jes@sgi.com>
* He did the all irix merge.
* AUTHOR: Ariel Faigon <ariel@sgi.com>
* Ported to Ficus/Kudzu (IRIX 6.4+).
* Got rid of all nlist and different (elf64, elf32, COFF) kernel
* dependencies
* Various small fixes and enhancements: multiple CPUs, nicer formats.
* Added -DORDER process display ordering
* cleaned most -fullwarn'ings.
* Need -D_OLD_TERMIOS when compiling on IRIX 6.4 to work on 6.2 systems
* Support much bigger values in memory sizes (over Peta-byte)
* AUTHOR: William LeFebvre
* Converted to ANSI C and updated to new module interface
*/
#define _KMEMUSER
#include "config.h"
#include <sys/types.h>
#include <sys/time.h>
#include <sys/stat.h>
#include <sys/swap.h>
#include <sys/proc.h>
#include <sys/procfs.h>
#include <sys/sysinfo.h>
#include <sys/sysmp.h>
#include <sys/utsname.h>
#include <sys/schedctl.h> /* for < 6.4 NDPHIMAX et al. */
#include <paths.h>
#include <assert.h>
#include <values.h>
#include <dirent.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <errno.h>
#include <fcntl.h>
#include <dlfcn.h>
#include "top.h"
#include "machine.h"
#include "utils.h"
#define KMEM "/dev/kmem"
typedef double load_avg;
#define loaddouble(la) (la)
#define intload(i) ((double)(i))
/*
* Structure for keeping track of CPU times from last time around
* the program. We keep these things in a hash table, which is
* recreated at every cycle.
*/
struct oldproc {
pid_t oldpid;
double oldtime;
double oldpct;
};
static int oldprocs; /* size of table */
static struct oldproc *oldbase;
#define HASH(x) ((x << 1) % oldprocs)
#define pagetok(pages) ((((uint64_t) pages) * pagesize) >> 10)
/*
* Ugly hack, save space and complexity of allocating and maintaining
* parallel arrays to the prpsinfo array: use spare space (pr_fill area)
* in prpsinfo structures to store %CPU calculated values
*/
#define D_align(addr) (((unsigned long)(addr) & ~0x0fU))
#define percent_cpu(pp) (* (double *) D_align(&((pp)->pr_fill[0])))
#define weighted_cpu(pp) (* (double *) D_align(&((pp)->pr_fill[4])))
/* Username field to fill in starts at: */
#define UNAME_START 16
/*
* These definitions control the format of the per-process area
*/
static char header[] =
" PID PGRP X PRI SIZE RES STATE TIME %WCPU %CPU COMMAND";
/*
012345678901234567890123456789012345678901234567890123456789012345678901234567
10 20 30 40 50 60 70
*/
/* PID PGRP USER PRI SIZE RES STATE TIME %WCPU %CPU CMD */
#define Proc_format \
"%7d %7d %-8.8s %4.4s %6.6s %5.5s %-6.6s %6.6s %5.2f %5.2f %-.10s"
/*
* these are for detailing the cpu states
* Data is taken from the sysinfo structure (see <sys/sysinfo.h>)
* We rely on the following values:
*
* #define CPU_IDLE 0
* #define CPU_USER 1
* #define CPU_KERNEL 2
* #define CPU_WAIT 3
* #define CPU_SXBRK 4
* #define CPU_INTR 5
*/
#ifndef CPU_STATES /* defined only in 6.4 and up */
# define CPU_STATES 6
#endif
int cpu_states[CPU_STATES];
char *cpustatenames[] = {
"idle", "usr", "ker", "wait", "xbrk", "intr",
NULL
};
/* these are for detailing the memory statistics */
#define MEMSTATS 10
int memory_stats[MEMSTATS];
char *memorynames[] = {
"K max, ", "K avail, ", "K free, ", "K swap, ", "K free swap", NULL
};
char uname_str[40];
double load[3];
static char fmt[MAX_COLS + 2];
int numcpus;
/* useful externals */
extern int errno;
extern char *sys_errlist[];
extern char *myname;
extern char *format_k();
extern char *format_time();
extern long percentages();
static int kmem;
static unsigned long avenrun_offset;
static float irix_ver; /* for easy numeric comparison */
static struct prpsinfo *pbase;
static struct prpsinfo **pref;
static struct oldproc *oldbase;
static int oldprocs; /* size of table */
static DIR *procdir;
static int ptable_size; /* allocated process table size */
static int nproc; /* estimated process table size */
static int pagesize;
/* get_process_info passes back a handle. This is what it looks like: */
struct handle {
struct prpsinfo **next_proc; /* points to next valid proc pointer */
int remaining; /* number of pointers remaining */
};
static struct handle handle;
void getptable(struct prpsinfo *baseptr);
void size(int fd, struct prpsinfo *ps);
extern char *ordernames[];
/*
* Process states letters are mapped into numbers
* 6.5 seems to have changed the semantics of prpsinfo.pr_state
* so we rely, (like ps does) on the char value pr_sname.
* The order we use here is what may be most interesting
* to top users: Most interesting state on top, least on bottom.
* 'S' (sleeping) is the most common case so I put it _after_
* zombie, even though it is more "active" than zombie.
*
* State letters and their meanings:
*
* R Process is running (may not have a processor yet)
* I Process is in intermediate state of creation
* X Process is waiting for memory
* T Process is stopped
* Z Process is terminated and parent not waiting (zombie)
* S Process is sleeping, waiting for a resource
*/
/* abbreviated process states */
static char *state_abbrev[] =
{ "", "sleep", "zomb", "stop", "swap", "start", "ready", "run", NULL };
/* Same but a little "wordier", used in CPU activity summary */
int process_states[8]; /* per state counters */
char *procstatenames[] = {
/* ready to run is considered running here */
"", " sleeping, ", " zombie, ", " stopped, ",
" swapped, ", " starting, ", " ready, ", " running, ",
NULL
};
#define S_RUNNING 7
#define S_READY 6
#define S_STARTING 5
#define S_SWAPPED 4
#define S_STOPPED 3
#define S_ZOMBIE 2
#define S_SLEEPING 1
#define IS_ACTIVE(pp) \
(first_screen ? proc_state(pp) >= S_STARTING : percent_cpu(pp) > 0.0)
/*
* proc_state
* map the pr_sname value to an integer.
* used as an index into state_abbrev[]
* as well as an "order" key
*/
static int proc_state(struct prpsinfo *pp)
{
char psname = pp->pr_sname;
switch (psname) {
case 'R': return
(pp->pr_sonproc >= 0 && pp->pr_sonproc < numcpus) ?
S_RUNNING /* on a processor */ : S_READY;
case 'I': return S_STARTING;
case 'X': return S_SWAPPED;
case 'T': return S_STOPPED;
case 'Z': return S_ZOMBIE;
case 'S': return S_SLEEPING;
default : return 0;
}
}
/*
* To avoid nlist'ing the kernel (with all the different kernel type
* complexities), we estimate the size of the needed working process
* table by scanning /proc/pinfo and taking the number of entries
* multiplied by some reasonable factor.
* Assume current dir is _PATH_PROCFSPI
*/
static int active_proc_count()
{
DIR *dirp;
int pcnt;
if ((dirp = opendir(".")) == NULL) {
(void) fprintf(stderr, "%s: Unable to open %s\n",
myname, _PATH_PROCFSPI);
exit(1);
}
for (pcnt = 0; readdir(dirp) != NULL; pcnt++)
;
closedir(dirp);
return pcnt;
}
/*
* allocate space for:
* proc structure array
* array of pointers to the above (used for sorting)
* array for storing per-process old CPU usage
*/
void
allocate_proc_tables()
{
int n_active = active_proc_count();
if (pbase != NULL) /* && n_active < ptable_size */
return;
/* Need to realloc if we exceed, but factor should be enough */
nproc = n_active * 5;
oldprocs = 2 * nproc;
pbase = (struct prpsinfo *)
malloc(nproc * sizeof(struct prpsinfo));
pref = (struct prpsinfo **)
malloc(nproc * sizeof(struct prpsinfo *));
oldbase = (struct oldproc *)
malloc (oldprocs * sizeof(struct oldproc));
ptable_size = nproc;
if (pbase == NULL || pref == NULL || oldbase == NULL) {
(void) fprintf(stderr, "%s: malloc: out of memory\n", myname);
exit (1);
}
}
int
machine_init(struct statics *statics)
{
struct oldproc *op, *endbase;
int pcnt = 0;
struct utsname utsname;
char tmpbuf[20];
uname(&utsname);
irix_ver = (float) atof((const char *)utsname.release);
strncpy(tmpbuf, utsname.release, 9);
tmpbuf[9] = '\0';
sprintf(uname_str, "%s %-.14s %s %s",
utsname.sysname, utsname.nodename,
tmpbuf, utsname.machine);
pagesize = getpagesize();
if ((kmem = open(KMEM, O_RDONLY)) == -1) {
perror(KMEM);
return -1;
}
if (chdir(_PATH_PROCFSPI)) {
/* handy for later on when we're reading it */
(void) fprintf(stderr, "%s: Unable to chdir to %s\n",
myname, _PATH_PROCFSPI);
return -1;
}
if ((procdir = opendir(".")) == NULL) {
(void) fprintf(stderr, "%s: Unable to open %s\n",
myname, _PATH_PROCFSPI);
return -1;
}
if ((avenrun_offset = sysmp(MP_KERNADDR, MPKA_AVENRUN)) == -1) {
perror("sysmp(MP_KERNADDR, MPKA_AVENRUN)");
return -1;
}
allocate_proc_tables();
oldprocs = 2 * nproc;
endbase = oldbase + oldprocs;
for (op = oldbase; op < endbase; op++) {
op->oldpid = -1;
}
statics->cpustate_names = cpustatenames;
statics->memory_names = memorynames;
statics->order_names = ordernames;
statics->procstate_names = procstatenames;
return (0);
}
char *
format_header(register char *uname_field)
{
register char *ptr;
ptr = header + UNAME_START;
while (*uname_field != '\0') {
*ptr++ = *uname_field++;
}
return (header);
}
void
get_system_info(struct system_info *si)
{
int i;
int avenrun[3];
struct rminfo realmem;
struct sysinfo sysinfo;
static time_t cp_old [CPU_STATES];
static time_t cp_diff[CPU_STATES]; /* for cpu state percentages */
off_t fswap; /* current free swap in blocks */
off_t tswap; /* total swap in blocks */
(void) getkval(avenrun_offset, (int *) avenrun, sizeof(avenrun), "avenrun");
for (i = 0; i < 3; i++) {
si->load_avg[i] = loaddouble(avenrun[i]);
si->load_avg[i] /= 1024.0;
}
if ((numcpus = sysmp(MP_NPROCS)) == -1) {
perror("sysmp(MP_NPROCS)");
return;
}
if (sysmp(MP_SAGET, MPSA_RMINFO, &realmem, sizeof(realmem)) == -1) {
perror("sysmp(MP_SAGET,MPSA_RMINFO, ...)");
return;
}
swapctl(SC_GETFREESWAP, &fswap);
swapctl(SC_GETSWAPTOT, &tswap);
memory_stats[0] = pagetok(realmem.physmem);
memory_stats[1] = pagetok(realmem.availrmem);
memory_stats[2] = pagetok(realmem.freemem);
memory_stats[3] = tswap / 2;
memory_stats[4] = fswap / 2;
if (sysmp(MP_SAGET,MPSA_SINFO, &sysinfo,sizeof(struct sysinfo)) == -1) {
perror("sysmp(MP_SAGET,MPSA_SINFO)");
return;
}
(void) percentages(CPU_STATES, cpu_states, sysinfo.cpu, cp_old, cp_diff);
si->cpustates = cpu_states;
si->memory = memory_stats;
si->last_pid = -1;
return;
}
caddr_t
get_process_info(struct system_info *si, struct process_select *sel, int compare_index)
{
int i, total_procs, active_procs;
struct prpsinfo **prefp;
struct prpsinfo *pp;
int show_uid;
static char first_screen = 1;
/* read all the proc structures */
getptable(pbase);
/* get a pointer to the states summary array */
si->procstates = process_states;
/* set up flags which define what we are going to select */
show_uid = sel->uid != -1;
/* count up process states and get pointers to interesting procs */
total_procs = 0;
active_procs = 0;
(void) memset(process_states, 0, sizeof(process_states));
prefp = pref;
for (pp = pbase, i = 0; i < nproc; pp++, i++) {
/*
* Place pointers to each valid proc structure in pref[].
* Process slots that are actually in use have a non-zero
* status field. Processes with SSYS set are system
* processes---these get ignored unless show_system is set.
* Ariel: IRIX 6.4 had to redefine "system processes"
* They do not exist outside the kernel in new kernels.
* Now defining as uid==0 and ppid==1 (init children)
*/
if (pp->pr_state &&
(sel->system || !(pp->pr_uid==0 && pp->pr_ppid==1))) {
total_procs++;
process_states[proc_state(pp)]++;
/*
* zombies are actually interesting (to avoid)
* although they are not active, so I leave them
* displayed.
*/
if (/* (! pp->pr_zomb) && */
(sel->idle || IS_ACTIVE(pp)) &&
(! show_uid || pp->pr_uid == (uid_t) sel->uid)) {
*prefp++ = pp;
active_procs++;
}
}
}
first_screen = 0;
/* if requested, sort the "interesting" processes */
qsort((char *) pref, active_procs, sizeof(struct prpsinfo *),
proc_compares[compare_index]);
/* remember active and total counts */
si->p_total = total_procs;
si->p_active = active_procs;
/* pass back a handle */
handle.next_proc = pref;
handle.remaining = active_procs;
return ((caddr_t) &handle);
}
/*
* Added cpu_id to running processes, add 'ready' (to run) state
*/
static char *
format_state(struct prpsinfo *pp)
{
static char state_str[16];
int state = proc_state(pp);
if (state == S_RUNNING) {
/*
* Alert: 6.2 (MP only?) binary incompatibility
* pp->pr_sonproc apparently (?) has a different
* offset on 6.2 machines... I've seen cases where
* a 6.4 compiled top running on 6.2 printed
* a garbage CPU-id. To be safe, I print the CPU-id
* only if it falls within range [0..numcpus-1]
*/
sprintf(state_str, "run/%d", pp->pr_sonproc);
return state_str;
}
/* default */
return state_abbrev[state];
}
static char *
format_prio(struct prpsinfo *pp)
{
static char prio_str[10];
if (irix_ver < 6.4) {
/*
* Note: this is _compiled_ on 6.x where x >= 4 but I would like
* it to run on 6.2 6.3 as well (backward binary compatibility).
* Scheduling is completely different between these IRIX versions
* and some scheduling classes may even have different names.
*
* The solution: have more than one style of 'priority' depending
* on the OS version.
*
* See npri(1) + nice(2) + realtime(5) for scheduling classes,
* and priority values.
*/
if (pp->pr_pri <= NDPHIMIN) /* real time? */
sprintf(prio_str, "+%d", pp->pr_pri);
else if (pp->pr_pri <= NDPNORMMIN) /* normal interactive */
sprintf(prio_str, "%d", pp->pr_pri);
else /* batch: low prio */
sprintf(prio_str, "b%d", pp->pr_pri);
} else {
/* copied from Kostadis's code */
if (strcmp(pp->pr_clname, "RT") == 0) /* real time */
sprintf(prio_str, "+%d", pp->pr_pri);
else if (strcmp(pp->pr_clname, "DL") == 0) /* unsupported ? */
sprintf(prio_str, "d%d", pp->pr_pri);
else if (strcmp(pp->pr_clname, "GN") == 0)
sprintf(prio_str, "g%d", pp->pr_pri);
else if (strcmp(pp->pr_clname, "GB") == 0)
sprintf(prio_str, "p%d", pp->pr_pri);
else if (strcmp(pp->pr_clname, "WL") == 0) /* weightless */
return "w";
else if (strcmp(pp->pr_clname, "BC") == 0)
return "bc"; /* batch critical */
else if (strcmp(pp->pr_clname, "B") == 0)
return "b"; /* batch */
else
sprintf(prio_str, "%d", pp->pr_pri);
}
return prio_str;
}
static double
clip_percent(double pct)
{
if (pct < 0) {
return 0.0;
} else if (pct >= 100) {
return 99.99;
}
return pct;
}
char *
format_next_process(caddr_t handle, char *(*get_userid)())
{
struct prpsinfo *pp;
struct handle *hp;
long cputime;
/* find and remember the next proc structure */
hp = (struct handle *) handle;
pp = *(hp->next_proc++);
hp->remaining--;
/* get the process cpu usage since startup */
cputime = pp->pr_time.tv_sec;
/* format this entry */
sprintf(fmt,
Proc_format,
pp->pr_pid,
pp->pr_pgrp,
(*get_userid) (pp->pr_uid),
format_prio(pp),
format_k(pagetok(pp->pr_size)),
format_k(pagetok(pp->pr_rssize)),
format_state(pp),
format_time(cputime),
clip_percent(weighted_cpu(pp)),
clip_percent(percent_cpu(pp)),
printable(pp->pr_fname));
/* return the result */
return (fmt);
}
/*
* getkval(offset, ptr, size, refstr) - get a value out of the kernel.
* "offset" is the byte offset into the kernel for the desired value,
* "ptr" points to a buffer into which the value is retrieved,
* "size" is the size of the buffer (and the object to retrieve),
* "refstr" is a reference string used when printing error meessages,
* if "refstr" starts with a '!', then a failure on read will not
* be fatal (this may seem like a silly way to do things, but I
* really didn't want the overhead of another argument).
*
*/
int
getkval(unsigned long offset, int *ptr, int size, char *refstr)
{
if (lseek(kmem, (long) offset, SEEK_SET) == -1) {
if (*refstr == '!')
refstr++;
(void) fprintf(stderr, "%s: %s: lseek to %s: %s\n",
myname, KMEM, refstr, strerror(errno));
exit(0);
}
if (read(kmem, (char *) ptr, size) == -1) {
if (*refstr == '!')
return (0);
else {
(void) fprintf(stderr, "%s: %s: reading %s: %s\n",
myname, KMEM, refstr, strerror(errno));
exit(0);
}
}
return (1);
}
/*
* compare_K - comparison functions for "qsort"
* Compares the resource consumption of two processes using five
* distinct keys. The keys are:
* percent cpu, cpu ticks, state, resident set size, total virtual
* memory usage. The process states are ordered as follows (from least
* to most important): WAIT, zombie, sleep, stop, idle, run.
* Different comparison functions are used for different orderings.
*/
/* these are names given to allowed sorting orders -- first is default */
char *ordernames[] = {
/*
* Aliases for user convenience/friendliness:
* mem == size
* rss == res
*/
"cpu", "size", "mem", "res", "rss",
"time", "state", "command", "prio", NULL
};
/* forward definitions for comparison functions */
int compare_cpu(struct prpsinfo **pp1, struct prpsinfo **pp2);
int compare_size(struct prpsinfo **pp1, struct prpsinfo **pp2);
int compare_res(struct prpsinfo **pp1, struct prpsinfo **pp2);
int compare_time(struct prpsinfo **pp1, struct prpsinfo **pp2);
int compare_state(struct prpsinfo **pp1, struct prpsinfo **pp2);
int compare_cmd(struct prpsinfo **pp1, struct prpsinfo **pp2);
int compare_prio(struct prpsinfo **pp1, struct prpsinfo **pp2);
int (*proc_compares[])() = {
compare_cpu,
compare_size,
compare_size,
compare_res,
compare_res,
compare_time,
compare_state,
compare_cmd,
compare_prio,
NULL
};
/*
* The possible comparison expressions. 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 = percent_cpu(p2) - percent_cpu(p1),\
(result = dresult > 0.0 ? 1 : dresult < 0.0 ? -1 : 0) == 0)
#define ORDERKEY_CPTICKS \
if ((result = p2->pr_time.tv_sec - p1->pr_time.tv_sec) == 0)
#define ORDERKEY_STATE if ((result = proc_state(p2) - proc_state(p1)) == 0)
#define ORDERKEY_PRIO if ((result = p2->pr_oldpri - p1->pr_oldpri) == 0)
#define ORDERKEY_RSSIZE if ((result = p2->pr_rssize - p1->pr_rssize) == 0)
#define ORDERKEY_MEM if ((result = (p2->pr_size - p1->pr_size)) == 0)
#define ORDERKEY_CMD if ((result = strcmp(p1->pr_fname,p2->pr_fname)) == 0)
int compare_cpu(struct prpsinfo **pp1, struct prpsinfo **pp2)
{
struct prpsinfo *p1, *p2;
int result;
double dresult;
/* remove one level of indirection */
p1 = *pp1;
p2 = *pp2;
/*
* order by various keys, resorting to the next one
* whenever there's a tie in comparisons
*/
ORDERKEY_PCTCPU
ORDERKEY_CPTICKS
ORDERKEY_STATE
ORDERKEY_PRIO
ORDERKEY_RSSIZE
ORDERKEY_MEM
;
return (result);
}
int compare_size(struct prpsinfo **pp1, struct prpsinfo **pp2)
{
struct prpsinfo *p1, *p2;
int result;
double dresult;
/* remove one level of indirection */
p1 = *pp1;
p2 = *pp2;
/*
* order by various keys, resorting to the next one
* whenever there's a tie in comparisons
*/
ORDERKEY_MEM
ORDERKEY_RSSIZE
ORDERKEY_PCTCPU
ORDERKEY_CPTICKS
ORDERKEY_STATE
ORDERKEY_PRIO
;
return (result);
}
int compare_res(struct prpsinfo **pp1, struct prpsinfo **pp2)
{
struct prpsinfo *p1, *p2;
int result;
double dresult;
/* remove one level of indirection */
p1 = *pp1;
p2 = *pp2;
/*
* order by various keys, resorting to the next one
* whenever there's a tie in comparisons
*/
ORDERKEY_RSSIZE
ORDERKEY_MEM
ORDERKEY_PCTCPU
ORDERKEY_CPTICKS
ORDERKEY_STATE
ORDERKEY_PRIO
;
return (result);
}
int compare_time(struct prpsinfo **pp1, struct prpsinfo **pp2)
{
struct prpsinfo *p1, *p2;
int result;
double dresult;
/* remove one level of indirection */
p1 = *pp1;
p2 = *pp2;
/*
* order by various keys, resorting to the next one
* whenever there's a tie in comparisons
*/
ORDERKEY_CPTICKS
ORDERKEY_RSSIZE
ORDERKEY_MEM
ORDERKEY_PCTCPU
ORDERKEY_STATE
ORDERKEY_PRIO
;
return (result);
}
int compare_cmd(struct prpsinfo **pp1, struct prpsinfo **pp2)
{
struct prpsinfo *p1, *p2;
int result;
double dresult;
/* remove one level of indirection */
p1 = *pp1;
p2 = *pp2;
/*
* order by various keys, resorting to the next one
* whenever there's a tie in comparisons
*/
ORDERKEY_CMD
ORDERKEY_PCTCPU
ORDERKEY_CPTICKS
ORDERKEY_RSSIZE
;
return (result);
}
int compare_state(struct prpsinfo **pp1, struct prpsinfo **pp2)
{
struct prpsinfo *p1, *p2;
int result;
double dresult;
/* remove one level of indirection */
p1 = *pp1;
p2 = *pp2;
/*
* order by various keys, resorting to the next one
* whenever there's a tie in comparisons
*/
ORDERKEY_STATE
ORDERKEY_PCTCPU
ORDERKEY_CPTICKS
ORDERKEY_RSSIZE
;
return (result);
}
int compare_prio(struct prpsinfo **pp1, struct prpsinfo **pp2)
{
struct prpsinfo *p1, *p2;
int result;
double dresult;
/* remove one level of indirection */
p1 = *pp1;
p2 = *pp2;
/*
* order by various keys, resorting to the next one
* whenever there's a tie in comparisons
*/
ORDERKEY_PRIO
ORDERKEY_PCTCPU
;
return (result);
}
/* return the owner of the specified process. */
uid_t
proc_owner(pid_t pid)
{
register struct prpsinfo *p;
int i;
for (i = 0, p = pbase; i < nproc; i++, p++)
if (p->pr_pid == pid)
return (p->pr_uid);
return (-1);
}
#ifdef DO_MAPSIZE
static void
size(int fd, struct prpsinfo *ps)
{
prmap_sgi_arg_t maparg;
struct prmap_sgi maps[256];
int nmaps;
double sz;
int i;
maparg.pr_vaddr = (caddr_t) maps;
maparg.pr_size = sizeof maps;
if ((nmaps = ioctl(fd, PIOCMAP_SGI, &maparg)) == -1) {
/* XXX - this will be confusing */
return;
}
for (i = 0, sz = 0; i < nmaps; ++i) {
sz += (double) maps[i].pr_wsize / MA_WSIZE_FRAC;
}
ps->pr_rssize = (long) sz;
}
#endif
/* get process table */
void
getptable(struct prpsinfo *baseptr)
{
struct prpsinfo *currproc; /* ptr to current proc struct */
int i, numprocs;
struct dirent *direntp;
struct oldproc *op, *endbase;
static struct timeval lasttime, thistime;
static double timediff, alpha, beta;
/* measure time between last call to getptable and current call */
gettimeofday (&thistime, NULL);
/*
* To avoid divides, we keep times in nanoseconds. This is
* scaled by 1e7 rather than 1e9 so that when we divide we
* get percent.
*/
timediff = ((double) thistime.tv_sec * 1.0e7 -
(double) lasttime.tv_sec * 1.0e7)
+
((double) thistime.tv_usec * 10 -
(double) lasttime.tv_usec * 10);
/*
* Under extreme load conditions, sca has experienced
* an assert(timediff > 0) failure here. His guess is that
* sometimes timed resets the time backwards and gettimeofday
* returns a lower number on a later call.
* To be on the safe side I fix it here by setting timediff
* to some arbitrary small value (in nanoseconds).
*/
if (timediff <= 0.0) timediff = 100.0;
lasttime = thistime; /* prepare for next round */
/*
* constants for exponential decaying average.
* avg = alpha * new + beta * avg
* The goal is 50% decay in 30 sec. However if the sample period
* is greater than 30 sec, there's not a lot we can do.
*/
if (timediff < 30.0e7) {
alpha = 0.5 * (timediff / 15.0e7);
beta = 1.0 - alpha;
} else {
alpha = 0.5;
beta = 0.5;
}
assert(alpha >= 0); assert(alpha <= 1);
assert(beta >= 0); assert(beta <= 1);
endbase = oldbase + oldprocs;
currproc = baseptr;
for (numprocs = 0, rewinddir(procdir); direntp = readdir(procdir);) {
int fd;
if ((fd = open(direntp->d_name, O_RDONLY)) < 0)
continue;
currproc = baseptr + numprocs;
if (ioctl(fd, PIOCPSINFO, currproc) < 0) {
(void) close(fd);
continue;
}
/*
* SVR4 doesn't keep track of CPU% in the kernel,
* so we have to do our own.
* See if we've heard of this process before.
* If so, compute % based on CPU since last time.
*/
op = oldbase + HASH (currproc->pr_pid);
for (;;) {
if (op->oldpid == -1) /* not there */
break;
if (op->oldpid == currproc->pr_pid) {
/* found old data */
percent_cpu(currproc) =
((currproc->pr_time.tv_sec * 1.0e9 +
currproc->pr_time.tv_nsec)
- op->oldtime) / timediff;
weighted_cpu(currproc) =
op->oldpct * beta +
percent_cpu(currproc) * alpha;
break;
}
op++; /* try next entry in hash table */
if (op == endbase) /* table wrap around */
op = oldbase;
}
/* Otherwise, it's new, so use all of its CPU time */
if (op->oldpid == -1) {
if (lasttime.tv_sec) {
percent_cpu(currproc) =
(currproc->pr_time.tv_sec * 1.0e9 +
currproc->pr_time.tv_nsec) / timediff;
weighted_cpu(currproc) = percent_cpu(currproc);
} else {
/* first screen -- no difference is possible */
percent_cpu(currproc) = 0.0;
weighted_cpu(currproc) = 0.0;
}
}
#ifdef DO_MAPSIZE
size(fd, currproc);
#endif
numprocs++;
(void) close(fd);
/*
* Bug: in case process count grew so dramatically
* as to exceed to table size. We give up on a full scan.
* the chances of this to happen are extremely slim due to
* the big factor we're using. getting nproc from nlist
* is not worth the headache. realloc wouldn't work either
* because we have pointers to the proc table so we cannot
* move it around.
*/
if (numprocs >= ptable_size) {
fprintf(stderr,
"preallocated proc table size (%d) exceeded, "
"skipping some processes\n", ptable_size);
break;
}
}
nproc = numprocs;
/*
* Save current CPU time for next time around
* For the moment recreate the hash table each time, as the code
* is easier that way.
*/
oldprocs = 2 * nproc;
endbase = oldbase + oldprocs;
for (op = oldbase; op < endbase; op++)
op->oldpid = -1;
for (i = 0, currproc = baseptr; i < nproc; i++, currproc++) {
/* find an empty spot */
op = oldbase + HASH (currproc->pr_pid);
for (;;) {
if (op->oldpid == -1)
break;
op++;
if (op == endbase)
op = oldbase;
}
op->oldpid = currproc->pr_pid;
op->oldtime = (currproc->pr_time.tv_sec * 1.0e9 +
currproc->pr_time.tv_nsec);
op->oldpct = weighted_cpu(currproc);
}
}