This is m_umax.c in view mode; [Download] [Up]
/*
* top - a top users display for Unix
*
* SYNOPSIS: Encore Multimax running any release of UMAX 4.3
*
* DESCRIPTION:
* This module makes top work on the following systems:
* Encore Multimax running UMAX 4.3 release 4.0 and later
*
* AUTHOR: William LeFebvre <phil@eecs.nwu.edu>
*/
/*
* The winner of the "wow what a hack" award:
* We don't really need the proc structure out of sys/proc.h, but we do
* need many of the #defines. So, we define a bogus "queue" structure
* so that we don't have to include that mess of stuff in machine/*.h
* just so that the proc struct will get defined cleanly.
*/
struct queue { int x };
#include <stdio.h>
#include <sys/types.h>
#include <sys/param.h>
#include <sys/time.h>
#include <sys/resource.h>
#include <sys/proc.h>
#include <machine/cpu.h>
#include <inq_stats/statistics.h>
#include <inq_stats/cpustats.h>
#include <inq_stats/procstats.h>
#include <inq_stats/vmstats.h>
#include "top.h"
#include "display.h"
#include "machine.h"
#include "utils.h"
struct handle
{
struct proc **next_proc; /* points to next valid proc pointer */
int remaining; /* number of pointers remaining */
};
/* Log base 2 of 1024 is 10 (2^10 == 1024) */
#define LOG1024 10
/* Convert clicks (kernel pages) to kbytes ... */
#if PGSHIFT>10
#define pagetok(size) ((size) << (PGSHIFT - LOG1024))
#else
#define pagetok(size) ((size) >> (LOG1024 - PGSHIFT))
#endif
/* what we consider to be process size: */
#define PROCSIZE(pp) ((pp)->pd_tsize + (pp)->pd_dsize + (pp)->pd_ssize)
/* the ps_nrun array index is incremented every 12th of a minute */
#define MINUTES(x) ((x) * 12)
/* convert a tv structure (seconds, microseconds) to a double */
#define TVTODOUBLE(tv) ((double)(tv).tv_sec + ((double)(tv).tv_usec / 1000000))
/*
* These definitions control the format of the per-process area
*/
static char header[] =
" PID X PRI NICE SIZE RES STATE TIME %CPU COMMAND";
/* 0123456 -- field to fill in starts at header+6 */
#define UNAME_START 6
#define Proc_format \
"%5d %-8.8s %3d %4d %5s %5s %-5s %6s %6.2f%% %s"
/* process state names for the "STATE" column of the display */
char *state_abbrev[] =
{
"", "", "wait", "run", "start", "stop", "exec", "event"
};
/* these are for detailing the process states */
int process_states[5];
char *procstatenames[] = {
" waiting, ",
#define P_SLEEP 0
" running, ",
#define P_RUN 1
" zombie, ",
#define P_ZOMBIE 2
" stopped, ",
#define P_STOP 3
" free slots",
#define P_FREE 4
NULL
};
/* these are for detailing the cpu states */
int cpu_states[4];
char *cpustatenames[] = {
"user", "nice", "system", "idle", NULL
};
/* these are for detailing the memory statistics */
int memory_stats[4];
char *memorynames[] = {
"K available, ", "K free, ", "K locked, ", "K virtual", NULL
};
/* these detail per-process information */
static int nprocs;
static int pref_len;
static struct proc_detail *pd;
static struct proc_detail **pref;
/* inq_stats structures and the STAT_DESCRs that use them */
static struct proc_config stat_pc;
static struct vm_config stat_vm;
static struct class_stats stat_class;
static struct proc_summary stat_ps;
static struct cpu_stats stat_cpu;
static struct stat_descr sd_procconfig = {
NULL, /* sd_next */
SUBSYS_PROC, /* sd_subsys */
PROCTYPE_CONFIG, /* sd_type */
0, /* sd_options */
0, /* sd_objid */
&stat_pc, /* sd_addr */
sizeof(stat_pc), /* sd_size */
0, /* sd_status */
0, /* sd_sizeused */
0 /* sd_time */
};
static struct stat_descr sd_memory = {
NULL, /* sd_next */
SUBSYS_VM, /* sd_subsys */
VMTYPE_SYSTEM, /* sd_type */
0, /* sd_options */
0, /* sd_objid */
&stat_vm, /* sd_addr */
sizeof(stat_vm), /* sd_size */
0, /* sd_status */
0, /* sd_sizeused */
0 /* sd_time */
};
static struct stat_descr sd_class = {
NULL, /* sd_next */
SUBSYS_CPU, /* sd_subsys */
CPUTYPE_CLASS, /* sd_type */
0, /* sd_options */
UMAXCLASS, /* sd_objid */
&stat_class, /* sd_addr */
sizeof(stat_class), /* sd_size */
0, /* sd_status */
0, /* sd_sizeused */
0 /* sd_time */
};
static struct stat_descr sd_procsummary = {
NULL, /* sd_next */
SUBSYS_PROC, /* sd_subsys */
PROCTYPE_SUMMARY, /* sd_type */
0, /* sd_options */
0, /* sd_objid */
&stat_ps, /* sd_addr */
sizeof(stat_ps), /* sd_size */
0, /* sd_status */
0, /* sd_sizeused */
0 /* sd_time */
};
static struct stat_descr sd_procdetail = {
NULL, /* sd_next */
SUBSYS_PROC, /* sd_subsys */
PROCTYPE_DETAIL, /* sd_type */
PROC_DETAIL_ALL | PROC_DETAIL_ALLPROC, /* sd_options */
0, /* sd_objid */
NULL, /* sd_addr */
0, /* sd_size */
0, /* sd_status */
0, /* sd_sizeused */
0 /* sd_time */
};
static struct stat_descr sd_cpu = {
NULL, /* sd_next */
SUBSYS_CPU, /* sd_subsys */
CPUTYPE_CPU, /* sd_type */
0, /* sd_options */
0, /* sd_objid */
&stat_cpu, /* sd_addr */
sizeof(stat_cpu), /* sd_size */
0, /* sd_status */
0, /* sd_sizeused */
0 /* sd_time */
};
/* precomputed values */
static int numcpus;
machine_init(statics)
struct statics *statics;
{
if (inq_stats(2, &sd_procconfig, &sd_class) == -1)
{
perror("proc config");
return(-1);
}
if (sd_procconfig.sd_status != 0)
{
fprintf(stderr, "stats status %d\n", sd_procconfig.sd_status);
}
#ifdef DEBUG
printf("pc_nprocs = %d\n", stat_pc.pc_nprocs);
printf("class_numcpus = %d\n", stat_class.class_numcpus);
#endif
/* things to remember */
numcpus = stat_class.class_numcpus;
/* space to allocate */
nprocs = stat_pc.pc_nprocs;
pd = (struct proc_detail *)malloc(nprocs * sizeof(struct proc_detail));
pref = (struct proc_detail **)malloc(nprocs * sizeof(struct proc_detail *));
if (pd == NULL || pref == NULL)
{
fprintf(stderr, "top: can't allocate sufficient memory\n");
return(-1);
}
/* pointers to assign */
sd_procdetail.sd_addr = pd;
sd_procdetail.sd_size = nprocs * sizeof(struct proc_detail);
/* fill in the statics stuff */
statics->procstate_names = procstatenames;
statics->cpustate_names = cpustatenames;
statics->memory_names = memorynames;
return(0);
}
char *format_header(uname_field)
register char *uname_field;
{
register char *ptr;
ptr = header + UNAME_START;
while (*uname_field != '\0')
{
*ptr++ = *uname_field++;
}
return(header);
}
get_system_info(si)
struct system_info *si;
{
/* get all status information at once */
inq_stats(1, &sd_memory);
/* fill in the memory statistics, converting to K */
memory_stats[0] = pagetok(stat_vm.vm_availmem);
memory_stats[1] = pagetok(stat_vm.vm_freemem);
memory_stats[2] = pagetok(stat_vm.vm_physmem - stat_vm.vm_availmem);
memory_stats[3] = 0; /* ??? */
/* set array pointers */
si->cpustates = cpu_states;
si->memory = memory_stats;
}
static struct handle handle;
caddr_t get_process_info(si, sel, compare)
struct system_info *si;
struct process_select *sel;
int (*compare)();
{
register int i;
register int index;
register int total;
int active_procs;
char show_idle;
char show_system;
char show_uid;
char show_command;
if (inq_stats(3, &sd_procsummary, &sd_cpu, &sd_procdetail) == -1)
{
perror("proc summary");
return(NULL);
}
if (sd_procsummary.sd_status != 0)
{
fprintf(stderr, "stats status %d\n", sd_procsummary.sd_status);
}
#ifdef DEBUG
printf("nfree = %d\n", stat_ps.ps_nfree);
printf("nzombies = %d\n", stat_ps.ps_nzombies);
printf("nnrunnable = %d\n", stat_ps.ps_nrunnable);
printf("nwaiting = %d\n", stat_ps.ps_nwaiting);
printf("nstopped = %d\n", stat_ps.ps_nstopped);
printf("curtime0 = %d.%d\n", stat_cpu.cpu_curtime.tv_sec, stat_cpu.cpu_curtime.tv_usec);
printf("starttime0 = %d.%d\n", stat_cpu.cpu_starttime.tv_sec, stat_cpu.cpu_starttime.tv_usec);
printf("usertime0 = %d.%d\n", stat_cpu.cpu_usertime.tv_sec, stat_cpu.cpu_usertime.tv_usec);
printf("systime0 = %d.%d\n", stat_cpu.cpu_systime.tv_sec, stat_cpu.cpu_systime.tv_usec);
printf("idletime0 = %d.%d\n", stat_cpu.cpu_idletime.tv_sec, stat_cpu.cpu_idletime.tv_usec);
printf("intrtime0 = %d.%d\n", stat_cpu.cpu_intrtime.tv_sec, stat_cpu.cpu_intrtime.tv_usec);
#endif
/* fill in the process related counts */
process_states[P_SLEEP] = stat_ps.ps_nwaiting;
process_states[P_RUN] = stat_ps.ps_nrunnable;
process_states[P_ZOMBIE] = stat_ps.ps_nzombies;
process_states[P_STOP] = stat_ps.ps_nstopped;
process_states[P_FREE] = stat_ps.ps_nfree;
si->procstates = process_states;
si->p_total = stat_ps.ps_nzombies +
stat_ps.ps_nrunnable +
stat_ps.ps_nwaiting +
stat_ps.ps_nstopped;
si->p_active = 0;
si->last_pid = -1;
/* calculate load averages, the ENCORE way! */
/* this code was inspiried by the program cpumeter */
i = total = 0;
index = stat_ps.ps_nrunidx;
/* we go in three cumulative steps: one for each avenrun measure */
/* we are (once again) sacrificing code size for speed */
while (i < MINUTES(1))
{
if (index < 0)
{
index = PS_NRUNSIZE - 1;
}
total += stat_ps.ps_nrun[index--];
i++;
}
si->load_avg[0] = (double)total / MINUTES(1);
while (i < MINUTES(5))
{
if (index < 0)
{
index = PS_NRUNSIZE - 1;
}
total += stat_ps.ps_nrun[index--];
i++;
}
si->load_avg[1] = (double)total / MINUTES(5);
while (i < MINUTES(15))
{
if (index < 0)
{
index = PS_NRUNSIZE - 1;
}
total += stat_ps.ps_nrun[index--];
i++;
}
si->load_avg[2] = (double)total / (double)MINUTES(15);
/* grab flags out of process_select for speed */
show_idle = sel->idle;
show_system = sel->system;
show_uid = sel->uid != -1;
show_command = sel->command != NULL;
/*
* Build a list of pointers to interesting proc_detail structures.
* inq_stats will return a proc_detail structure for every currently
* existing process.
*/
{
register struct proc_detail *pp;
register struct proc_detail **prefp;
register double virttime;
register double now;
/* pointer to destination array */
prefp = pref;
active_procs = 0;
/* calculate "now" based on inq_stats retrieval time */
now = TVTODOUBLE(sd_procdetail.sd_time);
/*
* Note: we will calculate the number of processes from
* procdetail.sd_sizeused just in case there is an inconsistency
* between it and the procsummary information.
*/
total = sd_procdetail.sd_sizeused / sizeof(struct proc_detail);
for (pp = pd, i = 0; i < total; pp++, i++)
{
/*
* Place pointers to each interesting structure in pref[]
* and compute something akin to %cpu usage. Computing %cpu
* is really hard with the information that inq_stats gives
* us, so we do the best we can based on the "virtual time"
* and cpu time fields. We also need a place to store this
* computation so that we only have to do it once. So we will
* borrow one of the int fields in the proc_detail, and set a
* #define accordingly.
*
* We currently have no good way to determine if a process is
* "idle", so we ignore the sel->idle flag.
*/
#define pd_pctcpu pd_swrss
if ((show_system || ((pp->pd_flag & SSYS) == 0)) &&
((pp->pd_flag & SZOMBIE) == 0) &&
(!show_uid || pp->pd_uid == (uid_t)sel->uid) &&
(!show_command || strcmp(sel->command, pp->pd_command) == 0))
{
/* calculate %cpu as best we can */
/* first, calculate total "virtual" cputime */
pp->pd_virttime = virttime = TVTODOUBLE(pp->pd_utime) +
TVTODOUBLE(pp->pd_stime);
/* %cpu is total cpu time over total wall time */
/* we express this as a percentage * 10 */
pp->pd_pctcpu = (int)(1000 * (virttime /
(now - TVTODOUBLE(pp->pd_starttime))));
/* store pointer to this record and move on */
*prefp++ = pp;
active_procs++;
}
}
}
/* if requested, sort the "interesting" processes */
if (compare != NULL)
{
qsort((char *)pref, active_procs,
sizeof(struct proc_detail *),
compare);
}
si->p_active = pref_len = active_procs;
/* pass back a handle */
handle.next_proc = pref;
handle.remaining = active_procs;
return((caddr_t)&handle);
}
char fmt[MAX_COLS]; /* static area where result is built */
char *format_next_process(handle, get_userid)
caddr_t handle;
char *(*get_userid)();
{
register struct proc_detail *pp;
register long cputime;
struct handle *hp;
/* find and remember the next proc structure */
hp = (struct handle *)handle;
pp = *(hp->next_proc++);
hp->remaining--;
/* set the cputime */
cputime = pp->pd_utime.tv_sec + pp->pd_stime.tv_sec;
/* calculate the base for cpu percentages */
#ifdef notyet
/*
* If there is more than one cpu then add the processor number to
* the "run/" string. Note that this will only show up if the
* process is in the run state. Also note: this will break for
* systems with more than 9 processors since the string will then
* be more than 5 characters. I'm still thinking about that one.
*/
if (numcpus > 1)
{
??? state_abbrev[SRUN][4] = (pp->p_cpuid & 0xf) + '0';
}
#endif
/* format this entry */
sprintf(fmt,
Proc_format,
pp->pd_pid,
(*get_userid)(pp->pd_uid),
pp->pd_pri, /* PZERO ??? */
pp->pd_nice, /* NZERO ??? */
format_k(pagetok(PROCSIZE(pp))),
format_k(pagetok(pp->pd_rssize)),
state_abbrev[pp->pd_state],
format_time((long)(pp->pd_virttime)),
(double)pp->pd_pctcpu / 10.,
printable(pp->pd_command));
/* return the result */
return(fmt);
}
/*
* proc_compare - comparison function for "qsort"
* Compares the resource consumption of two processes using five
* distinct keys. The keys (in descending order of importance) are:
* percent cpu, cpu ticks, state, resident set size, total virtual
* memory usage. The process states are ordered according to the
* premutation array "sorted_state" with higher numbers being sorted
* before lower numbers.
*/
static unsigned char sorted_state[] =
{
0, /* not used */
0, /* not used */
1, /* wait */
6, /* run */
3, /* start */
4, /* stop */
5, /* exec */
2 /* event */
};
proc_compare(pp1, pp2)
struct proc **pp1;
struct proc **pp2;
{
register struct proc_detail *p1;
register struct proc_detail *p2;
register int result;
/* remove one level of indirection */
p1 = *pp1;
p2 = *pp2;
/* compare percent cpu (pctcpu) */
if ((result = p2->pd_pctcpu - p1->pd_pctcpu) == 0)
{
/* use process state to break the tie */
if ((result = sorted_state[p2->pd_state] -
sorted_state[p1->pd_state]) == 0)
{
/* use priority to break the tie */
if ((result = p2->pd_pri - p1->pd_pri) == 0)
{
/* use resident set size (rssize) to break the tie */
if ((result = p2->pd_rssize - p1->pd_rssize) == 0)
{
/* use total memory to break the tie */
result = PROCSIZE(p2) - PROCSIZE(p1);
}
}
}
}
return(result);
}
/*
* 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(pid)
int pid;
{
register int cnt;
register struct proc_detail **prefp;
register struct proc_detail *pp;
prefp = pref;
cnt = pref_len;
while (--cnt >= 0)
{
if ((pp = *prefp++)->pd_pid == pid)
{
return(pp->pd_uid);
}
}
return(-1);
}
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.