machine.c revision 27390
124143Sjoerg/* 224143Sjoerg * top - a top users display for Unix 324143Sjoerg * 424143Sjoerg * SYNOPSIS: For FreeBSD-2.x system 524143Sjoerg * 624143Sjoerg * DESCRIPTION: 724143Sjoerg * Originally written for BSD4.4 system by Christos Zoulas. 824143Sjoerg * Ported to FreeBSD 2.x by Steven Wallace && Wolfram Schneider 924143Sjoerg * 1024143Sjoerg * This is the machine-dependent module for FreeBSD 2.2 1124143Sjoerg * Works for: 1224143Sjoerg * FreeBSD 2.2, and probably FreeBSD 2.1.x 1324143Sjoerg * 1424143Sjoerg * LIBS: -lkvm 1524143Sjoerg * 1624143Sjoerg * AUTHOR: Christos Zoulas <christos@ee.cornell.edu> 1724143Sjoerg * Steven Wallace <swallace@freebsd.org> 1824143Sjoerg * Wolfram Schneider <wosch@FreeBSD.org> 1924143Sjoerg * 2027390Speter * $Id: machine.c,v 1.4 1997/07/12 10:51:54 peter Exp $ 2124143Sjoerg */ 2224143Sjoerg 2324143Sjoerg 2424143Sjoerg#include <sys/types.h> 2524143Sjoerg#include <sys/signal.h> 2624143Sjoerg#include <sys/param.h> 2724143Sjoerg 2824143Sjoerg#include "os.h" 2924143Sjoerg#include <stdio.h> 3024143Sjoerg#include <nlist.h> 3124143Sjoerg#include <math.h> 3224143Sjoerg#include <kvm.h> 3327390Speter#include <pwd.h> 3424143Sjoerg#include <sys/errno.h> 3524143Sjoerg#include <sys/sysctl.h> 3624143Sjoerg#include <sys/dkstat.h> 3724143Sjoerg#include <sys/file.h> 3824143Sjoerg#include <sys/time.h> 3924143Sjoerg#include <sys/proc.h> 4024143Sjoerg#include <sys/user.h> 4124143Sjoerg#include <sys/vmmeter.h> 4224143Sjoerg 4324143Sjoerg/* Swap */ 4424143Sjoerg#include <stdlib.h> 4524143Sjoerg#include <sys/rlist.h> 4624143Sjoerg#include <sys/conf.h> 4724143Sjoerg 4824143Sjoerg#include <osreldate.h> /* for changes in kernel structures */ 4924143Sjoerg 5024143Sjoerg#include "top.h" 5124143Sjoerg#include "machine.h" 5224143Sjoerg 5324143Sjoergstatic int check_nlist __P((struct nlist *)); 5424143Sjoergstatic int getkval __P((unsigned long, int *, int, char *)); 5524143Sjoergextern char* printable __P((char *)); 5624143Sjoergint swapmode __P((int *retavail, int *retfree)); 5727340Speterstatic int smpmode; 5827390Speterstatic int namelength; 5927390Speterstatic int cmdlength; 6024143Sjoerg 6124143Sjoerg 6224143Sjoerg/* get_process_info passes back a handle. This is what it looks like: */ 6324143Sjoerg 6424143Sjoergstruct handle 6524143Sjoerg{ 6624143Sjoerg struct kinfo_proc **next_proc; /* points to next valid proc pointer */ 6724143Sjoerg int remaining; /* number of pointers remaining */ 6824143Sjoerg}; 6924143Sjoerg 7024143Sjoerg/* declarations for load_avg */ 7124143Sjoerg#include "loadavg.h" 7224143Sjoerg 7324143Sjoerg#define PP(pp, field) ((pp)->kp_proc . field) 7424143Sjoerg#define EP(pp, field) ((pp)->kp_eproc . field) 7524143Sjoerg#define VP(pp, field) ((pp)->kp_eproc.e_vm . field) 7624143Sjoerg 7724143Sjoerg/* define what weighted cpu is. */ 7824143Sjoerg#define weighted_cpu(pct, pp) (PP((pp), p_swtime) == 0 ? 0.0 : \ 7924143Sjoerg ((pct) / (1.0 - exp(PP((pp), p_swtime) * logcpu)))) 8024143Sjoerg 8124143Sjoerg/* what we consider to be process size: */ 8224143Sjoerg#define PROCSIZE(pp) (VP((pp), vm_tsize) + VP((pp), vm_dsize) + VP((pp), vm_ssize)) 8324143Sjoerg 8424143Sjoerg/* definitions for indices in the nlist array */ 8524143Sjoerg 8624143Sjoerg 8724143Sjoergstatic struct nlist nlst[] = { 8824143Sjoerg#define X_CCPU 0 8924143Sjoerg { "_ccpu" }, /* 0 */ 9024143Sjoerg#define X_CP_TIME 1 9124143Sjoerg { "_cp_time" }, /* 1 */ 9224143Sjoerg#define X_HZ 2 9324143Sjoerg { "_hz" }, /* 2 */ 9424143Sjoerg#define X_STATHZ 3 9524143Sjoerg { "_stathz" }, /* 3 */ 9624143Sjoerg#define X_AVENRUN 4 9724143Sjoerg { "_averunnable" }, /* 4 */ 9824143Sjoerg 9924143Sjoerg/* Swap */ 10024143Sjoerg#define VM_SWAPLIST 5 10124143Sjoerg { "_swaplist" },/* list of free swap areas */ 10224143Sjoerg#define VM_SWDEVT 6 10324143Sjoerg { "_swdevt" }, /* list of swap devices and sizes */ 10424143Sjoerg#define VM_NSWAP 7 10524143Sjoerg { "_nswap" }, /* size of largest swap device */ 10624143Sjoerg#define VM_NSWDEV 8 10724143Sjoerg { "_nswdev" }, /* number of swap devices */ 10824143Sjoerg#define VM_DMMAX 9 10924143Sjoerg { "_dmmax" }, /* maximum size of a swap block */ 11024143Sjoerg#define X_BUFSPACE 10 11124143Sjoerg { "_bufspace" }, /* K in buffer cache */ 11224143Sjoerg#define X_CNT 11 11324143Sjoerg { "_cnt" }, /* struct vmmeter cnt */ 11424143Sjoerg 11524143Sjoerg/* Last pid */ 11624143Sjoerg#define X_LASTPID 12 11724143Sjoerg { "_nextpid" }, 11824143Sjoerg { 0 } 11924143Sjoerg}; 12024143Sjoerg 12124143Sjoerg/* 12224143Sjoerg * These definitions control the format of the per-process area 12324143Sjoerg */ 12424143Sjoerg 12527340Speterstatic char smp_header[] = 12627390Speter " PID %-*.*s PRI NICE SIZE RES STATE C TIME WCPU CPU COMMAND"; 12724143Sjoerg 12827340Speter#define smp_Proc_format \ 12927390Speter "%5d %-*.*s %3d %3d%7s %6s %-6.6s%1x%7s %5.2f%% %5.2f%% %.*s" 13024143Sjoerg 13127340Speterstatic char up_header[] = 13227390Speter " PID %-*.*s PRI NICE SIZE RES STATE TIME WCPU CPU COMMAND"; 13324143Sjoerg 13427340Speter#define up_Proc_format \ 13527390Speter "%5d %-*.*s %3d %3d%7s %6s %-6.6s%.0d%7s %5.2f%% %5.2f%% %.*s" 13624143Sjoerg 13724143Sjoerg 13824143Sjoerg 13924143Sjoerg/* process state names for the "STATE" column of the display */ 14024143Sjoerg/* the extra nulls in the string "run" are for adding a slash and 14124143Sjoerg the processor number when needed */ 14224143Sjoerg 14324143Sjoergchar *state_abbrev[] = 14424143Sjoerg{ 14524143Sjoerg "", "START", "RUN\0\0\0", "SLEEP", "STOP", "ZOMB", 14624143Sjoerg}; 14724143Sjoerg 14824143Sjoerg 14924143Sjoergstatic kvm_t *kd; 15024143Sjoerg 15124143Sjoerg/* values that we stash away in _init and use in later routines */ 15224143Sjoerg 15324143Sjoergstatic double logcpu; 15424143Sjoerg 15524143Sjoerg/* these are retrieved from the kernel in _init */ 15624143Sjoerg 15724143Sjoergstatic long hz; 15824143Sjoergstatic load_avg ccpu; 15924143Sjoerg 16024143Sjoerg/* these are offsets obtained via nlist and used in the get_ functions */ 16124143Sjoerg 16224143Sjoergstatic unsigned long cp_time_offset; 16324143Sjoergstatic unsigned long avenrun_offset; 16424143Sjoergstatic unsigned long lastpid_offset; 16524143Sjoergstatic long lastpid; 16624143Sjoergstatic unsigned long cnt_offset; 16724143Sjoergstatic unsigned long bufspace_offset; 16824143Sjoergstatic long cnt; 16924143Sjoerg 17024143Sjoerg/* these are for calculating cpu state percentages */ 17124143Sjoerg 17224143Sjoergstatic long cp_time[CPUSTATES]; 17324143Sjoergstatic long cp_old[CPUSTATES]; 17424143Sjoergstatic long cp_diff[CPUSTATES]; 17524143Sjoerg 17624143Sjoerg/* these are for detailing the process states */ 17724143Sjoerg 17824143Sjoergint process_states[6]; 17924143Sjoergchar *procstatenames[] = { 18024143Sjoerg "", " starting, ", " running, ", " sleeping, ", " stopped, ", 18124143Sjoerg " zombie, ", 18224143Sjoerg NULL 18324143Sjoerg}; 18424143Sjoerg 18524143Sjoerg/* these are for detailing the cpu states */ 18624143Sjoerg 18724143Sjoergint cpu_states[CPUSTATES]; 18824143Sjoergchar *cpustatenames[] = { 18924143Sjoerg "user", "nice", "system", "interrupt", "idle", NULL 19024143Sjoerg}; 19124143Sjoerg 19224143Sjoerg/* these are for detailing the memory statistics */ 19324143Sjoerg 19424143Sjoergint memory_stats[7]; 19524143Sjoergchar *memorynames[] = { 19624143Sjoerg "K Active, ", "K Inact, ", "K Wired, ", "K Cache, ", "K Buf, ", "K Free", 19724143Sjoerg NULL 19824143Sjoerg}; 19924143Sjoerg 20024143Sjoergint swap_stats[7]; 20124143Sjoergchar *swapnames[] = { 20224143Sjoerg/* 0 1 2 3 4 5 */ 20324143Sjoerg "K Total, ", "K Used, ", "K Free, ", "% Inuse, ", "K In, ", "K Out", 20424143Sjoerg NULL 20524143Sjoerg}; 20624143Sjoerg 20724143Sjoerg 20824143Sjoerg/* these are for keeping track of the proc array */ 20924143Sjoerg 21024143Sjoergstatic int nproc; 21124143Sjoergstatic int onproc = -1; 21224143Sjoergstatic int pref_len; 21324143Sjoergstatic struct kinfo_proc *pbase; 21424143Sjoergstatic struct kinfo_proc **pref; 21524143Sjoerg 21624143Sjoerg/* these are for getting the memory statistics */ 21724143Sjoerg 21824143Sjoergstatic int pageshift; /* log base 2 of the pagesize */ 21924143Sjoerg 22024143Sjoerg/* define pagetok in terms of pageshift */ 22124143Sjoerg 22224143Sjoerg#define pagetok(size) ((size) << pageshift) 22324143Sjoerg 22424143Sjoerg/* useful externals */ 22524143Sjoerglong percentages(); 22624143Sjoerg 22724143Sjoergint 22824143Sjoergmachine_init(statics) 22924143Sjoerg 23024143Sjoergstruct statics *statics; 23124143Sjoerg 23224143Sjoerg{ 23324143Sjoerg register int i = 0; 23424143Sjoerg register int pagesize; 23527340Speter int modelen; 23627390Speter struct passwd *pw; 23724143Sjoerg 23827340Speter modelen = sizeof(smpmode); 23927340Speter if (sysctlbyname("kern.smp_active", &smpmode, &modelen, NULL, 0) < 0 || 24027340Speter modelen != sizeof(smpmode)) 24127340Speter smpmode = 0; 24227340Speter 24327390Speter while ((pw = getpwent()) != NULL) { 24427390Speter if (strlen(pw->pw_name) > namelength) 24527390Speter namelength = strlen(pw->pw_name); 24627390Speter } 24727390Speter if (namelength < 8) 24827390Speter namelength = 8; 24927390Speter if (namelength > 16) 25027390Speter namelength = 16; 25127390Speter 25224143Sjoerg if ((kd = kvm_open(NULL, NULL, NULL, O_RDONLY, "kvm_open")) == NULL) 25324143Sjoerg return -1; 25424143Sjoerg 25524143Sjoerg 25624143Sjoerg /* get the list of symbols we want to access in the kernel */ 25724143Sjoerg (void) kvm_nlist(kd, nlst); 25824143Sjoerg if (nlst[0].n_type == 0) 25924143Sjoerg { 26024143Sjoerg fprintf(stderr, "top: nlist failed\n"); 26124143Sjoerg return(-1); 26224143Sjoerg } 26324143Sjoerg 26424143Sjoerg /* make sure they were all found */ 26524143Sjoerg if (i > 0 && check_nlist(nlst) > 0) 26624143Sjoerg { 26724143Sjoerg return(-1); 26824143Sjoerg } 26924143Sjoerg 27024143Sjoerg /* get the symbol values out of kmem */ 27124143Sjoerg (void) getkval(nlst[X_STATHZ].n_value, (int *)(&hz), sizeof(hz), "!"); 27224143Sjoerg if (!hz) { 27324143Sjoerg (void) getkval(nlst[X_HZ].n_value, (int *)(&hz), sizeof(hz), 27424143Sjoerg nlst[X_HZ].n_name); 27524143Sjoerg } 27624143Sjoerg 27724143Sjoerg (void) getkval(nlst[X_CCPU].n_value, (int *)(&ccpu), sizeof(ccpu), 27824143Sjoerg nlst[X_CCPU].n_name); 27924143Sjoerg 28024143Sjoerg /* stash away certain offsets for later use */ 28124143Sjoerg cp_time_offset = nlst[X_CP_TIME].n_value; 28224143Sjoerg avenrun_offset = nlst[X_AVENRUN].n_value; 28324143Sjoerg lastpid_offset = nlst[X_LASTPID].n_value; 28424143Sjoerg cnt_offset = nlst[X_CNT].n_value; 28524143Sjoerg bufspace_offset = nlst[X_BUFSPACE].n_value; 28624143Sjoerg 28724143Sjoerg /* this is used in calculating WCPU -- calculate it ahead of time */ 28824143Sjoerg logcpu = log(loaddouble(ccpu)); 28924143Sjoerg 29024143Sjoerg pbase = NULL; 29124143Sjoerg pref = NULL; 29224143Sjoerg nproc = 0; 29324143Sjoerg onproc = -1; 29424143Sjoerg /* get the page size with "getpagesize" and calculate pageshift from it */ 29524143Sjoerg pagesize = getpagesize(); 29624143Sjoerg pageshift = 0; 29724143Sjoerg while (pagesize > 1) 29824143Sjoerg { 29924143Sjoerg pageshift++; 30024143Sjoerg pagesize >>= 1; 30124143Sjoerg } 30224143Sjoerg 30324143Sjoerg /* we only need the amount of log(2)1024 for our conversion */ 30424143Sjoerg pageshift -= LOG1024; 30524143Sjoerg 30624143Sjoerg /* fill in the statics information */ 30724143Sjoerg statics->procstate_names = procstatenames; 30824143Sjoerg statics->cpustate_names = cpustatenames; 30924143Sjoerg statics->memory_names = memorynames; 31024143Sjoerg statics->swap_names = swapnames; 31124143Sjoerg 31224143Sjoerg /* all done! */ 31324143Sjoerg return(0); 31424143Sjoerg} 31524143Sjoerg 31624143Sjoergchar *format_header(uname_field) 31724143Sjoerg 31824143Sjoergregister char *uname_field; 31924143Sjoerg 32024143Sjoerg{ 32124143Sjoerg register char *ptr; 32227390Speter static char Header[128]; 32324143Sjoerg 32427390Speter snprintf(Header, sizeof(Header), smpmode ? smp_header : up_header, 32527390Speter namelength, namelength, uname_field); 32627340Speter 32727390Speter cmdlength = 80 - strlen(Header) + 6; 32824143Sjoerg 32927390Speter return Header; 33024143Sjoerg} 33124143Sjoerg 33224143Sjoergstatic int swappgsin = -1; 33324143Sjoergstatic int swappgsout = -1; 33424143Sjoergextern struct timeval timeout; 33524143Sjoerg 33624143Sjoergvoid 33724143Sjoergget_system_info(si) 33824143Sjoerg 33924143Sjoergstruct system_info *si; 34024143Sjoerg 34124143Sjoerg{ 34224143Sjoerg long total; 34324143Sjoerg load_avg avenrun[3]; 34424143Sjoerg 34524143Sjoerg /* get the cp_time array */ 34624143Sjoerg (void) getkval(cp_time_offset, (int *)cp_time, sizeof(cp_time), 34724143Sjoerg nlst[X_CP_TIME].n_name); 34824143Sjoerg (void) getkval(avenrun_offset, (int *)avenrun, sizeof(avenrun), 34924143Sjoerg nlst[X_AVENRUN].n_name); 35024143Sjoerg 35124143Sjoerg (void) getkval(lastpid_offset, (int *)(&lastpid), sizeof(lastpid), 35224143Sjoerg "!"); 35324143Sjoerg 35424143Sjoerg /* convert load averages to doubles */ 35524143Sjoerg { 35624143Sjoerg register int i; 35724143Sjoerg register double *infoloadp; 35824143Sjoerg load_avg *avenrunp; 35924143Sjoerg 36024143Sjoerg#ifdef notyet 36124143Sjoerg struct loadavg sysload; 36224143Sjoerg int size; 36324143Sjoerg getkerninfo(KINFO_LOADAVG, &sysload, &size, 0); 36424143Sjoerg#endif 36524143Sjoerg 36624143Sjoerg infoloadp = si->load_avg; 36724143Sjoerg avenrunp = avenrun; 36824143Sjoerg for (i = 0; i < 3; i++) 36924143Sjoerg { 37024143Sjoerg#ifdef notyet 37124143Sjoerg *infoloadp++ = ((double) sysload.ldavg[i]) / sysload.fscale; 37224143Sjoerg#endif 37324143Sjoerg *infoloadp++ = loaddouble(*avenrunp++); 37424143Sjoerg } 37524143Sjoerg } 37624143Sjoerg 37724143Sjoerg /* convert cp_time counts to percentages */ 37824143Sjoerg total = percentages(CPUSTATES, cpu_states, cp_time, cp_old, cp_diff); 37924143Sjoerg 38024143Sjoerg /* sum memory & swap statistics */ 38124143Sjoerg { 38224143Sjoerg struct vmmeter sum; 38324143Sjoerg static unsigned int swap_delay = 0; 38424143Sjoerg static int swapavail = 0; 38524143Sjoerg static int swapfree = 0; 38624143Sjoerg static int bufspace = 0; 38724143Sjoerg 38824143Sjoerg (void) getkval(cnt_offset, (int *)(&sum), sizeof(sum), 38924143Sjoerg "_cnt"); 39024143Sjoerg (void) getkval(bufspace_offset, (int *)(&bufspace), sizeof(bufspace), 39124143Sjoerg "_bufspace"); 39224143Sjoerg 39324143Sjoerg /* convert memory stats to Kbytes */ 39424143Sjoerg memory_stats[0] = pagetok(sum.v_active_count); 39524143Sjoerg memory_stats[1] = pagetok(sum.v_inactive_count); 39624143Sjoerg memory_stats[2] = pagetok(sum.v_wire_count); 39724143Sjoerg memory_stats[3] = pagetok(sum.v_cache_count); 39824143Sjoerg memory_stats[4] = bufspace / 1024; 39924143Sjoerg memory_stats[5] = pagetok(sum.v_free_count); 40024143Sjoerg memory_stats[6] = -1; 40124143Sjoerg 40224143Sjoerg /* first interval */ 40324143Sjoerg if (swappgsin < 0) { 40424143Sjoerg swap_stats[4] = 0; 40524143Sjoerg swap_stats[5] = 0; 40624143Sjoerg } 40724143Sjoerg 40824143Sjoerg /* compute differences between old and new swap statistic */ 40924143Sjoerg else { 41024143Sjoerg swap_stats[4] = pagetok(((sum.v_swappgsin - swappgsin))); 41124143Sjoerg swap_stats[5] = pagetok(((sum.v_swappgsout - swappgsout))); 41224143Sjoerg } 41324143Sjoerg 41424143Sjoerg swappgsin = sum.v_swappgsin; 41524143Sjoerg swappgsout = sum.v_swappgsout; 41624143Sjoerg 41724143Sjoerg /* call CPU heavy swapmode() only for changes */ 41824143Sjoerg if (swap_stats[4] > 0 || swap_stats[5] > 0 || swap_delay == 0) { 41924143Sjoerg swap_stats[3] = swapmode(&swapavail, &swapfree); 42024143Sjoerg swap_stats[0] = swapavail; 42124143Sjoerg swap_stats[1] = swapavail - swapfree; 42224143Sjoerg swap_stats[2] = swapfree; 42324143Sjoerg } 42424143Sjoerg swap_delay = 1; 42524143Sjoerg swap_stats[6] = -1; 42624143Sjoerg } 42724143Sjoerg 42824143Sjoerg /* set arrays and strings */ 42924143Sjoerg si->cpustates = cpu_states; 43024143Sjoerg si->memory = memory_stats; 43124143Sjoerg si->swap = swap_stats; 43224143Sjoerg 43324143Sjoerg 43424143Sjoerg if(lastpid > 0) { 43524143Sjoerg si->last_pid = lastpid; 43624143Sjoerg } else { 43724143Sjoerg si->last_pid = -1; 43824143Sjoerg } 43924143Sjoerg} 44024143Sjoerg 44124143Sjoergstatic struct handle handle; 44224143Sjoerg 44324143Sjoergcaddr_t get_process_info(si, sel, compare) 44424143Sjoerg 44524143Sjoergstruct system_info *si; 44624143Sjoergstruct process_select *sel; 44724143Sjoergint (*compare)(); 44824143Sjoerg 44924143Sjoerg{ 45024143Sjoerg register int i; 45124143Sjoerg register int total_procs; 45224143Sjoerg register int active_procs; 45324143Sjoerg register struct kinfo_proc **prefp; 45424143Sjoerg register struct kinfo_proc *pp; 45524143Sjoerg 45624143Sjoerg /* these are copied out of sel for speed */ 45724143Sjoerg int show_idle; 45824143Sjoerg int show_system; 45924143Sjoerg int show_uid; 46024143Sjoerg int show_command; 46124143Sjoerg 46224143Sjoerg 46324143Sjoerg pbase = kvm_getprocs(kd, KERN_PROC_ALL, 0, &nproc); 46424143Sjoerg if (nproc > onproc) 46524143Sjoerg pref = (struct kinfo_proc **) realloc(pref, sizeof(struct kinfo_proc *) 46624143Sjoerg * (onproc = nproc)); 46724143Sjoerg if (pref == NULL || pbase == NULL) { 46824143Sjoerg (void) fprintf(stderr, "top: Out of memory.\n"); 46924143Sjoerg quit(23); 47024143Sjoerg } 47124143Sjoerg /* get a pointer to the states summary array */ 47224143Sjoerg si->procstates = process_states; 47324143Sjoerg 47424143Sjoerg /* set up flags which define what we are going to select */ 47524143Sjoerg show_idle = sel->idle; 47624143Sjoerg show_system = sel->system; 47724143Sjoerg show_uid = sel->uid != -1; 47824143Sjoerg show_command = sel->command != NULL; 47924143Sjoerg 48024143Sjoerg /* count up process states and get pointers to interesting procs */ 48124143Sjoerg total_procs = 0; 48224143Sjoerg active_procs = 0; 48324143Sjoerg memset((char *)process_states, 0, sizeof(process_states)); 48424143Sjoerg prefp = pref; 48524143Sjoerg for (pp = pbase, i = 0; i < nproc; pp++, i++) 48624143Sjoerg { 48724143Sjoerg /* 48824143Sjoerg * Place pointers to each valid proc structure in pref[]. 48924143Sjoerg * Process slots that are actually in use have a non-zero 49024143Sjoerg * status field. Processes with P_SYSTEM set are system 49124143Sjoerg * processes---these get ignored unless show_sysprocs is set. 49224143Sjoerg */ 49324143Sjoerg if (PP(pp, p_stat) != 0 && 49424143Sjoerg (show_system || ((PP(pp, p_flag) & P_SYSTEM) == 0))) 49524143Sjoerg { 49624143Sjoerg total_procs++; 49724143Sjoerg process_states[(unsigned char) PP(pp, p_stat)]++; 49824143Sjoerg if ((PP(pp, p_stat) != SZOMB) && 49924143Sjoerg (show_idle || (PP(pp, p_pctcpu) != 0) || 50024143Sjoerg (PP(pp, p_stat) == SRUN)) && 50124143Sjoerg (!show_uid || EP(pp, e_pcred.p_ruid) == (uid_t)sel->uid)) 50224143Sjoerg { 50324143Sjoerg *prefp++ = pp; 50424143Sjoerg active_procs++; 50524143Sjoerg } 50624143Sjoerg } 50724143Sjoerg } 50824143Sjoerg 50924143Sjoerg /* if requested, sort the "interesting" processes */ 51024143Sjoerg if (compare != NULL) 51124143Sjoerg { 51224143Sjoerg qsort((char *)pref, active_procs, sizeof(struct kinfo_proc *), compare); 51324143Sjoerg } 51424143Sjoerg 51524143Sjoerg /* remember active and total counts */ 51624143Sjoerg si->p_total = total_procs; 51724143Sjoerg si->p_active = pref_len = active_procs; 51824143Sjoerg 51924143Sjoerg /* pass back a handle */ 52024143Sjoerg handle.next_proc = pref; 52124143Sjoerg handle.remaining = active_procs; 52224143Sjoerg return((caddr_t)&handle); 52324143Sjoerg} 52424143Sjoerg 52524143Sjoergchar fmt[128]; /* static area where result is built */ 52624143Sjoerg 52724143Sjoergchar *format_next_process(handle, get_userid) 52824143Sjoerg 52924143Sjoergcaddr_t handle; 53024143Sjoergchar *(*get_userid)(); 53124143Sjoerg 53224143Sjoerg{ 53324143Sjoerg register struct kinfo_proc *pp; 53424143Sjoerg register long cputime; 53524143Sjoerg register double pct; 53624143Sjoerg struct handle *hp; 53724143Sjoerg char status[16]; 53824143Sjoerg 53924143Sjoerg /* find and remember the next proc structure */ 54024143Sjoerg hp = (struct handle *)handle; 54124143Sjoerg pp = *(hp->next_proc++); 54224143Sjoerg hp->remaining--; 54324143Sjoerg 54424143Sjoerg 54524143Sjoerg /* get the process's user struct and set cputime */ 54624143Sjoerg if ((PP(pp, p_flag) & P_INMEM) == 0) { 54724143Sjoerg /* 54824143Sjoerg * Print swapped processes as <pname> 54924143Sjoerg */ 55024143Sjoerg char *comm = PP(pp, p_comm); 55124143Sjoerg#define COMSIZ sizeof(PP(pp, p_comm)) 55224143Sjoerg char buf[COMSIZ]; 55324143Sjoerg (void) strncpy(buf, comm, COMSIZ); 55424143Sjoerg comm[0] = '<'; 55524143Sjoerg (void) strncpy(&comm[1], buf, COMSIZ - 2); 55624143Sjoerg comm[COMSIZ - 2] = '\0'; 55724143Sjoerg (void) strncat(comm, ">", COMSIZ - 1); 55824143Sjoerg comm[COMSIZ - 1] = '\0'; 55924143Sjoerg } 56024143Sjoerg 56124143Sjoerg#if 0 56224143Sjoerg /* This does not produce the correct results */ 56324143Sjoerg cputime = PP(pp, p_uticks) + PP(pp, p_sticks) + PP(pp, p_iticks); 56424143Sjoerg#endif 56524143Sjoerg cputime = PP(pp, p_rtime).tv_sec; /* This does not count interrupts */ 56624143Sjoerg 56724143Sjoerg /* calculate the base for cpu percentages */ 56824143Sjoerg pct = pctdouble(PP(pp, p_pctcpu)); 56924143Sjoerg 57024143Sjoerg /* generate "STATE" field */ 57124143Sjoerg switch (PP(pp, p_stat)) { 57224143Sjoerg case SRUN: 57327340Speter if (smpmode && PP(pp, p_oncpu) >= 0) 57424143Sjoerg sprintf(status, "CPU%d", PP(pp, p_oncpu)); 57524143Sjoerg else 57624143Sjoerg strcpy(status, "RUN"); 57724143Sjoerg break; 57824143Sjoerg case SSLEEP: 57924143Sjoerg if (PP(pp, p_wmesg) != NULL) { 58024143Sjoerg sprintf(status, "%.6s", EP(pp, e_wmesg)); 58124143Sjoerg break; 58224143Sjoerg } 58324143Sjoerg /* fall through */ 58424143Sjoerg default: 58524143Sjoerg sprintf(status, "%.6s", state_abbrev[(unsigned char) PP(pp, p_stat)]); 58624143Sjoerg break; 58724143Sjoerg } 58824143Sjoerg 58924143Sjoerg /* format this entry */ 59024143Sjoerg sprintf(fmt, 59127340Speter smpmode ? smp_Proc_format : up_Proc_format, 59224143Sjoerg PP(pp, p_pid), 59327390Speter namelength, namelength, 59424143Sjoerg (*get_userid)(EP(pp, e_pcred.p_ruid)), 59524143Sjoerg PP(pp, p_priority) - PZERO, 59624143Sjoerg PP(pp, p_nice) - NZERO, 59724143Sjoerg format_k2(pagetok(PROCSIZE(pp))), 59824143Sjoerg format_k2(pagetok(VP(pp, vm_rssize))), 59924143Sjoerg status, 60027340Speter smpmode ? PP(pp, p_lastcpu) : 0, 60124143Sjoerg format_time(cputime), 60224143Sjoerg 10000.0 * weighted_cpu(pct, pp) / hz, 60324143Sjoerg 10000.0 * pct / hz, 60427390Speter cmdlength, 60524143Sjoerg printable(PP(pp, p_comm))); 60624143Sjoerg 60724143Sjoerg /* return the result */ 60824143Sjoerg return(fmt); 60924143Sjoerg} 61024143Sjoerg 61124143Sjoerg 61224143Sjoerg/* 61324143Sjoerg * check_nlist(nlst) - checks the nlist to see if any symbols were not 61424143Sjoerg * found. For every symbol that was not found, a one-line 61524143Sjoerg * message is printed to stderr. The routine returns the 61624143Sjoerg * number of symbols NOT found. 61724143Sjoerg */ 61824143Sjoerg 61924143Sjoergstatic int check_nlist(nlst) 62024143Sjoerg 62124143Sjoergregister struct nlist *nlst; 62224143Sjoerg 62324143Sjoerg{ 62424143Sjoerg register int i; 62524143Sjoerg 62624143Sjoerg /* check to see if we got ALL the symbols we requested */ 62724143Sjoerg /* this will write one line to stderr for every symbol not found */ 62824143Sjoerg 62924143Sjoerg i = 0; 63024143Sjoerg while (nlst->n_name != NULL) 63124143Sjoerg { 63224143Sjoerg if (nlst->n_type == 0) 63324143Sjoerg { 63424143Sjoerg /* this one wasn't found */ 63524143Sjoerg (void) fprintf(stderr, "kernel: no symbol named `%s'\n", 63624143Sjoerg nlst->n_name); 63724143Sjoerg i = 1; 63824143Sjoerg } 63924143Sjoerg nlst++; 64024143Sjoerg } 64124143Sjoerg 64224143Sjoerg return(i); 64324143Sjoerg} 64424143Sjoerg 64524143Sjoerg 64624143Sjoerg/* 64724143Sjoerg * getkval(offset, ptr, size, refstr) - get a value out of the kernel. 64824143Sjoerg * "offset" is the byte offset into the kernel for the desired value, 64924143Sjoerg * "ptr" points to a buffer into which the value is retrieved, 65024143Sjoerg * "size" is the size of the buffer (and the object to retrieve), 65124143Sjoerg * "refstr" is a reference string used when printing error meessages, 65224143Sjoerg * if "refstr" starts with a '!', then a failure on read will not 65324143Sjoerg * be fatal (this may seem like a silly way to do things, but I 65424143Sjoerg * really didn't want the overhead of another argument). 65524143Sjoerg * 65624143Sjoerg */ 65724143Sjoerg 65824143Sjoergstatic int getkval(offset, ptr, size, refstr) 65924143Sjoerg 66024143Sjoergunsigned long offset; 66124143Sjoergint *ptr; 66224143Sjoergint size; 66324143Sjoergchar *refstr; 66424143Sjoerg 66524143Sjoerg{ 66624143Sjoerg if (kvm_read(kd, offset, (char *) ptr, size) != size) 66724143Sjoerg { 66824143Sjoerg if (*refstr == '!') 66924143Sjoerg { 67024143Sjoerg return(0); 67124143Sjoerg } 67224143Sjoerg else 67324143Sjoerg { 67424143Sjoerg fprintf(stderr, "top: kvm_read for %s: %s\n", 67524143Sjoerg refstr, strerror(errno)); 67624143Sjoerg quit(23); 67724143Sjoerg } 67824143Sjoerg } 67924143Sjoerg return(1); 68024143Sjoerg} 68124143Sjoerg 68224143Sjoerg/* comparison routine for qsort */ 68324143Sjoerg 68424143Sjoerg/* 68524143Sjoerg * proc_compare - comparison function for "qsort" 68624143Sjoerg * Compares the resource consumption of two processes using five 68724143Sjoerg * distinct keys. The keys (in descending order of importance) are: 68824143Sjoerg * percent cpu, cpu ticks, state, resident set size, total virtual 68924143Sjoerg * memory usage. The process states are ordered as follows (from least 69024143Sjoerg * to most important): WAIT, zombie, sleep, stop, start, run. The 69124143Sjoerg * array declaration below maps a process state index into a number 69224143Sjoerg * that reflects this ordering. 69324143Sjoerg */ 69424143Sjoerg 69524143Sjoergstatic unsigned char sorted_state[] = 69624143Sjoerg{ 69724143Sjoerg 0, /* not used */ 69824143Sjoerg 3, /* sleep */ 69924143Sjoerg 1, /* ABANDONED (WAIT) */ 70024143Sjoerg 6, /* run */ 70124143Sjoerg 5, /* start */ 70224143Sjoerg 2, /* zombie */ 70324143Sjoerg 4 /* stop */ 70424143Sjoerg}; 70524143Sjoerg 70624143Sjoergint 70724143Sjoergproc_compare(pp1, pp2) 70824143Sjoerg 70924143Sjoergstruct proc **pp1; 71024143Sjoergstruct proc **pp2; 71124143Sjoerg 71224143Sjoerg{ 71324143Sjoerg register struct kinfo_proc *p1; 71424143Sjoerg register struct kinfo_proc *p2; 71524143Sjoerg register int result; 71624143Sjoerg register pctcpu lresult; 71724143Sjoerg 71824143Sjoerg /* remove one level of indirection */ 71924143Sjoerg p1 = *(struct kinfo_proc **) pp1; 72024143Sjoerg p2 = *(struct kinfo_proc **) pp2; 72124143Sjoerg 72224143Sjoerg /* compare percent cpu (pctcpu) */ 72324143Sjoerg if ((lresult = PP(p2, p_pctcpu) - PP(p1, p_pctcpu)) == 0) 72424143Sjoerg { 72524143Sjoerg /* use cpticks to break the tie */ 72624143Sjoerg if ((result = PP(p2, p_cpticks) - PP(p1, p_cpticks)) == 0) 72724143Sjoerg { 72824143Sjoerg /* use process state to break the tie */ 72924143Sjoerg if ((result = sorted_state[(unsigned char) PP(p2, p_stat)] - 73024143Sjoerg sorted_state[(unsigned char) PP(p1, p_stat)]) == 0) 73124143Sjoerg { 73224143Sjoerg /* use priority to break the tie */ 73324143Sjoerg if ((result = PP(p2, p_priority) - PP(p1, p_priority)) == 0) 73424143Sjoerg { 73524143Sjoerg /* use resident set size (rssize) to break the tie */ 73624143Sjoerg if ((result = VP(p2, vm_rssize) - VP(p1, vm_rssize)) == 0) 73724143Sjoerg { 73824143Sjoerg /* use total memory to break the tie */ 73924143Sjoerg result = PROCSIZE(p2) - PROCSIZE(p1); 74024143Sjoerg } 74124143Sjoerg } 74224143Sjoerg } 74324143Sjoerg } 74424143Sjoerg } 74524143Sjoerg else 74624143Sjoerg { 74724143Sjoerg result = lresult < 0 ? -1 : 1; 74824143Sjoerg } 74924143Sjoerg 75024143Sjoerg return(result); 75124143Sjoerg} 75224143Sjoerg 75324143Sjoerg 75424143Sjoerg/* 75524143Sjoerg * proc_owner(pid) - returns the uid that owns process "pid", or -1 if 75624143Sjoerg * the process does not exist. 75724143Sjoerg * It is EXTREMLY IMPORTANT that this function work correctly. 75824143Sjoerg * If top runs setuid root (as in SVR4), then this function 75924143Sjoerg * is the only thing that stands in the way of a serious 76024143Sjoerg * security problem. It validates requests for the "kill" 76124143Sjoerg * and "renice" commands. 76224143Sjoerg */ 76324143Sjoerg 76424143Sjoergint proc_owner(pid) 76524143Sjoerg 76624143Sjoergint pid; 76724143Sjoerg 76824143Sjoerg{ 76924143Sjoerg register int cnt; 77024143Sjoerg register struct kinfo_proc **prefp; 77124143Sjoerg register struct kinfo_proc *pp; 77224143Sjoerg 77324143Sjoerg prefp = pref; 77424143Sjoerg cnt = pref_len; 77524143Sjoerg while (--cnt >= 0) 77624143Sjoerg { 77724143Sjoerg pp = *prefp++; 77824143Sjoerg if (PP(pp, p_pid) == (pid_t)pid) 77924143Sjoerg { 78024143Sjoerg return((int)EP(pp, e_pcred.p_ruid)); 78124143Sjoerg } 78224143Sjoerg } 78324143Sjoerg return(-1); 78424143Sjoerg} 78524143Sjoerg 78624143Sjoerg 78724143Sjoerg/* 78824143Sjoerg * swapmode is based on a program called swapinfo written 78924143Sjoerg * by Kevin Lahey <kml@rokkaku.atl.ga.us>. 79024143Sjoerg */ 79124143Sjoerg 79224143Sjoerg#define SVAR(var) __STRING(var) /* to force expansion */ 79324143Sjoerg#define KGET(idx, var) \ 79424143Sjoerg KGET1(idx, &var, sizeof(var), SVAR(var)) 79524143Sjoerg#define KGET1(idx, p, s, msg) \ 79624143Sjoerg KGET2(nlst[idx].n_value, p, s, msg) 79724143Sjoerg#define KGET2(addr, p, s, msg) \ 79824143Sjoerg if (kvm_read(kd, (u_long)(addr), p, s) != s) { \ 79924143Sjoerg warnx("cannot read %s: %s", msg, kvm_geterr(kd)); \ 80024143Sjoerg return (0); \ 80124143Sjoerg } 80224143Sjoerg#define KGETRET(addr, p, s, msg) \ 80324143Sjoerg if (kvm_read(kd, (u_long)(addr), p, s) != s) { \ 80424143Sjoerg warnx("cannot read %s: %s", msg, kvm_geterr(kd)); \ 80524143Sjoerg return (0); \ 80624143Sjoerg } 80724143Sjoerg 80824143Sjoerg 80924143Sjoergint 81024143Sjoergswapmode(retavail, retfree) 81124143Sjoerg int *retavail; 81224143Sjoerg int *retfree; 81324143Sjoerg{ 81424143Sjoerg char *header; 81524143Sjoerg int hlen, nswap, nswdev, dmmax; 81624143Sjoerg int i, div, avail, nfree, npfree, used; 81724143Sjoerg struct swdevt *sw; 81824143Sjoerg long blocksize, *perdev; 81924143Sjoerg u_long ptr; 82024143Sjoerg struct rlist head; 82124143Sjoerg#if __FreeBSD_version >= 220000 82224143Sjoerg struct rlisthdr swaplist; 82324143Sjoerg#else 82424143Sjoerg struct rlist *swaplist; 82524143Sjoerg#endif 82624143Sjoerg struct rlist *swapptr; 82724143Sjoerg 82824143Sjoerg /* 82924143Sjoerg * Counter for error messages. If we reach the limit, 83024143Sjoerg * stop reading information from swap devices and 83124143Sjoerg * return zero. This prevent endless 'bad address' 83224143Sjoerg * messages. 83324143Sjoerg */ 83424143Sjoerg static warning = 10; 83524143Sjoerg 83624143Sjoerg if (warning <= 0) { 83724143Sjoerg /* a single warning */ 83824143Sjoerg if (!warning) { 83924143Sjoerg warning--; 84024143Sjoerg fprintf(stderr, 84124143Sjoerg "Too much errors, stop reading swap devices ...\n"); 84224143Sjoerg (void)sleep(3); 84324143Sjoerg } 84424143Sjoerg return(0); 84524143Sjoerg } 84624143Sjoerg warning--; /* decrease counter, see end of function */ 84724143Sjoerg 84824143Sjoerg KGET(VM_NSWAP, nswap); 84924143Sjoerg if (!nswap) { 85024143Sjoerg fprintf(stderr, "No swap space available\n"); 85124143Sjoerg return(0); 85224143Sjoerg } 85324143Sjoerg 85424143Sjoerg KGET(VM_NSWDEV, nswdev); 85524143Sjoerg KGET(VM_DMMAX, dmmax); 85624143Sjoerg KGET1(VM_SWAPLIST, &swaplist, sizeof(swaplist), "swaplist"); 85724143Sjoerg if ((sw = (struct swdevt *)malloc(nswdev * sizeof(*sw))) == NULL || 85824143Sjoerg (perdev = (long *)malloc(nswdev * sizeof(*perdev))) == NULL) 85924143Sjoerg err(1, "malloc"); 86024143Sjoerg KGET1(VM_SWDEVT, &ptr, sizeof ptr, "swdevt"); 86124143Sjoerg KGET2(ptr, sw, nswdev * sizeof(*sw), "*swdevt"); 86224143Sjoerg 86324143Sjoerg /* Count up swap space. */ 86424143Sjoerg nfree = 0; 86524143Sjoerg memset(perdev, 0, nswdev * sizeof(*perdev)); 86624143Sjoerg#if __FreeBSD_version >= 220000 86724143Sjoerg swapptr = swaplist.rlh_list; 86824143Sjoerg while (swapptr) { 86924143Sjoerg#else 87024143Sjoerg while (swaplist) { 87124143Sjoerg#endif 87224143Sjoerg int top, bottom, next_block; 87324143Sjoerg#if __FreeBSD_version >= 220000 87424143Sjoerg KGET2(swapptr, &head, sizeof(struct rlist), "swapptr"); 87524143Sjoerg#else 87624143Sjoerg KGET2(swaplist, &head, sizeof(struct rlist), "swaplist"); 87724143Sjoerg#endif 87824143Sjoerg 87924143Sjoerg top = head.rl_end; 88024143Sjoerg bottom = head.rl_start; 88124143Sjoerg 88224143Sjoerg nfree += top - bottom + 1; 88324143Sjoerg 88424143Sjoerg /* 88524143Sjoerg * Swap space is split up among the configured disks. 88624143Sjoerg * 88724143Sjoerg * For interleaved swap devices, the first dmmax blocks 88824143Sjoerg * of swap space some from the first disk, the next dmmax 88924143Sjoerg * blocks from the next, and so on up to nswap blocks. 89024143Sjoerg * 89124143Sjoerg * The list of free space joins adjacent free blocks, 89224143Sjoerg * ignoring device boundries. If we want to keep track 89324143Sjoerg * of this information per device, we'll just have to 89424143Sjoerg * extract it ourselves. 89524143Sjoerg */ 89624143Sjoerg while (top / dmmax != bottom / dmmax) { 89724143Sjoerg next_block = ((bottom + dmmax) / dmmax); 89824143Sjoerg perdev[(bottom / dmmax) % nswdev] += 89924143Sjoerg next_block * dmmax - bottom; 90024143Sjoerg bottom = next_block * dmmax; 90124143Sjoerg } 90224143Sjoerg perdev[(bottom / dmmax) % nswdev] += 90324143Sjoerg top - bottom + 1; 90424143Sjoerg 90524143Sjoerg#if __FreeBSD_version >= 220000 90624143Sjoerg swapptr = head.rl_next; 90724143Sjoerg#else 90824143Sjoerg swaplist = head.rl_next; 90924143Sjoerg#endif 91024143Sjoerg } 91124143Sjoerg 91224143Sjoerg header = getbsize(&hlen, &blocksize); 91324143Sjoerg div = blocksize / 512; 91424143Sjoerg avail = npfree = 0; 91524143Sjoerg for (i = 0; i < nswdev; i++) { 91624143Sjoerg int xsize, xfree; 91724143Sjoerg 91824143Sjoerg /* 91924143Sjoerg * Don't report statistics for partitions which have not 92024143Sjoerg * yet been activated via swapon(8). 92124143Sjoerg */ 92224143Sjoerg 92324143Sjoerg xsize = sw[i].sw_nblks; 92424143Sjoerg xfree = perdev[i]; 92524143Sjoerg used = xsize - xfree; 92624143Sjoerg npfree++; 92724143Sjoerg avail += xsize; 92824143Sjoerg } 92924143Sjoerg 93024143Sjoerg /* 93124143Sjoerg * If only one partition has been set up via swapon(8), we don't 93224143Sjoerg * need to bother with totals. 93324143Sjoerg */ 93424143Sjoerg *retavail = avail / 2; 93524143Sjoerg *retfree = nfree / 2; 93624143Sjoerg used = avail - nfree; 93724143Sjoerg free(sw); free(perdev); 93824143Sjoerg 93924143Sjoerg /* increase counter, no errors occurs */ 94024143Sjoerg warning++; 94124143Sjoerg 94224143Sjoerg return (int)(((double)used / (double)avail * 100.0) + 0.5); 94324143Sjoerg} 944