Newsgroups: comp.sources.unix From: lefebvre@athens.dis.anl.gov (William LeFebvre) Subject: v29i009: top-3.3 - top users display, V3.3, Part09/16 References: <1.801816033.16454@gw.home.vix.com> Sender: unix-sources-moderator@gw.home.vix.com Approved: vixie@gw.home.vix.com Submitted-By: lefebvre@athens.dis.anl.gov (William LeFebvre) Posting-Number: Volume 29, Issue 9 Archive-Name: top-3.3/part09 #! /bin/sh # This is a shell archive. Remove anything before this line, then unpack # it by saving it into a file and typing "sh file". To overwrite existing # files, type "sh file -c". You can also feed this as standard input via # unshar, or by typing "sh 'machine/m_bsd43.c' <<'END_OF_FILE' X/* X * top - a top users display for Unix X * X * SYNOPSIS: any generic 4.3BSD system X * X * DESCRIPTION: X * This is the machine-dependent module for BSD4.3 X * Works for: X * 4.3 BSD X * AOS4.3, IBM rt/pc X * mtXinu, vax X * X * LIBS: X * X * AUTHOR: Christos Zoulas X */ X X#include X#include X#include X X#include X#include X#include X#include X#include X#include X#include X#include X#include X#include X#include X X X#define DOSWAP X X#include "top.h" X#include "machine.h" X#include "utils.h" X Xextern int errno, sys_nerr; Xextern char *sys_errlist[]; X#define strerror(e) (((e) >= 0 && (e) < sys_nerr) ? sys_errlist[(e)] : "Unknown error") X X#define VMUNIX "/vmunix" X#define KMEM "/dev/kmem" X#define MEM "/dev/mem" X#ifdef DOSWAP X#define SWAP "/dev/drum" X#endif X X/* get_process_info passes back a handle. This is what it looks like: */ X Xstruct handle X{ X struct proc **next_proc; /* points to next valid proc pointer */ X int remaining; /* number of pointers remaining */ X}; X X/* declarations for load_avg */ X#include "loadavg.h" X X/* define what weighted cpu is. */ X#define weighted_cpu(pct, pp) ((pp)->p_time == 0 ? 0.0 : \ X ((pct) / (1.0 - exp((pp)->p_time * logcpu)))) X X/* what we consider to be process size: */ X#define PROCSIZE(pp) ((pp)->p_tsize + (pp)->p_dsize + (pp)->p_ssize) X X/* definitions for indices in the nlist array */ X#define X_AVENRUN 0 X#define X_CCPU 1 X#define X_NPROC 2 X#define X_PROC 3 X#define X_TOTAL 4 X#define X_CP_TIME 5 X#define X_MPID 6 X#define X_HZ 7 X Xstatic struct nlist nlst[] = { X { "_avenrun" }, /* 0 */ X { "_ccpu" }, /* 1 */ X { "_nproc" }, /* 2 */ X { "_proc" }, /* 3 */ X { "_total" }, /* 4 */ X { "_cp_time" }, /* 5 */ X { "_mpid" }, /* 6 */ X { "_hz" }, /* 7 */ X { 0 } X}; X X/* X * These definitions control the format of the per-process area X */ X Xstatic char header[] = X " PID X PRI NICE SIZE RES STATE TIME WCPU CPU COMMAND"; X/* 0123456 -- field to fill in starts at header+6 */ X#define UNAME_START 6 X X#define Proc_format \ X "%5d %-8.8s %3d %4d %5s %5s %-5s %6s %5.2f%% %5.2f%% %.14s" X X X/* process state names for the "STATE" column of the display */ X/* the extra nulls in the string "run" are for adding a slash and X the processor number when needed */ X Xchar *state_abbrev[] = X{ X "", "sleep", "WAIT", "run\0\0\0", "start", "zomb", "stop" X}; X X Xstatic int kmem, mem; X#ifdef DOSWAP Xstatic int swap; X#endif X X/* values that we stash away in _init and use in later routines */ X Xstatic double logcpu; X X/* these are retrieved from the kernel in _init */ X Xstatic unsigned long proc; Xstatic int nproc; Xstatic long hz; Xstatic load_avg ccpu; Xstatic int ncpu = 0; X X/* these are offsets obtained via nlist and used in the get_ functions */ X Xstatic unsigned long avenrun_offset; Xstatic unsigned long mpid_offset; Xstatic unsigned long total_offset; Xstatic unsigned long cp_time_offset; X X/* these are for calculating cpu state percentages */ X Xstatic long cp_time[CPUSTATES]; Xstatic long cp_old[CPUSTATES]; Xstatic long cp_diff[CPUSTATES]; X X/* these are for detailing the process states */ X Xint process_states[7]; Xchar *procstatenames[] = { X "", " sleeping, ", " ABANDONED, ", " running, ", " starting, ", X " zombie, ", " stopped, ", X NULL X}; X X/* these are for detailing the cpu states */ X Xint cpu_states[4]; Xchar *cpustatenames[] = { X "user", "nice", "system", "idle", NULL X}; X X/* these are for detailing the memory statistics */ X Xint memory_stats[8]; Xchar *memorynames[] = { X "Real: ", "K/", "K act/tot ", "Virtual: ", "K/", X "K act/tot ", "Free: ", "K", NULL X}; X X/* these are for keeping track of the proc array */ X Xstatic int bytes; Xstatic int pref_len; Xstatic struct proc *pbase; Xstatic struct proc **pref; X X/* these are for getting the memory statistics */ X Xstatic int pageshift; /* log base 2 of the pagesize */ X X/* define pagetok in terms of pageshift */ X X#define pagetok(size) ((size) << pageshift) X X/* useful externals */ Xextern int errno; Xextern char *sys_errlist[]; X Xlong lseek(); Xlong time(); X Xmachine_init(statics) X Xstruct statics *statics; X X{ X register int i = 0; X register int pagesize; X X if ((kmem = open(KMEM, O_RDONLY)) == -1) { X perror(KMEM); X return(-1); X } X if ((mem = open(MEM, O_RDONLY)) == -1) { X perror(MEM); X return(-1); X } X X#ifdef DOSWAP X if ((swap = open(SWAP, O_RDONLY)) == -1) { X perror(SWAP); X return(-1); X } X#endif X X /* get the list of symbols we want to access in the kernel */ X (void) nlist(VMUNIX, nlst); X if (nlst[0].n_type == 0) X { X fprintf(stderr, "top: nlist failed\n"); X return(-1); X } X X /* make sure they were all found */ X if (i > 0 && check_nlist(nlst) > 0) X { X return(-1); X } X X /* get the symbol values out of kmem */ X (void) getkval(nlst[X_PROC].n_value, (int *)(&proc), sizeof(proc), X nlst[X_PROC].n_name); X (void) getkval(nlst[X_NPROC].n_value, &nproc, sizeof(nproc), X nlst[X_NPROC].n_name); X (void) getkval(nlst[X_HZ].n_value, (int *)(&hz), sizeof(hz), X nlst[X_HZ].n_name); X (void) getkval(nlst[X_CCPU].n_value, (int *)(&ccpu), sizeof(ccpu), X nlst[X_CCPU].n_name); X X /* stash away certain offsets for later use */ X mpid_offset = nlst[X_MPID].n_value; X avenrun_offset = nlst[X_AVENRUN].n_value; X total_offset = nlst[X_TOTAL].n_value; X cp_time_offset = nlst[X_CP_TIME].n_value; X X /* this is used in calculating WCPU -- calculate it ahead of time */ X logcpu = log(loaddouble(ccpu)); X X /* allocate space for proc structure array and array of pointers */ X bytes = nproc * sizeof(struct proc); X pbase = (struct proc *)malloc(bytes); X pref = (struct proc **)malloc(nproc * sizeof(struct proc *)); X X /* Just in case ... */ X if (pbase == (struct proc *)NULL || pref == (struct proc **)NULL) X { X fprintf(stderr, "top: can't allocate sufficient memory\n"); X return(-1); X } X X /* get the page size with "getpagesize" and calculate pageshift from it */ X pagesize = getpagesize(); X pageshift = 0; X while (pagesize > 1) X { X pageshift++; X pagesize >>= 1; X } X X /* we only need the amount of log(2)1024 for our conversion */ X pageshift -= LOG1024; X X /* fill in the statics information */ X statics->procstate_names = procstatenames; X statics->cpustate_names = cpustatenames; X statics->memory_names = memorynames; X X /* all done! */ X return(0); X} X Xchar *format_header(uname_field) X Xregister char *uname_field; X X{ X register char *ptr; X X ptr = header + UNAME_START; X while (*uname_field != '\0') X { X *ptr++ = *uname_field++; X } X X return(header); X} X Xget_system_info(si) X Xstruct system_info *si; X X{ X load_avg avenrun[3]; X long total; X X /* get the cp_time array */ X (void) getkval(cp_time_offset, (int *)cp_time, sizeof(cp_time), X "_cp_time"); X X /* get load average array */ X (void) getkval(avenrun_offset, (int *)avenrun, sizeof(avenrun), X "_avenrun"); X X /* get mpid -- process id of last process */ X (void) getkval(mpid_offset, &(si->last_pid), sizeof(si->last_pid), X "_mpid"); X X /* convert load averages to doubles */ X { X register int i; X register double *infoloadp; X register load_avg *sysloadp; X X infoloadp = si->load_avg; X sysloadp = avenrun; X for (i = 0; i < 3; i++) X { X *infoloadp++ = loaddouble(*sysloadp++); X } X } X X /* convert cp_time counts to percentages */ X total = percentages(CPUSTATES, cpu_states, cp_time, cp_old, cp_diff); X X /* sum memory statistics */ X { X struct vmtotal total; X X /* get total -- systemwide main memory usage structure */ X (void) getkval(total_offset, (int *)(&total), sizeof(total), X "_total"); X /* convert memory stats to Kbytes */ X memory_stats[0] = -1; X memory_stats[1] = pagetok(total.t_arm); X memory_stats[2] = pagetok(total.t_rm); X memory_stats[3] = -1; X memory_stats[4] = pagetok(total.t_avm); X memory_stats[5] = pagetok(total.t_vm); X memory_stats[6] = -1; X memory_stats[7] = pagetok(total.t_free); X } X X /* set arrays and strings */ X si->cpustates = cpu_states; X si->memory = memory_stats; X} X Xstatic struct handle handle; X Xcaddr_t get_process_info(si, sel, compare) X Xstruct system_info *si; Xstruct process_select *sel; Xint (*compare)(); X X{ X register int i; X register int total_procs; X register int active_procs; X register struct proc **prefp; X register struct proc *pp; X X /* these are copied out of sel for speed */ X int show_idle; X int show_system; X int show_uid; X int show_command; X X /* read all the proc structures in one fell swoop */ X (void) getkval(proc, (int *)pbase, bytes, "proc array"); X X /* get a pointer to the states summary array */ X si->procstates = process_states; X X /* set up flags which define what we are going to select */ X show_idle = sel->idle; X show_system = sel->system; X show_uid = sel->uid != -1; X show_command = sel->command != NULL; X X /* count up process states and get pointers to interesting procs */ X total_procs = 0; X active_procs = 0; X memset((char *)process_states, 0, sizeof(process_states)); X prefp = pref; X for (pp = pbase, i = 0; i < nproc; pp++, i++) X { X /* X * Place pointers to each valid proc structure in pref[]. X * Process slots that are actually in use have a non-zero X * status field. Processes with SSYS set are system X * processes---these get ignored unless show_sysprocs is set. X */ X if (pp->p_stat != 0 && X (show_system || ((pp->p_flag & SSYS) == 0))) X { X total_procs++; X process_states[pp->p_stat]++; X if ((pp->p_stat != SZOMB) && X (show_idle || (pp->p_pctcpu != 0) || (pp->p_stat == SRUN)) && X (!show_uid || pp->p_uid == (uid_t)sel->uid)) X { X *prefp++ = pp; X active_procs++; X } X } X } X X /* if requested, sort the "interesting" processes */ X if (compare != NULL) X { X qsort((char *)pref, active_procs, sizeof(struct proc *), compare); X } X X /* remember active and total counts */ X si->p_total = total_procs; X si->p_active = pref_len = active_procs; X X /* pass back a handle */ X handle.next_proc = pref; X handle.remaining = active_procs; X return((caddr_t)&handle); X} X Xchar fmt[MAX_COLS]; /* static area where result is built */ X Xchar *format_next_process(handle, get_userid) X Xcaddr_t handle; Xchar *(*get_userid)(); X X{ X register struct proc *pp; X register long cputime; X register double pct; X int where; X struct user u; X struct handle *hp; X X /* find and remember the next proc structure */ X hp = (struct handle *)handle; X pp = *(hp->next_proc++); X hp->remaining--; X X X /* get the process's user struct and set cputime */ X where = getu(pp, &u); X if (where == -1) X { X (void) strcpy(u.u_comm, ""); X cputime = 0; X } X else X { X X X /* set u_comm for system processes */ X if (u.u_comm[0] == '\0') X { X if (pp->p_pid == 0) X { X (void) strcpy(u.u_comm, "Swapper"); X } X else if (pp->p_pid == 2) X { X (void) strcpy(u.u_comm, "Pager"); X } X } X if (where == 1) { X /* X * Print swapped processes as X */ X char buf[sizeof(u.u_comm)]; X (void) strncpy(buf, u.u_comm, sizeof(u.u_comm)); X u.u_comm[0] = '<'; X (void) strncpy(&u.u_comm[1], buf, sizeof(u.u_comm) - 2); X u.u_comm[sizeof(u.u_comm) - 2] = '\0'; X (void) strncat(u.u_comm, ">", sizeof(u.u_comm) - 1); X u.u_comm[sizeof(u.u_comm) - 1] = '\0'; X } X X cputime = u.u_ru.ru_utime.tv_sec + u.u_ru.ru_stime.tv_sec; X } X X /* calculate the base for cpu percentages */ X pct = pctdouble(pp->p_pctcpu); X X /* format this entry */ X sprintf(fmt, X Proc_format, X pp->p_pid, X (*get_userid)(pp->p_uid), X pp->p_pri - PZERO, X pp->p_nice - NZERO, X format_k(pagetok(PROCSIZE(pp))), X format_k(pagetok(pp->p_rssize)), X state_abbrev[pp->p_stat], X format_time(cputime), X 100.0 * weighted_cpu(pct, pp), X 100.0 * pct, X printable(u.u_comm)); X X /* return the result */ X return(fmt); X} X X/* X * getu(p, u) - get the user structure for the process whose proc structure X * is pointed to by p. The user structure is put in the buffer pointed X * to by u. Return 0 if successful, -1 on failure (such as the process X * being swapped out). X */ X X#ifdef ibm032 Xstatic struct alignuser { X char userfill[UPAGES*NBPG-sizeof (struct user)]; X struct user user; X} au; X# define USERSIZE sizeof(struct alignuser) X# define GETUSER(b) (&au) X# define SETUSER(b) *(b) = au.user X#else X# define USERSIZE sizeof(struct user) X# define GETUSER(b) (b) X# define SETUSER(b) /* Nothing */ X#endif X Xgetu(p, u) X Xregister struct proc *p; Xstruct user *u; X X{ X struct pte uptes[UPAGES]; X register caddr_t upage; X register struct pte *pte; X register nbytes, n; X X /* X * Check if the process is currently loaded or swapped out. The way we X * get the u area is totally different for the two cases. For this X * application, we just don't bother if the process is swapped out. X */ X if ((p->p_flag & SLOAD) == 0) { X#ifdef DOSWAP X if (lseek(swap, (long)dtob(p->p_swaddr), 0) == -1) { X perror("lseek(swap)"); X return(-1); X } X if (read(swap, (char *) GETUSER(u), USERSIZE) != USERSIZE) { X perror("read(swap)"); X return(-1); X } X SETUSER(u); X return (1); X#else X return(-1); X#endif X } X X /* X * Process is currently in memory, we hope! X */ X if (!getkval((unsigned long)p->p_addr, (int *)uptes, sizeof(uptes), X "!p->p_addr")) X { X#ifdef DEBUG X perror("getkval(uptes)"); X#endif X /* we can't seem to get to it, so pretend it's swapped out */ X return(-1); X } X upage = (caddr_t) GETUSER(u); X pte = uptes; X for (nbytes = USERSIZE; nbytes > 0; nbytes -= NBPG) { X (void) lseek(mem, (long)(pte++->pg_pfnum * NBPG), 0); X#ifdef DEBUG X perror("lseek(mem)"); X#endif X n = MIN(nbytes, NBPG); X if (read(mem, upage, n) != n) { X#ifdef DEBUG X perror("read(mem)"); X#endif X /* we can't seem to get to it, so pretend it's swapped out */ X return(-1); X } X upage += n; X } X SETUSER(u); X return(0); X} X X/* X * check_nlist(nlst) - checks the nlist to see if any symbols were not X * found. For every symbol that was not found, a one-line X * message is printed to stderr. The routine returns the X * number of symbols NOT found. X */ X Xint check_nlist(nlst) X Xregister struct nlist *nlst; X X{ X register int i; X X /* check to see if we got ALL the symbols we requested */ X /* this will write one line to stderr for every symbol not found */ X X i = 0; X while (nlst->n_name != NULL) X { X if (nlst->n_type == 0) X { X /* this one wasn't found */ X fprintf(stderr, "kernel: no symbol named `%s'\n", nlst->n_name); X i = 1; X } X nlst++; X } X X return(i); X} X X X/* X * getkval(offset, ptr, size, refstr) - get a value out of the kernel. X * "offset" is the byte offset into the kernel for the desired value, X * "ptr" points to a buffer into which the value is retrieved, X * "size" is the size of the buffer (and the object to retrieve), X * "refstr" is a reference string used when printing error meessages, X * if "refstr" starts with a '!', then a failure on read will not X * be fatal (this may seem like a silly way to do things, but I X * really didn't want the overhead of another argument). X * X */ X Xgetkval(offset, ptr, size, refstr) X Xunsigned long offset; Xint *ptr; Xint size; Xchar *refstr; X X{ X if (lseek(kmem, (long)offset, L_SET) == -1) { X if (*refstr == '!') X refstr++; X (void) fprintf(stderr, "%s: lseek to %s: %s\n", KMEM, X refstr, strerror(errno)); X quit(23); X } X if (read(kmem, (char *) ptr, size) == -1) { X if (*refstr == '!') X return(0); X else { X (void) fprintf(stderr, "%s: reading %s: %s\n", KMEM, X refstr, strerror(errno)); X quit(23); X } X } X return(1); X} X X/* comparison routine for qsort */ X X/* X * proc_compare - comparison function for "qsort" X * Compares the resource consumption of two processes using five X * distinct keys. The keys (in descending order of importance) are: X * percent cpu, cpu ticks, state, resident set size, total virtual X * memory usage. The process states are ordered as follows (from least X * to most important): WAIT, zombie, sleep, stop, start, run. The X * array declaration below maps a process state index into a number X * that reflects this ordering. X */ X Xstatic unsigned char sorted_state[] = X{ X 0, /* not used */ X 3, /* sleep */ X 1, /* ABANDONED (WAIT) */ X 6, /* run */ X 5, /* start */ X 2, /* zombie */ X 4 /* stop */ X}; X Xproc_compare(pp1, pp2) X Xstruct proc **pp1; Xstruct proc **pp2; X X{ X register struct proc *p1; X register struct proc *p2; X register int result; X register pctcpu lresult; X X /* remove one level of indirection */ X p1 = *pp1; X p2 = *pp2; X X /* compare percent cpu (pctcpu) */ X if ((lresult = p2->p_pctcpu - p1->p_pctcpu) == 0) X { X /* use cpticks to break the tie */ X if ((result = p2->p_cpticks - p1->p_cpticks) == 0) X { X /* use process state to break the tie */ X if ((result = sorted_state[p2->p_stat] - X sorted_state[p1->p_stat]) == 0) X { X /* use priority to break the tie */ X if ((result = p2->p_pri - p1->p_pri) == 0) X { X /* use resident set size (rssize) to break the tie */ X if ((result = p2->p_rssize - p1->p_rssize) == 0) X { X /* use total memory to break the tie */ X result = PROCSIZE(p2) - PROCSIZE(p1); X } X } X } X } X } X else X { X result = lresult < 0 ? -1 : 1; X } X X return(result); X} X X/* X * proc_owner(pid) - returns the uid that owns process "pid", or -1 if X * the process does not exist. X * It is EXTREMLY IMPORTANT that this function work correctly. X * If top runs setuid root (as in SVR4), then this function X * is the only thing that stands in the way of a serious X * security problem. It validates requests for the "kill" X * and "renice" commands. X */ X Xint proc_owner(pid) X Xint pid; X X{ X register int cnt; X register struct proc **prefp; X register struct proc *pp; X X prefp = pref; X cnt = pref_len; X while (--cnt >= 0) X { X if ((pp = *prefp++)->p_pid == (pid_t)pid) X { X return((int)pp->p_uid); X } X } X return(-1); X} END_OF_FILE if test 18437 -ne `wc -c <'machine/m_bsd43.c'`; then echo shar: \"'machine/m_bsd43.c'\" unpacked with wrong size! fi # end of 'machine/m_bsd43.c' fi if test -f 'machine/m_ncr3000.c' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'machine/m_ncr3000.c'\" else echo shar: Extracting \"'machine/m_ncr3000.c'\" \(18818 characters\) sed "s/^X//" >'machine/m_ncr3000.c' <<'END_OF_FILE' X/* X * top - a top users display for Unix X * X * SYNOPSIS: For NCR 3000 series systems Release 2.00.02 and above - X * works on 2.03.00 and earlier (and probably later) OS releases. X * (Intel based System V Release 4) X * X * DESCRIPTION: X * System V release 4 for NCR 3000 series OS Rel 02.03.00 and above X * X * LIBS: -lelf X * X * AUTHORS: Andrew Herbert X * Robert Boucher X * Jeff Janvrin X * did the port to statfs (2.03) X */ X X#include "top.h" X#include "machine.h" X#include "utils.h" X#include X#include X#include X#include X#include X#include X#include X#include X#include X#include X#include X#include X#include X#include X#include X#include X#include X#include X#include X#include X#include X X#define UNIX "/stand/unix" X#define KMEM "/dev/kmem" X#define PROCFS "/proc" X#define CPUSTATES 5 X X#ifndef PRIO_MAX X#define PRIO_MAX 20 X#endif X#ifndef PRIO_MIN X#define PRIO_MIN -20 X#endif X X#ifndef FSCALE X#define FSHIFT 8 /* bits to right of fixed binary point */ X#define FSCALE (1<pr_cpu / FSCALE) X#define weighted_cpu(pct, pp) ( ((pp)->pr_time.tv_sec) == 0 ? 0.0 : \ X ((pp)->pr_cpu) / ((pp)->pr_time.tv_sec) ) X#define pagetok(size) ctob(size) >> LOG1024 X X/* definitions for the index in the nlist array */ X#define X_AVENRUN 0 X#define X_MPID 1 X#define X_V 2 X#define X_NPROC 3 X#define X_ANONINFO 4 X#define X_TOTAL 5 X#define X_SYSINFO 6 X Xstatic struct nlist nlst[] = X{ X{"avenrun"}, /* 0 */ X{"mpid"}, /* 1 */ X{"v"}, /* 2 */ X{"nproc"}, /* 3 */ X{"anoninfo"}, /* 4 */ X{"total"}, /* 5 */ X{"sysinfo"}, /* 6 */ X{NULL} X}; X Xstatic unsigned long avenrun_offset; Xstatic unsigned long mpid_offset; Xstatic unsigned long nproc_offset; Xstatic unsigned long anoninfo_offset; Xstatic unsigned long total_offset; Xstatic unsigned long sysinfo_offset; X X/* get_process_info passes back a handle. This is what it looks like: */ X Xstruct handle X { X struct prpsinfo **next_proc;/* points to next valid proc pointer */ X int remaining; /* number of pointers remaining */ X }; X X/* X * These definitions control the format of the per-process area X */ X Xstatic char header[] = X" PID X PRI NICE SIZE RES STATE TIME WCPU CPU COMMAND"; X/* 0123456 -- field to fill in starts at header+6 */ X#define UNAME_START 6 X#define Proc_format \ X "%5d %-8.8s %3d %4d %5s %5s %-5s %6s %3d.0%% %5.2f%% %.16s" X Xchar *state_abbrev[] = X{"", "sleep", "run", "zombie", "stop", "start", "cpu", "swap"}; X Xint process_states[8]; Xchar *procstatenames[] = X{ X "", " sleeping, ", " running, ", " zombie, ", " stopped, ", X " starting, ", " on cpu, ", " swapped, ", X NULL X}; X Xint cpu_states[CPUSTATES]; Xchar *cpustatenames[] = X{"idle", "user", "kernel", "wait", "swap", NULL}; X X/* these are for detailing the memory statistics */ X Xint memory_stats[5]; Xchar *memorynames[] = X{"K real, ", "K active, ", "K free, ", "K swap, ", "K free swap", NULL}; X Xstatic int kmem = -1; Xstatic int nproc; Xstatic int bytes; Xstatic int use_stats = 0; Xstatic struct prpsinfo *pbase; Xstatic struct prpsinfo **pref; Xstatic DIR *procdir; X X/* useful externals */ Xextern int errno; Xextern char *sys_errlist[]; Xextern char *myname; Xextern int check_nlist (); Xextern int getkval (); Xextern void perror (); Xextern void getptable (); Xextern void quit (); Xextern int nlist (); X Xint Xmachine_init (struct statics *statics) X { X static struct var v; X X /* fill in the statics information */ X statics->procstate_names = procstatenames; X statics->cpustate_names = cpustatenames; X statics->memory_names = memorynames; X X /* get the list of symbols we want to access in the kernel */ X if (nlist (UNIX, nlst)) X { X (void) fprintf (stderr, "Unable to nlist %s\n", UNIX); X return (-1); X } X X /* make sure they were all found */ X if (check_nlist (nlst) > 0) X return (-1); X X /* open kernel memory */ X if ((kmem = open (KMEM, O_RDONLY)) == -1) X { X perror (KMEM); X return (-1); X } X X /* get the symbol values out of kmem */ X /* NPROC Tuning parameter for max number of processes */ X (void) getkval (nlst[X_V].n_value, (int *) &v, sizeof (struct var), nlst[X_V].n_name); X nproc = v.v_proc; X X /* stash away certain offsets for later use */ X mpid_offset = nlst[X_MPID].n_value; X nproc_offset = nlst[X_NPROC].n_value; X avenrun_offset = nlst[X_AVENRUN].n_value; X anoninfo_offset = nlst[X_ANONINFO].n_value; X total_offset = nlst[X_TOTAL].n_value; X/* JJ this may need to be changed */ X sysinfo_offset = nlst[X_SYSINFO].n_value; X X /* allocate space for proc structure array and array of pointers */ X bytes = nproc * sizeof (struct prpsinfo); X pbase = (struct prpsinfo *) malloc (bytes); X pref = (struct prpsinfo **) malloc (nproc * sizeof (struct prpsinfo *)); X X /* Just in case ... */ X if (pbase == (struct prpsinfo *) NULL || pref == (struct prpsinfo **) NULL) X { X (void) fprintf (stderr, "%s: can't allocate sufficient memory\n", myname); X return (-1); X } X X if (!(procdir = opendir (PROCFS))) X { X (void) fprintf (stderr, "Unable to open %s\n", PROCFS); X return (-1); X } X X if (chdir (PROCFS)) X { /* handy for later on when we're reading it */ X (void) fprintf (stderr, "Unable to chdir to %s\n", PROCFS); X return (-1); X } X X /* all done! */ X return (0); X } X Xchar * Xformat_header (char *uname_field) X{ X register char *ptr; X X ptr = header + UNAME_START; X while (*uname_field != '\0') X *ptr++ = *uname_field++; X X return (header); X} X Xvoid Xget_system_info (struct system_info *si) X{ X long avenrun[3]; X struct sysinfo sysinfo; X static struct sysinfo *mpinfo = NULL; /* array, per-processor sysinfo structures. */ X struct vmtotal total; X struct anoninfo anoninfo; X static time_t cp_old[CPUSTATES]; X static time_t cp_diff[CPUSTATES]; /* for cpu state percentages */ X static int num_cpus; X static int fd_cpu = 0; X register int i; X X if ( use_stats == 1) { X if ( fd_cpu == 0 ) { X if ((fd_cpu = open("/stats/cpuinfo", O_RDONLY)) == -1) { X (void) fprintf (stderr, "%s: Open of /stats/cpuinfo failed\n", myname); X quit(2); X } X if (read(fd_cpu, &num_cpus, sizeof(int)) != sizeof(int)) { X (void) fprintf (stderr, "%s: Read of /stats/cpuinfo failed\n", myname); X quit(2); X } X close(fd_cpu); X } X if (mpinfo == NULL) { X mpinfo = (struct sysinfo *)calloc(num_cpus, sizeof(mpinfo[0])); X if (mpinfo == NULL) { X (void) fprintf (stderr, "%s: can't allocate space for per-processor sysinfos\n", myname); X quit(12); X } X } X /* Read the per cpu sysinfo structures into mpinfo struct. */ X read_sysinfos(num_cpus, mpinfo); X /* Add up all of the percpu sysinfos to get global sysinfo */ X sysinfo_data(num_cpus, &sysinfo, mpinfo); X } else { X (void) getkval (sysinfo_offset, &sysinfo, sizeof (struct sysinfo), "sysinfo"); X } X X /* convert cp_time counts to percentages */ X (void) percentages (CPUSTATES, cpu_states, sysinfo.cpu, cp_old, cp_diff); X X /* get mpid -- process id of last process */ X (void) getkval (mpid_offset, &(si->last_pid), sizeof (si->last_pid), X "mpid"); X X /* get load average array */ X (void) getkval (avenrun_offset, (int *) avenrun, sizeof (avenrun), "avenrun"); X X /* convert load averages to doubles */ X for (i = 0; i < 3; i++) X si->load_avg[i] = loaddouble (avenrun[i]); X X /* get total -- systemwide main memory usage structure */ X (void) getkval (total_offset, (int *) (&total), sizeof (total), "total"); X /* convert memory stats to Kbytes */ X memory_stats[0] = pagetok (total.t_rm); X memory_stats[1] = pagetok (total.t_arm); X memory_stats[2] = pagetok (total.t_free); X (void) getkval (anoninfo_offset, (int *) (&anoninfo), sizeof (anoninfo), X "anoninfo"); X memory_stats[3] = pagetok (anoninfo.ani_max - anoninfo.ani_free); X memory_stats[4] = pagetok (anoninfo.ani_max - anoninfo.ani_resv); X X /* set arrays and strings */ X si->cpustates = cpu_states; X si->memory = memory_stats; X} X Xstatic struct handle handle; X Xcaddr_t Xget_process_info ( X struct system_info *si, X struct process_select *sel, X int (*compare) ()) X{ X register int i; X register int total_procs; X register int active_procs; X register struct prpsinfo **prefp; X register struct prpsinfo *pp; X X /* these are copied out of sel for speed */ X int show_idle; X int show_system; X int show_uid; X X /* Get current number of processes */ X (void) getkval (nproc_offset, (int *) (&nproc), sizeof (nproc), "nproc"); X X /* read all the proc structures */ X getptable (pbase); X X /* get a pointer to the states summary array */ X si->procstates = process_states; X X /* set up flags which define what we are going to select */ X show_idle = sel->idle; X show_system = sel->system; X show_uid = sel->uid != -1; X X /* count up process states and get pointers to interesting procs */ X total_procs = 0; X active_procs = 0; X (void) memset (process_states, 0, sizeof (process_states)); X prefp = pref; X X for (pp = pbase, i = 0; i < nproc; pp++, i++) X { X /* X * Place pointers to each valid proc structure in pref[]. X * Process slots that are actually in use have a non-zero X * status field. Processes with SSYS set are system X * processes---these get ignored unless show_sysprocs is set. X */ X if (pp->pr_state != 0 && X (show_system || ((pp->pr_flag & SSYS) == 0))) X { X total_procs++; X process_states[pp->pr_state]++; X if ((!pp->pr_zomb) && X (show_idle || (pp->pr_state == SRUN) || (pp->pr_state == SONPROC)) && X (!show_uid || pp->pr_uid == (uid_t) sel->uid)) X { X *prefp++ = pp; X active_procs++; X } X } X } X X /* if requested, sort the "interesting" processes */ X if (compare != NULL) X qsort ((char *) pref, active_procs, sizeof (struct prpsinfo *), compare); X X /* remember active and total counts */ X si->p_total = total_procs; X si->p_active = active_procs; X X /* pass back a handle */ X handle.next_proc = pref; X handle.remaining = active_procs; X return ((caddr_t) & handle); X} X Xchar fmt[MAX_COLS]; /* static area where result is built */ X Xchar * Xformat_next_process ( X caddr_t handle, X char *(*get_userid) ()) X{ X register struct prpsinfo *pp; X struct handle *hp; X register long cputime; X register double pctcpu; X X /* find and remember the next proc structure */ X hp = (struct handle *) handle; X pp = *(hp->next_proc++); X hp->remaining--; X X /* get the cpu usage and calculate the cpu percentages */ X cputime = pp->pr_time.tv_sec; X pctcpu = percent_cpu (pp); X X /* format this entry */ X (void) sprintf (fmt, X Proc_format, X pp->pr_pid, X (*get_userid) (pp->pr_uid), X pp->pr_pri - PZERO, X pp->pr_nice - NZERO, X format_k(pagetok (pp->pr_size)), X format_k(pagetok (pp->pr_rssize)), X state_abbrev[pp->pr_state], X format_time(cputime), X (pp->pr_cpu & 0377), X 100.0 * pctcpu, X pp->pr_fname); X X /* return the result */ X return (fmt); X} X X/* X * check_nlist(nlst) - checks the nlist to see if any symbols were not X * found. For every symbol that was not found, a one-line X * message is printed to stderr. The routine returns the X * number of symbols NOT found. X */ Xint Xcheck_nlist (register struct nlist *nlst) X{ X register int i; X struct stat stat_buf; X X /* check to see if we got ALL the symbols we requested */ X /* this will write one line to stderr for every symbol not found */ X X i = 0; X while (nlst->n_name != NULL) X { X if (nlst->n_type == 0) X { X if (strcmp("sysinfo", nlst->n_name) == 0) X { X /* check to see if /stats file system exists. If so, */ X /* ignore error. */ X if ( !((stat("/stats/sysinfo", &stat_buf) == 0) && X (stat_buf.st_mode & S_IFREG)) ) X { X (void) fprintf (stderr, "kernel: no symbol named `%s'\n", nlst->n_name); X i = 1; X } else { X use_stats = 1; X } X } else { X X /* this one wasn't found */ X (void) fprintf (stderr, "kernel: no symbol named `%s'\n", nlst->n_name); X i = 1; X } X } X nlst++; X } X return (i); X} X X X/* X * getkval(offset, ptr, size, refstr) - get a value out of the kernel. X * "offset" is the byte offset into the kernel for the desired value, X * "ptr" points to a buffer into which the value is retrieved, X * "size" is the size of the buffer (and the object to retrieve), X * "refstr" is a reference string used when printing error meessages, X * if "refstr" starts with a '!', then a failure on read will not X * be fatal (this may seem like a silly way to do things, but I X * really didn't want the overhead of another argument). X * X */ Xint Xgetkval ( X unsigned long offset, X int *ptr, X int size, X char *refstr) X{ X if (lseek (kmem, (long) offset, 0) == -1) X { X if (*refstr == '!') X refstr++; X (void) fprintf (stderr, "%s: lseek to %s: %s\n", X myname, refstr, sys_errlist[errno]); X quit (22); X } X if (read (kmem, (char *) ptr, size) == -1) X if (*refstr == '!') X /* we lost the race with the kernel, process isn't in memory */ X return (0); X else X { X (void) fprintf (stderr, "%s: reading %s: %s\n", X myname, refstr, sys_errlist[errno]); X quit (23); X } X return (1); X} X X/* comparison routine for qsort */ X X/* X * proc_compare - comparison function for "qsort" X * Compares the resource consumption of two processes using five X * distinct keys. The keys (in descending order of importance) are: X * percent cpu, cpu ticks, state, resident set size, total virtual X * memory usage. The process states are ordered as follows (from least X * to most important): WAIT, zombie, sleep, stop, start, run. The X * array declaration below maps a process state index into a number X * that reflects this ordering. X */ X X Xunsigned char sorted_state[] = X{ X 0, /* not used */ X 3, /* sleep */ X 6, /* run */ X 2, /* zombie */ X 4, /* stop */ X 5, /* start */ X 7, /* run on a processor */ X 1 /* being swapped (WAIT) */ X}; X Xint Xproc_compare ( X struct prpsinfo **pp1, X struct prpsinfo **pp2) X{ X register struct prpsinfo *p1; X register struct prpsinfo *p2; X register long result; X X /* remove one level of indirection */ X p1 = *pp1; X p2 = *pp2; X X /* compare percent cpu (pctcpu) */ X if ((result = (long) (p2->pr_cpu - p1->pr_cpu)) == 0) X { X /* use cpticks to break the tie */ X if ((result = p2->pr_time.tv_sec - p1->pr_time.tv_sec) == 0) X { X /* use process state to break the tie */ X if ((result = (long) (sorted_state[p2->pr_state] - X sorted_state[p1->pr_state])) == 0) X { X /* use priority to break the tie */ X if ((result = p2->pr_oldpri - p1->pr_oldpri) == 0) X { X /* use resident set size (rssize) to break the tie */ X if ((result = p2->pr_rssize - p1->pr_rssize) == 0) X { X /* use total memory to break the tie */ X result = (p2->pr_size - p1->pr_size); X } X } X } X } X } X return (result); X } X X/* Xget process table X*/ Xvoid Xgetptable (struct prpsinfo *baseptr) X{ X struct prpsinfo *currproc; /* pointer to current proc structure */ X int numprocs = 0; X struct dirent *direntp; X X for (rewinddir (procdir); direntp = readdir (procdir);) X { X int fd; X X if ((fd = open (direntp->d_name, O_RDONLY)) < 0) X continue; X X currproc = &baseptr[numprocs]; X if (ioctl (fd, PIOCPSINFO, currproc) < 0) X { X (void) close (fd); X continue; X } X X numprocs++; X (void) close (fd); X } X X if (nproc != numprocs) X nproc = numprocs; X} X X/* return the owner of the specified process, for use in commands.c as we're X running setuid root */ Xuid_t Xproc_owner (pid_t pid) X{ X register struct prpsinfo *p; X int i; X for (i = 0, p = pbase; i < nproc; i++, p++) X if (p->pr_pid == pid) X return (p->pr_uid); X X return (-1); X} X Xint Xsetpriority (int dummy, int who, int niceval) X{ X int scale; X int prio; X pcinfo_t pcinfo; X pcparms_t pcparms; X tsparms_t *tsparms; X X strcpy (pcinfo.pc_clname, "TS"); X if (priocntl (0, 0, PC_GETCID, (caddr_t) & pcinfo) == -1) X return (-1); X X prio = niceval; X if (prio > PRIO_MAX) X prio = PRIO_MAX; X else if (prio < PRIO_MIN) X prio = PRIO_MIN; X X tsparms = (tsparms_t *) pcparms.pc_clparms; X scale = ((tsinfo_t *) pcinfo.pc_clinfo)->ts_maxupri; X tsparms->ts_uprilim = tsparms->ts_upri = -(scale * prio) / 20; X pcparms.pc_cid = pcinfo.pc_cid; X X if (priocntl (P_PID, who, PC_SETPARMS, (caddr_t) & pcparms) == -1) X return (-1); X X return (0); X} X X/**************************************************************** X * read_sysinfos() - * X * Read all of the CPU specific sysinfo sturctures in from * X * the /stats file system. * X ****************************************************************/ Xread_sysinfos(num_cpus, buf) X int num_cpus; X struct sysinfo *buf; X{ X X static int fd1=0; /* file descriptor for /stats/sysinfo */ X int read_sz; X X /* Open /stats/sysinfo one time only and leave it open */ X if (fd1==0) { X if ((fd1 = open("/stats/sysinfo", O_RDONLY)) == -1) X (void) fprintf (stderr, "%s: Open of /stats/sysinfo failed\n", myname); X } X /* reset the read pointer to the beginning of the file */ X if (lseek(fd1, 0L, SEEK_SET) == -1) X (void) fprintf (stderr, "%s: lseek to beginning of /stats/sysinfo failed\n", myname); X read_sz = num_cpus * sizeof(buf[0]); X if (read(fd1, buf, read_sz) != read_sz) X (void) fprintf (stderr, "%s: Read of /stats/sysinfo failed\n", myname); X} X X/**************************************************************** X * sysinfo_data() - * X * Add up all of the CPU specific sysinfo sturctures to * X * make the GLOBAL sysinfo. * X ****************************************************************/ Xsysinfo_data(num_cpus, global_si, percpu_si) X int num_cpus; X struct sysinfo *global_si; X struct sysinfo *percpu_si; X{ X struct sysinfo *percpu_p; X int cpu, i, *global, *src; X X /* null out the global statistics from last sample */ X memset(global_si, 0, sizeof(struct sysinfo)); X X percpu_p = (struct sysinfo *)percpu_si; X for(cpu = 0; cpu < num_cpus; cpu++) { X global = (int *)global_si; X src = (int *)percpu_p; X X /* assume sysinfo ends on an int boundary */ X /* Currently, all of the struct sysinfo members are the same X * size as an int. If that changes, we may not be able to X * do this. But this should be safe. X */ X for(i=0; i