/* * pg_top - a top PostgreSQL users display for Unix * * SYNOPSIS: For Intel based System V Release 5 (Unixware7) * * DESCRIPTION: * System V release 5 for i[3456]86 * Works for: * i586-sco-sysv5uw7 i386 SCO UNIX_SVR5 (UnixWare 7) * * LIBS: -lelf -lmas * * CFLAGS: -DHAVE_GETOPT -DORDER * * AUTHORS: Mike Hopkirk * David Cutter * Andrew Herbert * Robert Boucher */ /* build config * SHOW_NICE - process nice fields don't seem to be being updated so changed * default to display # of threads in use instead. * define this to display nice fields (values always 0) * #define SHOW_NICE 1 */ #define _KMEMUSER #define prpsinfo psinfo #include #define pr_state pr_lwp.pr_state #define pr_nice pr_lwp.pr_nice #define pr_pri pr_lwp.pr_pri #define pr_onpro pr_lwp.pr_onpro #define ZOMBIE(p) ((p)->pr_nlwp == 0) #define SIZE_K(p) pagetok((p)->pr_size) #define RSS_K(p) pagetok((p)->pr_rssize) #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "pg_top.h" #include "machine.h" #include "utils.h" #define UNIX "/stand/unix" #define KMEM "/dev/kmem" #define PROCFS "/proc" #define CPUSTATES 5 #ifndef PRIO_MAX #define PRIO_MAX 20 #endif #ifndef PRIO_MIN #define PRIO_MIN -20 #endif #ifndef FSCALE #define FSHIFT 8 /* bits to right of fixed binary point */ #define FSCALE (1<> LOG1024 /* definitions for the index in the nlist array */ #define X_AVENRUN 0 #define X_V 1 #define X_MPID 2 static struct nlist nlst[] = { {"avenrun"}, /* 0 */ {"v"}, /* 1 */ {"nextpid"}, /* 2 */ {NULL} }; static unsigned long avenrun_offset; static unsigned long mpid_offset; static unsigned int pagesz; static void reallocproc(int n); static int maxprocs; /* 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 */ }; /* * These definitions control the format of the per-process area */ static char header[] = #ifdef SHOW_NICE " PID X PRI NICE SIZE RES STATE TIME CPU COMMAND"; #else " PID X PRI THR SIZE RES STATE TIME CPU COMMAND"; #endif /* 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 %8.4f%% %.16s" char *state_abbrev[] = {"oncpu", "run", "sleep", "stop", "idle", "zombie"}; #define sZOMB 5 int process_states[8]; char *procstatenames[] = { " on cpu, ", " running, ", " sleeping, ", " stopped, ", " idling ", " zombie, ", NULL }; int cpu_states[CPUSTATES]; char *cpustatenames[] = {"idle", "user", "kernel", "wait", NULL}; /* these are for detailing the memory statistics */ long memory_stats[5]; char *memorynames[] = {"K phys, ", "K used, ", "K free, ", "K swapUsed, ", "K swapFree", NULL}; /* these are names given to allowed sorting orders -- first is default */ char *ordernames[] = { "state", "cpu", "size", "res", "time", "pid", "uid", "rpid", "ruid", NULL }; /* forward definitions for comparison functions */ int proc_compare(); int compare_cpu(); int compare_size(); int compare_res(); int compare_time(); int compare_pid(); int compare_uid(); int compare_rpid(); int compare_ruid(); int (*proc_compares[]) () = { proc_compare, compare_cpu, compare_size, compare_res, compare_time, compare_pid, compare_uid, compare_rpid, compare_ruid, NULL }; static int kmem = -1; static int nproc; static int bytes; static struct prpsinfo *pbase; static struct prpsinfo **pref; static DIR *procdir; /* useful externals */ extern int errno; extern char *sys_errlist[]; extern char *myname; extern long percentages(); extern int check_nlist(); extern int getkval(); extern void perror(); extern void getptable(); extern void quit(); extern int nlist(); /* fwd dcls */ static int kmet_init(void); static int get_cpustates(int *new); int machine_init(struct statics * statics) { static struct var v; int i; /* fill in the statics information */ statics->procstate_names = procstatenames; statics->cpustate_names = cpustatenames; statics->memory_names = memorynames; statics->order_names = ordernames; /* get the list of symbols we want to access in the kernel */ if (nlist(UNIX, nlst)) { (void) fprintf(stderr, "Unable to nlist %s\n", UNIX); return (-1); } /* make sure they were all found */ if (check_nlist(nlst) > 0) return (-1); /* open kernel memory */ if ((kmem = open(KMEM, O_RDONLY)) == -1) { perror(KMEM); return (-1); } v.v_proc = 200; /* arbitrary default */ /* get the symbol values out of kmem */ /* 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; maxprocs = nproc; /* stash away certain offsets for later use */ mpid_offset = nlst[X_MPID].n_value; avenrun_offset = nlst[X_AVENRUN].n_value; /* 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 *)); pagesz = sysconf(_SC_PAGESIZE); /* Just in case ... */ if (pbase == (struct prpsinfo *) NULL || pref == (struct prpsinfo **) NULL) { (void) fprintf(stderr, "%s: can't allocate sufficient memory\n", myname); return (-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); } kmet_init(); /* all done! */ return (0); } char * format_header(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]; long mem; static time_t cp_old[CPUSTATES]; static time_t cp_diff[CPUSTATES]; /* for cpu state percentages */ register int i; static long swap_total; static long swap_free; int new_states[CPUSTATES]; get_cpustates(new_states); /* convert cp_time counts to percentages */ (void) percentages(CPUSTATES, cpu_states, new_states, cp_old, cp_diff); si->last_pid = -1; /* * get mpid -- process id of last process svr5 is nextpid - next pid to be * assigned (already incremented) */ (void) getkval(mpid_offset, &(si->last_pid), sizeof(si->last_pid), "nextpid"); (si->last_pid)--; /* so we shld decrement for display */ /* 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]); mem = sysconf(_SC_TOTAL_MEMORY); /* physical mem */ memory_stats[0] = pagetok(mem); mem = kmet_get_freemem(); /* free mem */ memory_stats[2] = pagetok(mem); /* mem = sysconf(_SC_GENERAL_MEMORY); */ memory_stats[1] = memory_stats[0] - memory_stats[2]; /* active */ get_swapinfo(&swap_total, &swap_free); memory_stats[3] = pagetok(swap_total - swap_free); memory_stats[4] = pagetok(swap_free); /* 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 idx) { 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 */ /* 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; nproc = kmet_get_nproc(); /* 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 PR_ISSYS set are system processes---these get * ignored unless show_sysprocs is set. */ if ((pp->pr_state >= SONPROC && pp->pr_state <= SIDL) && (show_system || ((pp->pr_flag & PR_ISSYS) == 0))) { total_procs++; process_states[pp->pr_state]++; if ((!ZOMBIE(pp)) && (show_idle || (pp->pr_state == SRUN) || (pp->pr_state == SONPROC)) && (!show_uid || pp->pr_uid == (uid_t) sel->uid)) { *prefp++ = pp; active_procs++; } if (ZOMBIE(pp)) process_states[sZOMB]++; /* invented */ } } /* if requested, sort the "interesting" processes */ qsort((char *) pref, active_procs, sizeof(struct prpsinfo *), proc_compares[idx]); /* 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); } /* * cpu percentage calculation is as fm ps.c * seems to be ratio of (sys+user time used)/(elapsed time) * i.e percent of cpu utilised when on cpu */ static double percent_cpu(struct prpsinfo * pp) { static time_t tim = 0L; time_t starttime; time_t ctime; time_t etime; /* if (tim == 0L) */ tim = time((time_t *) 0); starttime = pp->pr_start.tv_sec; if (pp->pr_start.tv_nsec > 500000000) starttime++; etime = (tim - starttime); ctime = pp->pr_time.tv_sec; if (pp->pr_time.tv_nsec > 500000000) ctime++; if (etime) { /* return (float)(ctime * 100) / (unsigned)etime; */ /* * this was ocasionally giving vals >100 for some unknown reason so * the below normalises it */ double pct; pct = (float) (ctime * 100) / (unsigned) etime; return (pct < 100.0) ? pct : 100.00; } return 0.00; } 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 */ (void) sprintf(fmt, Proc_format, pp->pr_pid, (*get_userid) (pp->pr_uid), pp->pr_pri, #ifdef SHOW_NICE pp->pr_nice, #else (u_short) pp->pr_nlwp < 999 ? (u_short) pp->pr_nlwp : 999, #endif format_k(SIZE_K(pp)), format_k(RSS_K(pp)), (ZOMBIE(pp)) ? state_abbrev[sZOMB] : state_abbrev[pp->pr_state], format_time(cputime), /* 100.0 * */ pctcpu, printable(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_value == 0) { /* this one wasn't found */ (void) 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 (lseek(kmem, (long) offset, 0) == -1) { if (*refstr == '!') refstr++; (void) fprintf(stderr, "%s: lseek to %s: %s\n", myname, refstr, sys_errlist[errno]); quit(22); } if (read(kmem, (char *) ptr, size) == -1) if (*refstr == '!') /* we lost the race with the kernel, process isn't in memory */ return (0); else { (void) fprintf(stderr, "%s: reading %s: %s\n", myname, refstr, sys_errlist[errno]); quit(23); } return (1); } /* ----------------- comparison routines for qsort ---------------- */ /* First, the possible comparison keys. 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 = (long) (sorted_state[p2->pr_state] - \ sorted_state[p1->pr_state])) == 0) #define ORDERKEY_PRIO if ((result = p2->pr_pri - p1->pr_pri) == 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_PID if ((result = (p2->pr_pid - p1->pr_pid)) == 0) #define ORDERKEY_UID if ((result = (p2->pr_uid - p1->pr_uid)) == 0) #define ORDERKEY_RPID if ((result = (p1->pr_pid - p2->pr_pid)) == 0) #define ORDERKEY_RUID if ((result = (p1->pr_uid - p2->pr_uid)) == 0) /* states enum {SONPROC, SRUN, SSLEEP, SSTOP, SIDL} */ unsigned char sorted_state[] = { 7, /* onproc */ 6, /* run */ 5, /* sleep */ 4, /* stop */ 3, /* idle */ 2, /* zombie */ 0, /* unused */ 0 /* unused */ }; #if 0 /* * proc_compare - original singleton 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. */ /* default comparison rtn */ int original_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); result = dresult > 0.0 ? 1 : dresult < 0.0 ? -1 : 0; if (result) { /* 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_pri - p1->pr_pri) == 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); } #endif /* original comparison rtn */ /* compare_state - comparison function for sorting by state,pri,time,size */ 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; ORDERKEY_STATE ORDERKEY_PRIO ORDERKEY_CPTICKS ORDERKEY_RSSIZE ORDERKEY_MEM ORDERKEY_PCTCPU ; return (result); } /* compare_cpu - the comparison function for sorting by cpu % (deflt) */ int compare_cpu( 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; ORDERKEY_PCTCPU ORDERKEY_CPTICKS ORDERKEY_STATE ORDERKEY_PRIO ORDERKEY_RSSIZE ORDERKEY_MEM ; return (result); } /* compare_size - the comparison function for sorting by total memory usage */ int compare_size( 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; ORDERKEY_MEM ORDERKEY_RSSIZE ORDERKEY_PCTCPU ORDERKEY_CPTICKS ORDERKEY_STATE ORDERKEY_PRIO ; return (result); } /* compare_res - the comparison function for sorting by resident set size */ int compare_res( 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; ORDERKEY_RSSIZE ORDERKEY_MEM ORDERKEY_PCTCPU ORDERKEY_CPTICKS ORDERKEY_STATE ORDERKEY_PRIO ; return (result); } /* compare_time - the comparison function for sorting by total cpu time */ int compare_time( 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; ORDERKEY_CPTICKS ORDERKEY_PCTCPU ORDERKEY_STATE ORDERKEY_PRIO ORDERKEY_MEM ORDERKEY_RSSIZE ; return (result); } /* compare_pid - the comparison function for sorting by pid */ int compare_pid( 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; ORDERKEY_PID ORDERKEY_CPTICKS ORDERKEY_PCTCPU ORDERKEY_STATE ORDERKEY_PRIO ORDERKEY_MEM ORDERKEY_RSSIZE ; return (result); } /* compare_uid - the comparison function for sorting by user ID */ int compare_uid( 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; ORDERKEY_UID ORDERKEY_CPTICKS ORDERKEY_PCTCPU ORDERKEY_STATE ORDERKEY_PRIO ORDERKEY_MEM ORDERKEY_RSSIZE ; return (result); } /* compare_rpid - the comparison function for sorting by pid ascending */ int compare_rpid( 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; ORDERKEY_RPID ORDERKEY_CPTICKS ORDERKEY_PCTCPU ORDERKEY_STATE ORDERKEY_PRIO ORDERKEY_MEM ORDERKEY_RSSIZE ; return (result); } /* compare_uid - the comparison function for sorting by user ID ascending */ int compare_ruid( 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; ORDERKEY_RUID ORDERKEY_CPTICKS ORDERKEY_PCTCPU ORDERKEY_STATE ORDERKEY_PRIO ORDERKEY_MEM ORDERKEY_RSSIZE ; return (result); } /* ---------------- helper rtns ---------------- */ /* * get process table */ void getptable(struct prpsinfo * baseptr) { struct prpsinfo *currproc; /* pointer to current proc structure */ int numprocs = 0; struct dirent *direntp; currproc = baseptr; for (rewinddir(procdir); direntp = readdir(procdir);) { int fd; char buf[30]; sprintf(buf, "%s/psinfo", direntp->d_name); if ((fd = open(buf, O_RDONLY)) < 0) continue; if (read(fd, currproc, sizeof(psinfo_t)) != sizeof(psinfo_t)) { (void) close(fd); continue; } numprocs++; currproc++; (void) close(fd); /* Atypical place for growth */ if (numprocs >= maxprocs) { reallocproc(2 * numprocs); currproc = (struct prpsinfo *) ((char *) baseptr + sizeof(psinfo_t) * numprocs); } } if (nproc != numprocs) nproc = numprocs; } /* return the owner of the specified process, for use in commands.c as we're running setuid root */ int proc_owner(int pid) { register struct prpsinfo *p; int i; for (i = 0, p = pbase; i < nproc; i++, p++) if (p->pr_pid == (pid_t) pid) return ((int) (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); } get_swapinfo(long *total, long *fr) { register int cnt, i; register long t, f; struct swaptable *swt; struct swapent *ste; static char path[256]; /* get total number of swap entries */ cnt = swapctl(SC_GETNSWP, 0); /* allocate enough space to hold count + n swapents */ swt = (struct swaptable *) malloc(sizeof(int) + cnt * sizeof(struct swapent)); if (swt == NULL) { *total = 0; *fr = 0; return; } swt->swt_n = cnt; /* * fill in ste_path pointers: we don't care about the paths, so we point * them all to the same buffer */ ste = &(swt->swt_ent[0]); i = cnt; while (--i >= 0) { ste++->ste_path = path; } /* grab all swap info */ swapctl(SC_LIST, swt); /* walk thru the structs and sum up the fields */ t = f = 0; ste = &(swt->swt_ent[0]); i = cnt; while (--i >= 0) { /* dont count slots being deleted */ if (!(ste->ste_flags & ST_INDEL)) { t += ste->ste_pages; f += ste->ste_free; } ste++; } /* fill in the results */ *total = t; *fr = f; free(swt); } /* * When we reach a proc limit, we need to realloc the stuff. */ static void reallocproc(int n) { int bytes; struct oldproc *op, *endbase; if (n < maxprocs) return; maxprocs = n; /* allocate space for proc structure array and array of pointers */ bytes = maxprocs * sizeof(psinfo_t); pbase = (struct prpsinfo *) realloc(pbase, bytes); pref = (struct prpsinfo **) realloc(pref, maxprocs * sizeof(struct prpsinfo *)); /* Just in case ... */ if (pbase == (struct prpsinfo *) NULL || pref == (struct prpsinfo **) NULL) { fprintf(stderr, "%s: can't allocate sufficient memory\n", myname); quit(1); } } /* ---------------------------------------------------------------- */ /* Access kernel Metrics * SVR5 uses metreg inteface to Kernel statistics (metrics) * see /usr/include/mas.h, /usr/include/metreg.h */ #include #include #include #include static int md; /* metric descriptor handle */ static uint32 ncpu; /* number of processors in system */ /* fwd dcls */ static uint32 kmet_get_cpu(int type, char *desc); static void kmet_verify( uint32 md, metid_t id, units_t units, type_t mettype, uint32 metsz, uint32 nobj, uint32 nlocs, resource_t res_id, uint32 ressz); static int get_cpustates(int *new) { new[0] = (int) kmet_get_cpu(MPC_CPU_IDLE, "idle"); new[1] = (int) kmet_get_cpu(MPC_CPU_USR, "usr"); new[2] = (int) kmet_get_cpu(MPC_CPU_SYS, "sys"); new[3] = (int) kmet_get_cpu(MPC_CPU_WIO, "wio"); } /* initialises kernel metrics access and gets #cpus */ static int kmet_init() { uint32 *ncpu_p; /* open (and map in) the metric access file and assoc data structures */ if ((md = mas_open(MAS_FILE, MAS_MMAP_ACCESS)) < 0) { (void) fprintf(stderr, "mas_open failed\n"); mas_perror(); quit(10); } /* verify the NCPU metric is everything we expect */ kmet_verify(md, NCPU, CPUS, CONFIGURABLE, sizeof(short), 1, 1, MAS_SYSTEM, sizeof(uint32)); /* get the number of cpu's on the system */ if ((ncpu_p = (uint32 *) mas_get_met(md, NCPU, 0)) == NULL) { (void) fprintf(stderr, "mas_get_met of ncpu failed\n"); mas_perror(); quit(12); } ncpu = (uint32) (*(short *) ncpu_p); /* * check that MPC_CPU_IDLE is of the form we expect ( paranoically we * should check the rest as well but ... ) */ kmet_verify(md, MPC_CPU_IDLE, TIX, PROFILE, sizeof(uint32), 1, ncpu, NCPU, sizeof(short)); kmet_verify(md, PROCUSE, PROCESSES, COUNT, sizeof(uint32), 1, 1, MAS_SYSTEM, sizeof(uint32)); nproc = kmet_get_nproc(); return 0; } /* done with kernel metrics access */ static int kmet_done() { if (mas_close(md) < 0) { (void) fprintf(stderr, "mas_close failed\n"); mas_perror(); quit(14); } } static uint32 kmet_get_cpu(int type, char *desc) { int i; uint32 r = 0, rtot = 0; for (i = 0; i < ncpu; i++) { r = *(uint32 *) mas_get_met(md, (metid_t) type, 0); if (!r) { (void) fprintf(stderr, "mas_get_met of %s failed\n", desc); mas_perror(); quit(12); } rtot += r; /* sum them for multi cpus */ } return rtot /* /ncpu */ ; } static int kmet_get_freemem() { dl_t *fm_p, fm, fmc, denom; time_t td1; static time_t td0; static dl_t fm_old; td1 = time(NULL); if ((fm_p = (dl_t *) mas_get_met(md, FREEMEM, 0)) == NULL) { (void) fprintf(stderr, "mas_get_met of freemem failed\n"); mas_perror(); quit(12); } fm = *fm_p; denom.dl_hop = 0; denom.dl_lop = (long) (td1 - td0); td0 = td1; /* * calculate the freemem difference divided by the time diff giving the * freemem in that time sample (new - old) / (time_between_samples) */ fmc = lsub(fm, fm_old); fm_old = fm; fmc = ldivide(fmc, denom); return fmc.dl_lop; } /* * return # of processes currently executing on system */ static int kmet_get_nproc() { uint32 *p; if ((p = (uint32 *) mas_get_met(md, PROCUSE, 0)) == NULL) { (void) fprintf(stderr, "mas_get_met of procuse failed\n"); mas_perror(); quit(11); } nproc = (int) *p; } /* * Function: kmet_verify * renamed from mas_usrtime example verify_met() fm Doug Souders * * Description: Verify the registration data associated with this metric * match what are expected. Cautious consumer applications * should do this sort of verification before using metrics. */ static void kmet_verify( uint32 md, /* metric descriptor */ metid_t id, /* metric id number */ units_t units, /* expected units of metric */ type_t mettype, /* expected type of metric */ uint32 metsz, /* expected object size of metric */ uint32 nobj, /* expected number of array elements */ uint32 nlocs, /* expected number of instances */ resource_t res_id, /* expected resource id number */ uint32 ressz /* expected resource object size */ ) { char *name; /* the name of the metric */ units_t *units_p; /* the units of the metric */ type_t *mettype_p; /* type field of the metric */ uint32 *objsz_p; /* size of each element in met */ uint32 *nobj_p; /* num of elements >1 then array */ uint32 *nlocs_p; /* total number of instances */ uint32 *status_p; /* status word (update|avail) */ resource_t *resource_p; /* the resource list of the met */ uint32 *resval_p; /* pointer to resource */ uint32 *ressz_p; /* size of the resource met */ if (!(name = mas_get_met_name(md, id))) { (void) fprintf(stderr, "mas_get_met_name failed\n"); mas_perror(); quit(11); } if (!(status_p = mas_get_met_status(md, id))) { (void) fprintf(stderr, "mas_get_met_status of %s failed\n", name); mas_perror(); quit(11); } if (*status_p != MAS_AVAILABLE) { (void) fprintf(stderr, "unexpected status word for %s\n" "- expected %u got %u\n", name, MAS_AVAILABLE, *status_p); quit(11); } if (!(units_p = mas_get_met_units(md, id))) { (void) fprintf(stderr, "mas_get_met_units of %s failed\n", name); mas_perror(); quit(11); } if (units != *units_p) { (void) fprintf(stderr, "unexpected units for %s\n" "- expected %u got %u\n", name, units, *units_p); quit(11); } if (!(mettype_p = mas_get_met_type(md, id))) { (void) fprintf(stderr, "mas_get_met_type of %s failed\n", name); mas_perror(); quit(11); } if (mettype != *mettype_p) { (void) fprintf(stderr, "unexpected metric type for %s\n" "- expected %u got %u\n", name, mettype, *mettype_p); quit(11); } if (!(objsz_p = mas_get_met_objsz(md, id))) { (void) fprintf(stderr, "mas_get_met_objsz of %s failed\n", name); mas_perror(); quit(11); } if (*objsz_p != metsz) { (void) fprintf(stderr, "unexpected object size for %s\n" "- expected %u got %u\n", name, metsz, *objsz_p); quit(11); } if (!(nobj_p = mas_get_met_nobj(md, id))) { (void) fprintf(stderr, "mas_get_met_nobj of %s failed\n", name); mas_perror(); quit(11); } if (nobj != *nobj_p) { (void) fprintf(stderr, "unexpected number of objects for %s\n" "- expected %u got %u\n", name, nobj, *nobj_p); quit(11); } /* get the number of instances that libmas thinks it knows about */ if (!(nlocs_p = mas_get_met_nlocs(md, id))) { (void) fprintf(stderr, "mas_get_met_nlocs of %s failed\n", name); mas_perror(); quit(11); } if (nlocs != *nlocs_p) { (void) fprintf(stderr, "unexpected number of instances for %s" " - expected %u got %u\n", name, nlocs, *nlocs_p); quit(11); } /* get the resource list for the metric */ if (!(resource_p = mas_get_met_resources(md, id))) { (void) fprintf(stderr, "mas_get_met_resources of %s failed\n", name); mas_perror(); quit(11); } if (*resource_p != res_id) { (void) fprintf(stderr, "unexpected resource id for %s\n" "- expected %u got %u\n", name, res_id, *resource_p); quit(11); } /* get the size of the resource */ if (!(ressz_p = mas_get_met_objsz(md, (metid_t) (*resource_p)))) { (void) fprintf(stderr, "mas_get_met_objsz of resource failed\n"); mas_perror(); quit(11); } if (*ressz_p != ressz) { (void) fprintf(stderr, "unexpected resource size for %s\n" "- expected %u got %u\n", name, ressz, *ressz_p); quit(11); } /* * get the address of the resource */ if (!(resval_p = (uint32 *) mas_get_met(md, *resource_p, 0))) { (void) fprintf(stderr, "mas_get_met of resource failed\n"); mas_perror(); quit(11); } if (ressz == sizeof(short)) { if ((uint32) (*(short *) resval_p) != nlocs) { (void) fprintf(stderr, "unexpected resource value for %s\n" "- expected %u got %u\n", name, nlocs, (uint32) (*(short *) resval_p)); quit(11); } } else { /* assume size of uint32 */ if (*resval_p != nlocs) { (void) fprintf(stderr, "unexpected resource value for %s\n" "- expected %u got %u\n", name, nlocs, *resval_p); quit(11); } } return; }