This is m_sunos5.c in view mode; [Download] [Up]
/* * top - a top users display for Unix * * SYNOPSIS: Any Sun running SunOS 5.x (Solaris 2.x) * * DESCRIPTION: * This is the machine-dependent module for SunOS 5.x (Solaris 2). * There is some support for MP architectures. * This makes top work on the following systems: * SunOS 5.0 (not tested) * SunOS 5.1 * SunOS 5.2 * SunOS 5.3 * * Tested on a SPARCclassic with SunOS 5.1, using gcc-2.3.3, and * SPARCsystem 600 with SunOS 5.2, using Sun C * * LIBS: -lelf -lkvm * * CFLAGS: -DHAVE_GETOPT * * * AUTHORS: Torsten Kasch <torsten@techfak.uni-bielefeld.de> * Robert Boucher <boucher@sofkin.ca> * CONTRIBUTORS: Marc Cohen <marc@aai.com> * Charles Hedrick <hedrick@geneva.rutgers.edu> * William L. Jones <jones@chpc> * Petri Kutvonen <kutvonen@cs.helsinki.fi> * Casper Dik <casper@fwi.uva.nl> * Tim Pugh <tpugh@oce.orst.edu> */ #define _KMEMUSER #include "top.h" #include "machine.h" #include "utils.h" #include <stdio.h> #include <fcntl.h> #include <unistd.h> #include <stdlib.h> #include <errno.h> #include <dirent.h> #include <nlist.h> #include <string.h> #include <kvm.h> #include <sys/types.h> #include <sys/param.h> #include <sys/signal.h> #include <sys/fault.h> #include <sys/sysinfo.h> #include <sys/sysmacros.h> #include <sys/syscall.h> #include <sys/user.h> #include <sys/proc.h> #include <sys/procfs.h> #include <sys/vm.h> #include <sys/var.h> #include <sys/cpuvar.h> #include <sys/file.h> #include <sys/time.h> #include <sys/priocntl.h> #include <sys/tspriocntl.h> #include <sys/processor.h> #include <vm/anon.h> #include <math.h> #define UNIX "/dev/ksyms" #define KMEM "/dev/kmem" #define PROCFS "/proc" #define CPUSTATES 5 #ifndef PRIO_MIN #define PRIO_MIN -20 #endif #ifndef PRIO_MAX #define PRIO_MAX 20 #endif #ifndef FSCALE #define FSHIFT 8 /* bits to right of fixed binary point */ #define FSCALE (1<<FSHIFT) #endif /* FSCALE */ #define loaddouble(la) ((double)(la) / FSCALE) #define dbl_align(x) (((unsigned long)(x)+(sizeof(double)-1)) & \ ~(sizeof(double)-1)) #ifdef SOLARIS24 /* * snarfed from <sys/procfs.h>: * The following percent numbers are 16-bit binary * fractions [0 .. 1] with the binary point to the * right of the high-order bit (one == 0x8000) */ #define percent_cpu(pp) (((double)pp->pr_pctcpu)/0x8000*100) #define weighted_cpu(pp) (*(double *)dbl_align(pp->pr_filler)) #else #define percent_cpu(pp) (*(double *)dbl_align(&pp->pr_filler[0])) #define weighted_cpu(pp) (*(double *)dbl_align(&pp->pr_filler[2])) #endif /* NOTE: this next macro assumes that PAGESHIFT is 12---i.e.: a page is 4K. This is a blatantly general assumption and ought to be fixed */ #define pagetok(size) ((size)<<2) /* definitions for indices in the nlist array */ #define X_AVENRUN 0 #define X_MPID 1 #define X_CPU 2 #define X_V 3 #define X_NPROC 4 #define X_ANONINFO 5 #define X_FREEMEM 6 #define X_MAXMEM 7 #define X_AVAILRMEM 8 #define X_SWAPFS_MINFREE 9 #define X_NCPUS 10 static struct nlist nlst[] = { {"avenrun"}, /* 0 */ {"mpid"}, /* 1 */ {"cpu"}, /* 2 */ {"v"}, /* 3 */ {"nproc"}, /* 4 */ {"anoninfo"}, /* 5 */ {"freemem"}, /* 6 */ {"maxmem"}, /* 7 */ {"availrmem"}, /* 8 */ {"swapfs_minfree"}, /* 9 */ {"ncpus"}, /* 10 */ {0} }; static unsigned long avenrun_offset; static unsigned long mpid_offset; static unsigned long *cpu_offset; static unsigned long nproc_offset; static unsigned long freemem_offset; static unsigned long maxmem_offset; static unsigned long availrmem_offset; static unsigned long swapfs_minfree_offset; static unsigned long anoninfo_offset; /* 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 */ }; /* * 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; }; int oldprocs; /* size of table */ #define HASH(x) ((x << 1) % oldprocs) /* * GCC assumes that all doubles are aligned. Unfortunately it * doesn't round up the structure size to be a multiple of 8. * Thus we'll get a coredump when going through array. The * following is a size rounded up to 8. */ #define PRPSINFOSIZE dbl_align(sizeof(struct prpsinfo)) /* * These definitions control the format of the per-process area */ static char header[] = " PID X PRI NICE SIZE RES STATE TIME WCPU 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 %5.2f%% %5.2f%% %s" /* process state names for the "STATE" column of the display */ /* the extra nulls in the string "run" are for adding a slash and the processor number when needed */ char *state_abbrev[] = {"", "sleep", "run", "zombie", "stop", "start", "cpu", "swap"}; int process_states[8]; char *procstatenames[] = { "", " sleeping, ", " running, ", " zombie, ", " stopped, ", " starting, ", " on cpu, ", " swapped, ", NULL }; int cpu_states[CPUSTATES]; char *cpustatenames[] = {"idle", "user", "kernel", "iowait", "swap", NULL}; /* these are for detailing the memory statistics */ int memory_stats[5]; char *memorynames[] = {"K real, ", "K active, ", "K free, ", "K swap, ", "K free swap", NULL}; kvm_t *kd; static DIR *procdir; static int nproc; static int ncpus; /* these are for keeping track of the proc array */ static int bytes; static struct prpsinfo *pbase; static struct prpsinfo **pref; static struct oldproc *oldbase; /* useful externals */ extern int errno; extern char *sys_errlist[]; extern char *myname; extern int check_nlist (); extern int gettimeofday (); extern int getkval (); extern void perror (); extern void getptable (); extern void quit (); extern int nlist (); int machine_init (struct statics *statics) { static struct var v; struct oldproc *op, *endbase; int i; int offset; processor_info_t pi; /* perform the kvm_open */ kd = kvm_open (NULL, NULL, NULL, O_RDONLY, "top"); /* turn off super user privs */ seteuid(getuid()); /* fill in the statics information */ statics->procstate_names = procstatenames; statics->cpustate_names = cpustatenames; statics->memory_names = memorynames; /* test kvm_open return value */ if (kd == NULL) { perror ("kvm_open"); return (-1); } if (kvm_nlist (kd, nlst) < 0) { perror ("kvm_nlist"); return (-1); } if (check_nlist (nlst) != 0) return (-1); /* NPROC Tuning parameter for max number of processes */ (void) getkval (nlst[X_V].n_value, &v, sizeof (struct var), nlst[X_V].n_name); nproc = v.v_proc; /* stash away certain offsets for later use */ mpid_offset = nlst[X_MPID].n_value; nproc_offset = nlst[X_NPROC].n_value; avenrun_offset = nlst[X_AVENRUN].n_value; anoninfo_offset = nlst[X_ANONINFO].n_value; freemem_offset = nlst[X_FREEMEM].n_value; maxmem_offset = nlst[X_MAXMEM].n_value; availrmem_offset = nlst[X_AVAILRMEM].n_value; swapfs_minfree_offset = nlst[X_SWAPFS_MINFREE].n_value; (void) getkval (nlst[X_NCPUS].n_value, (int *) (&ncpus), sizeof (ncpus), "ncpus"); cpu_offset = (unsigned long *) malloc (ncpus * sizeof (unsigned long)); for (i = offset = 0; i < ncpus; offset += sizeof(unsigned long)) { (void) getkval (nlst[X_CPU].n_value + offset, &cpu_offset[i], sizeof (unsigned long), nlst[X_CPU].n_name ); if (cpu_offset[i] != 0) i++; } /* allocate space for proc structure array and array of pointers */ bytes = nproc * PRPSINFOSIZE; pbase = (struct prpsinfo *) malloc (bytes); pref = (struct prpsinfo **) malloc (nproc * sizeof (struct prpsinfo *)); oldbase = (struct oldproc *) malloc (2 * nproc * sizeof (struct oldproc)); /* Just in case ... */ if (pbase == (struct prpsinfo *) NULL || pref == (struct prpsinfo **) NULL || oldbase == (struct oldproc *) NULL) { fprintf (stderr, "%s: can't allocate sufficient memory\n", myname); return (-1); } oldprocs = 2 * nproc; endbase = oldbase + oldprocs; for (op = oldbase; op < endbase; op++) op->oldpid = -1; if (!(procdir = opendir (PROCFS))) { (void) fprintf (stderr, "Unable to open %s\n", PROCFS); return (-1); } if (chdir (PROCFS)) { /* handy for later on when we're reading it */ (void) fprintf (stderr, "Unable to chdir to %s\n", PROCFS); return (-1); } /* all done! */ 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) { long avenrun[3]; struct cpu cpu; static int freemem; static int maxmem; static int availrmem; static int swapfs_minfree; struct anoninfo anoninfo; static long cp_time[CPUSTATES]; static long cp_old[CPUSTATES]; static long cp_diff[CPUSTATES]; register int j, i; /* get the cp_time array */ for (j = 0; j < CPUSTATES; j++) cp_time[j] = 0L; for (i = 0; i < ncpus; i++) if (cpu_offset[i] != 0) { (void) getkval (cpu_offset[i], &cpu, sizeof (struct cpu), "cpu"); for (j = 0; j < CPUSTATES-1; j++) cp_time[j] += (long) cpu.cpu_stat.cpu_sysinfo.cpu[j]; cp_time[CPUSTATES-2] += (long) cpu.cpu_stat.cpu_sysinfo.wait[W_IO] + (long) cpu.cpu_stat.cpu_sysinfo.wait[W_PIO]; cp_time[CPUSTATES-1] += (long) cpu.cpu_stat.cpu_sysinfo.wait[W_SWAP]; } /* convert cp_time counts to percentages */ (void) percentages (CPUSTATES, cpu_states, cp_time, cp_old, cp_diff); /* get mpid -- process id of last process */ (void) getkval (mpid_offset, &(si->last_pid), sizeof (si->last_pid), "mpid"); /* get load average array */ (void) getkval (avenrun_offset, (int *) avenrun, sizeof (avenrun), "avenrun"); /* convert load averages to doubles */ for (i = 0; i < 3; i++) si->load_avg[i] = loaddouble (avenrun[i]); /* get system wide main memory usage structure */ (void) getkval (freemem_offset, (int *) (&freemem), sizeof (freemem), "freemem"); (void) getkval (maxmem_offset, (int *) (&maxmem), sizeof (maxmem), "maxmem"); memory_stats[0] = pagetok (maxmem); memory_stats[1] = 0; memory_stats[2] = pagetok (freemem); (void) getkval (anoninfo_offset, (int *) (&anoninfo), sizeof (anoninfo), "anoninfo"); (void) getkval (availrmem_offset, (int *) (&availrmem), sizeof (availrmem), "availrmem"); (void) getkval (swapfs_minfree_offset, (int *) (&swapfs_minfree), sizeof (swapfs_minfree), "swapfs_minfree"); memory_stats[3] = pagetok (anoninfo.ani_resv); memory_stats[4] = pagetok (MAX ((int) (anoninfo.ani_max - anoninfo.ani_resv), 0) + availrmem - swapfs_minfree); /* set arrays and strings */ si->cpustates = cpu_states; si->memory = memory_stats; } static struct handle handle; caddr_t get_process_info ( struct system_info *si, struct process_select *sel, int (*compare) ()) { register int i; register int total_procs; register int active_procs; register struct prpsinfo **prefp; register struct prpsinfo *pp; /* these are copied out of sel for speed */ int show_idle; int show_system; int show_uid; /* Get current number of processes */ (void) getkval (nproc_offset, (int *) (&nproc), sizeof (nproc), "nproc"); /* 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_idle = sel->idle; show_system = sel->system; 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; i++, pp = (struct prpsinfo *) ((char *) pp + PRPSINFOSIZE)) { /* * 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_sysprocs is set. */ if (pp->pr_state != 0 && (show_system || ((pp->pr_flag & SSYS) == 0))) { total_procs++; process_states[pp->pr_state]++; if ((!pp->pr_zomb) && (show_idle || percent_cpu (pp) || (pp->pr_state == SRUN) || (pp->pr_state == SONPROC)) && (!show_uid || pp->pr_uid == (uid_t) sel->uid)) { *prefp++ = pp; active_procs++; } } } /* if requested, sort the "interesting" processes */ if (compare != NULL) qsort ((char *) pref, active_procs, sizeof (struct prpsinfo *), compare); /* 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); } char fmt[MAX_COLS]; /* static area where result is built */ char * format_next_process ( caddr_t handle, char *(*get_userid) ()) { register struct prpsinfo *pp; struct handle *hp; register long cputime; register double pctcpu; /* find and remember the next proc structure */ hp = (struct handle *) handle; pp = *(hp->next_proc++); hp->remaining--; /* get the cpu usage and calculate the cpu percentages */ cputime = pp->pr_time.tv_sec; pctcpu = percent_cpu (pp); /* format this entry */ sprintf (fmt, Proc_format, pp->pr_pid, (*get_userid) (pp->pr_uid), pp->pr_pri - PZERO, pp->pr_nice - NZERO, format_k(pp->pr_bysize / 1024), format_k(pp->pr_byrssize / 1024), state_abbrev[pp->pr_state], format_time(cputime), weighted_cpu (pp), pctcpu, pp->pr_fname); /* return the result */ return (fmt); } /* * check_nlist(nlst) - checks the nlist to see if any symbols were not * found. For every symbol that was not found, a one-line * message is printed to stderr. The routine returns the * number of symbols NOT found. */ int check_nlist (register struct nlist *nlst) { register int i; /* check to see if we got ALL the symbols we requested */ /* this will write one line to stderr for every symbol not found */ i = 0; while (nlst->n_name != NULL) { if (nlst->n_type == 0) { /* this one wasn't found */ fprintf (stderr, "kernel: no symbol named `%s'\n", nlst->n_name); i = 1; } nlst++; } return (i); } /* * 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 (kvm_read (kd, offset, (char *) ptr, size) != size) { if (*refstr == '!') { return (0); } else { fprintf (stderr, "top: kvm_read for %s: %s\n", refstr, sys_errlist[errno]); quit (23); } } return (1); } /* comparison routine for qsort */ /* * 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 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. */ unsigned char sorted_state[] = { 0, /* not used */ 3, /* sleep */ 6, /* run */ 2, /* zombie */ 4, /* stop */ 5, /* start */ 7, /* run on a processor */ 1 /* being swapped (WAIT) */ }; int proc_compare ( struct prpsinfo **pp1, struct prpsinfo **pp2) { register struct prpsinfo *p1; register struct prpsinfo *p2; register long result; double dresult; /* remove one level of indirection */ p1 = *pp1; p2 = *pp2; /* compare percent cpu (pctcpu) */ dresult = percent_cpu (p2) - percent_cpu (p1); if (dresult != 0.0) { if (dresult > 0.0) return 1; else return -1; } { /* use cpticks to break the tie */ if ((result = p2->pr_time.tv_sec - p1->pr_time.tv_sec) == 0) { /* use process state to break the tie */ if ((result = (long) (sorted_state[p2->pr_state] - sorted_state[p1->pr_state])) == 0) { /* use priority to break the tie */ if ((result = p2->pr_oldpri - p1->pr_oldpri) == 0) { /* use resident set size (rssize) to break the tie */ if ((result = p2->pr_rssize - p1->pr_rssize) == 0) { /* use total memory to break the tie */ result = (p2->pr_size - p1->pr_size); } } } } } return (result); } /* get process table V.4 only has a linked list of processes so we want to follow that linked list, get all the process structures, and put them in our own table */ void getptable (struct prpsinfo *baseptr) { struct prpsinfo *currproc; /* pointer to current proc structure */ int numprocs = 0; int i; struct dirent *direntp; struct oldproc *op; static struct timeval lasttime = {0, 0}; struct timeval thistime; double timediff; double alpha, beta; struct oldproc *endbase; char d_name[12]; gettimeofday (&thistime); /* * To avoid divides, we keep times in nanoseconds. This is * scaled by 1e7 rather than 1e9 so that when we divide we * get percent. */ if (lasttime.tv_sec) timediff = ((double) thistime.tv_sec * 1.0e7 + ((double) thistime.tv_usec * 10.0)) - ((double) lasttime.tv_sec * 1.0e7 + ((double) lasttime.tv_usec * 10.0)); else timediff = 1.0e7; /* * constants for exponential 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 / 30.0e7); beta = 1.0 - alpha; } else { alpha = 0.5; beta = 0.5; } endbase = oldbase + oldprocs; currproc = baseptr; /* before reading /proc files, turn on root privs */ /* (we don't care if this fails since it will be caught later) */ seteuid(0); rewinddir (procdir); while( (direntp = readdir (procdir)) != NULL) { int fd; if(direntp->d_ino <= 64) continue; /* use file name = (direntp->d_ino - 64), not direntp->d_name. * 1/12/96 tpugh */ sprintf(d_name,"%i",direntp->d_ino - 64); if ((fd = open (d_name, O_RDONLY)) < 0) { perror("top getptable open"); continue; } 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); while (1) { if (op->oldpid == -1) /* not there */ break; if (op->oldpid == currproc->pr_pid) { /* found old data */ #ifndef SOLARIS24 percent_cpu (currproc) = ((currproc->pr_time.tv_sec * 1.0e9 + currproc->pr_time.tv_nsec) - op->oldtime) / timediff; #endif weighted_cpu (currproc) = op->oldpct * beta + percent_cpu (currproc) * alpha; break; } op++; /* try next entry in hash table */ if (op == endbase) /* table wrapped around */ op = oldbase; } /* Otherwise, it's new, so use all of its CPU time */ if (op->oldpid == -1) { #ifdef SOLARIS24 weighted_cpu (currproc) = percent_cpu (currproc); #else 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; } #endif } numprocs++; currproc = (struct prpsinfo *) ((char *) currproc + PRPSINFOSIZE); (void) close (fd); } /* turn off root privs */ seteuid(getuid()); if (nproc != numprocs) 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 = (struct prpsinfo *) ((char *) currproc + PRPSINFOSIZE)) { /* find an empty spot */ op = oldbase + HASH (currproc->pr_pid); while (1) { 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); } lasttime = thistime; } /* * 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. */ uid_t proc_owner (pid_t pid) { register struct prpsinfo *p; int i; for (i = 0, p = pbase; i < nproc; i++, p = (struct prpsinfo *) ((char *) p + PRPSINFOSIZE)) { if (p->pr_pid == pid) return (p->pr_uid); } return (-1); } int setpriority (int dummy, int who, int niceval) { int scale; int prio; pcinfo_t pcinfo; pcparms_t pcparms; tsparms_t *tsparms; strcpy (pcinfo.pc_clname, "TS"); if (priocntl (0, 0, PC_GETCID, (caddr_t) & pcinfo) == -1) return (-1); prio = niceval; if (prio > PRIO_MAX) prio = PRIO_MAX; else if (prio < PRIO_MIN) prio = PRIO_MIN; tsparms = (tsparms_t *) pcparms.pc_clparms; scale = ((tsinfo_t *) pcinfo.pc_clinfo)->ts_maxupri; tsparms->ts_uprilim = tsparms->ts_upri = -(scale * prio) / 20; pcparms.pc_cid = pcinfo.pc_cid; if (priocntl (P_PID, who, PC_SETPARMS, (caddr_t) & pcparms) == -1) return (-1); return (0); }
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.