/* * pg_top - a top PostgreSQL users display for Unix * * SYNOPSIS: any uniprocessor, 32 bit SGI machine running IRIX 5.3 * * DESCRIPTION: * This is the machine-dependent module for IRIX 5.3. * It has been tested on Indys running 5.3 and Indigos running 5.3XFS * * LIBS: -lmld * CFLAGS: -DHAVE_GETOPT * * AUTHOR: Sandeep Cariapa * This is not a supported product of Silicon Graphics, Inc. * Please do not call SGI for support. * */ #define _KMEMUSER #include "config.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "pg_top.h" #include "machine.h" #ifdef IRIX64 #define nlist nlist64 #define lseek lseek64 #define off_t off64_t #endif #define UNIX "/unix" #define KMEM "/dev/kmem" #define CPUSTATES 6 #ifndef FSCALE #define FSHIFT 8 /* bits to right of fixed binary point */ #define FSCALE (1<pr_fill) #define weighted_cpu(pp) (*(double *)&pp->pr_fill[2]) static int pagesize; #define pagetok(size) ((size)*pagesize) static int numcpus; /* * 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%% %.16s" /* these are for detailing the process states */ char *state_abbrev[] = {"", "sleep", "run\0\0\0", "zombie", "stop", "idle", "", "swap"}; int process_states[8]; char *procstatenames[] = { "", " sleeping, ", " running, ", " zombie, ", " stopped, ", " idle, ", "", " swapped, ", NULL }; /* these are for detailing the cpu states */ int cpu_states[CPUSTATES]; char *cpustatenames[] = { "idle", "usr", "ker", "wait", "swp", "intr", NULL }; /* these are for detailing the memory statistics */ long memory_stats[5]; char *memorynames[] = { "K max, ", "K avail, ", "K free, ", "K swap, ", "K free swap", NULL }; /* useful externals */ extern int errno; extern char *myname; extern char *sys_errlist[]; extern char *format_k(); extern char *format_time(); extern long percentages(); /* forward references */ int proc_compare(void *pp1, void *pp2); #define X_AVENRUN 0 #define X_NPROC 1 #define X_FREEMEM 2 #define X_MAXMEM 3 #define X_AVAILRMEM 4 #define X_MPID 5 static struct nlist nlst[] = { {"avenrun"}, /* 0. Array containing the 3 load averages. */ {"nproc"}, /* 1. Kernel parameter: Max number of * processes. */ {"freemem"}, /* 2. Amount of free memory in system. */ {"maxmem"}, /* 3. Maximum amount of memory usable by * system. */ {"availrmem"}, /* 4. Available real memory. */ #ifndef IRIX64 {"mpid"}, /* 5. PID of last process. */ #endif {0} }; static unsigned long avenrun_offset; static unsigned long nproc_offset; static unsigned long freemem_offset; static unsigned long maxmem_offset; static unsigned long availrmem_offset; static unsigned long mpid_offset; double load[3]; char fmt[MAX_COLS]; static int kmem; static int nproc; static int bytes; static struct prpsinfo *pbase; static struct prpsinfo **pref; static DIR *procdir; /* 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(); /* * 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 PRPSINFOSIZE (sizeof(struct prpsinfo)) int machine_init(statics) struct statics *statics; { struct oldproc *op, *endbase; if ((kmem = open(KMEM, O_RDONLY)) == -1) { perror(KMEM); return (-1); } /* get the list of symbols we want to access in the kernel */ (void) nlist(UNIX, nlst); if (nlst[0].n_type == 0) { fprintf(stderr, "%s: nlist failed\n", myname); return (-1); } /* Check if we got all of 'em. */ if (check_nlist(nlst) > 0) { return (-1); } avenrun_offset = nlst[X_AVENRUN].n_value; nproc_offset = nlst[X_NPROC].n_value; freemem_offset = nlst[X_FREEMEM].n_value; maxmem_offset = nlst[X_MAXMEM].n_value; availrmem_offset = nlst[X_AVAILRMEM].n_value; #ifndef IRIX64 mpid_offset = nlst[X_MPID].n_value; #endif /* * Got to do this first so that we can map real estate for the process * array. */ (void) getkval(nproc_offset, (int *) (&nproc), sizeof(nproc), "nproc"); /* allocate space for proc structure array and array of pointers */ bytes = nproc * sizeof(struct prpsinfo); 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) { (void) 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(_PATH_PROCFSPI))) { (void) fprintf(stderr, "Unable to open %s\n", _PATH_PROCFSPI); return (-1); } if (chdir(_PATH_PROCFSPI)) { /* handy for later on when we're reading it */ (void) fprintf(stderr, "Unable to chdir to %s\n", _PATH_PROCFSPI); return (-1); } statics->procstate_names = procstatenames; statics->cpustate_names = cpustatenames; statics->memory_names = memorynames; pagesize = getpagesize() / 1024; /* all done! */ 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); } void get_system_info(si) struct system_info *si; { register int i; int avenrun[3]; static int freemem; static int maxmem; static int availrmem; struct sysinfo sysinfo; static long cp_new[CPUSTATES]; static long cp_old[CPUSTATES]; static long cp_diff[CPUSTATES]; /* 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] = si->load_avg[i] / 1024.0; } (void) getkval(freemem_offset, (int *) (&freemem), sizeof(freemem), "freemem"); (void) getkval(maxmem_offset, (int *) (&maxmem), sizeof(maxmem), "maxmem"); (void) getkval(availrmem_offset, (int *) (&availrmem), sizeof(availrmem), "availrmem"); #ifdef IRIX64 si->last_pid = 0; #else (void) getkval(mpid_offset, &(si->last_pid), sizeof(si->last_pid), "mpid"); #endif swapctl(SC_GETFREESWAP, &fswap); swapctl(SC_GETSWAPTOT, &tswap); memory_stats[0] = pagetok(maxmem); memory_stats[1] = pagetok(availrmem); memory_stats[2] = pagetok(freemem); memory_stats[3] = tswap / 2; memory_stats[4] = fswap / 2; /* * use sysmp() to get current sysinfo usage. Can run into all kinds of * problems if you try to nlist this kernel variable. */ if (sysmp(MP_SAGET, MPSA_SINFO, &sysinfo, sizeof(struct sysinfo)) == -1) { perror("sysmp"); return; } /* copy sysinfo.cpu to an array of longs, as expected by percentages() */ for (i = 0; i < CPUSTATES; i++) { cp_new[i] = sysinfo.cpu[i]; } (void) percentages(CPUSTATES, cpu_states, cp_new, cp_old, cp_diff); si->cpustates = cpu_states; si->memory = memory_stats; numcpus = sysmp(MP_NPROCS); /* add a slash to the "run" state abbreviation */ if (numcpus > 1) { state_abbrev[SRUN][3] = '/'; } return; } caddr_t get_process_info(si, sel, x) struct system_info *si; struct process_select *sel; int x; { 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; /* 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; 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. */ 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 || (pp->pr_state == SRUN)) && (!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 *), proc_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 * format_next_process(handle, get_userid) 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); if (numcpus > 1) { if (pp->pr_sonproc < 0) state_abbrev[SRUN][4] = '*'; else state_abbrev[SRUN][4] = pp->pr_sonproc + '0'; } /* 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(pagetok(pp->pr_size)), format_k(pagetok(pp->pr_rssize)), state_abbrev[pp->pr_state], format_time(cputime), weighted_cpu(pp), pctcpu, 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(offset, ptr, size, refstr) off_t offset; int *ptr; int size; char *refstr; { if (lseek(kmem, offset, SEEK_SET) == -1) { if (*refstr == '!') refstr++; (void) fprintf(stderr, "%s: lseek to %s: %s\n", KMEM, refstr, strerror(errno)); quit(0); } if (read(kmem, (char *) ptr, size) == -1) { if (*refstr == '!') return (0); else { (void) fprintf(stderr, "%s: reading %s: %s\n", KMEM, refstr, strerror(errno)); quit(0); } } return (1); } /* * 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, idle, 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, /* idle */ 0, /* not used */ 1 /* being swapped (WAIT) */ }; int proc_compare(pp1, pp2) void *pp1; void *pp2; { register struct prpsinfo *p1; register struct prpsinfo *p2; register long result; /* remove one level of indirection */ p1 = *(struct prpsinfo **) pp1; p2 = *(struct prpsinfo **) pp2; /* compare percent cpu (pctcpu) */ if ((result = (long) (p2->pr_cpu - p1->pr_cpu)) == 0) { /* 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); } /* return the owner of the specified process. */ int proc_owner(pid) int pid; { register struct prpsinfo *p; int i; for (i = 0, p = pbase; i < nproc; i++, p++) if (p->pr_pid == (oid_t) pid) return ((int) p->pr_uid); return (-1); } /* * 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(nlst) 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); } /* get process table */ void getptable(baseptr) struct prpsinfo *baseptr; { struct prpsinfo *currproc; /* pointer to current proc structure */ int numprocs = 0; int i; struct dirent *directp; struct oldproc *op; static struct timeval lasttime = {0L, 0L}; struct timeval thistime; struct timezone thiszone; double timediff; double alpha, beta; struct oldproc *endbase; gettimeofday(&thistime, &thiszone); /* * 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; for (rewinddir(procdir); directp = readdir(procdir);) { int fd; if ((fd = open(directp->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); while (1) { 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 wrapped 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; } } numprocs++; (void) close(fd); } 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; } void get_io_info(struct io_info *io_info) { /* Not supported yet */ memset(io_info, 0, sizeof(*io_info)); } void get_disk_info(struct disk_info *disk_info, char *data_directory) { /* Not supported yet */ memset(disk_info, 0, sizeof(*disk_info)); }