/* * Copyright (c) 1984 through 2008, William LeFebvre * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above * copyright notice, this list of conditions and the following disclaimer * in the documentation and/or other materials provided with the * distribution. * * * Neither the name of William LeFebvre nor the names of other * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /* * top - a top users display for Unix * * SYNOPSIS: For FreeBSD 5.x, 6.x, 7.x, 8.x * * DESCRIPTION: * Originally written for BSD4.4 system by Christos Zoulas. * Ported to FreeBSD 2.x by Steven Wallace && Wolfram Schneider * Order support hacked in from top-3.5beta6/machine/m_aix41.c * by Monte Mitzelfelt * Ported to FreeBSD 5.x and higher by William LeFebvre * * AUTHOR: Christos Zoulas * Steven Wallace * Wolfram Schneider */ #include #include #include #include #include "config.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef HAVE_UNISTD_H #include #endif /* Swap */ #include #include #include /* for changes in kernel structures */ #include "top.h" #include "machine.h" #include "utils.h" #include "username.h" #include "hash.h" #include "display.h" extern char* printable __P((char *)); int swapmode __P((int *retavail, int *retfree)); static int smpmode; static int namelength; /* * Versions prior to 5.x do not track threads in kinfo_proc, so we * simply do not display any information about them. * Versions 5.x, 6.x, and 7.x track threads but the data reported * as runtime for each thread is actually per-process and is just * duplicated across all threads. It would be very wrong to show * this data individually for each thread. Therefore we will show * a THR column (number of threads) but not provide any sort of * per-thread display. We distinguish between these three ways of * handling threads as follows: HAS_THREADS indicates that the * system has and tracks kernel threads (a THR column will appear * in the display). HAS_SHOWTHREADS indicates that the system * reports correct per-thread information and we will provide a * per-thread display (the 'H' and 't' command) upon request. * HAS_SHOWTHREADS implies HAS_THREADS. */ /* HAS_THREADS for anything 5.x and up */ #if OSMAJOR >= 5 #define HAS_THREADS #endif /* HAS_SHOWTHREADS for anything 8.x and up */ #if OSMAJOR >=8 #define HAS_SHOWTHREADS #endif /* get_process_info passes back a handle. This is what it looks like: */ struct handle { struct kinfo_proc **next_proc; /* points to next valid proc pointer */ int remaining; /* number of pointers remaining */ }; /* declarations for load_avg */ #include "loadavg.h" /* * Macros to access process information: * In versions 4.x and earlier the kinfo_proc structure was a collection of * substructures (kp_proc and kp_eproc). Starting with 5.0 kinfo_proc was * redesigned and "flattene" so that most of the information was available * in a single structure. We use macros to access the various types of * information and define these macros according to the OS revision. The * names PP, EP, and VP are due to the fact that information was originally * contained in the different substructures. We retain these names in the * code for backward compatibility. These macros use ANSI concatenation. * PP: proc * EP: extented proc * VP: vm (virtual memory information) * PRUID: Real uid * RP: rusage * PPCPU: where we store calculated cpu% data * SPPTR: where we store pointer to extra calculated data * SP: access to the extra calculated data pointed to by SPPTR */ #if OSMAJOR <= 4 #define PP(pp, field) ((pp)->kp_proc . p_##field) #define EP(pp, field) ((pp)->kp_eproc . e_##field) #define VP(pp, field) ((pp)->kp_eproc.e_vm . vm_##field) #define PRUID(pp) ((pp)->kp_eproc.e_pcred.p_ruid) #else #define PP(pp, field) ((pp)->ki_##field) #define EP(pp, field) ((pp)->ki_##field) #define VP(pp, field) ((pp)->ki_##field) #define PRUID(pp) ((pp)->ki_ruid) #define RP(pp, field) ((pp)->ki_rusage.ru_##field) #define PPCPU(pp) ((pp)->ki_sparelongs[0]) #define SPPTR(pp) ((pp)->ki_spareptrs[0]) #define SP(pp, field) (((struct save_proc *)((pp)->ki_spareptrs[0]))->sp_##field) #endif /* what we consider to be process size: */ #if OSMAJOR <= 4 #define PROCSIZE(pp) (VP((pp), map.size) / 1024) #else #define PROCSIZE(pp) (((pp)->ki_size) / 1024) #endif /* calculate a per-second rate using milliseconds */ #define per_second(n, msec) (((n) * 1000) / (msec)) /* 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[] = { "?", "START", "RUN", "SLEEP", "STOP", "ZOMB", "WAIT", "LOCK" }; #define NUM_STATES 8 /* kernel access */ static kvm_t *kd; /* these are for dealing with sysctl-based data */ #define MAXMIBLEN 8 struct sysctl_mib { char *name; int mib[MAXMIBLEN]; size_t miblen; }; static struct sysctl_mib mibs[] = { { "vm.stats.sys.v_swtch" }, #define V_SWTCH 0 { "vm.stats.sys.v_trap" }, #define V_TRAP 1 { "vm.stats.sys.v_intr" }, #define V_INTR 2 { "vm.stats.sys.v_soft" }, #define V_SOFT 3 { "vm.stats.vm.v_forks" }, #define V_FORKS 4 { "vm.stats.vm.v_vforks" }, #define V_VFORKS 5 { "vm.stats.vm.v_rforks" }, #define V_RFORKS 6 { "vm.stats.vm.v_vm_faults" }, #define V_VM_FAULTS 7 { "vm.stats.vm.v_swapin" }, #define V_SWAPIN 8 { "vm.stats.vm.v_swapout" }, #define V_SWAPOUT 9 { "vm.stats.vm.v_tfree" }, #define V_TFREE 10 { "vm.stats.vm.v_vnodein" }, #define V_VNODEIN 11 { "vm.stats.vm.v_vnodeout" }, #define V_VNODEOUT 12 { "vm.stats.vm.v_active_count" }, #define V_ACTIVE_COUNT 13 { "vm.stats.vm.v_inactive_count" }, #define V_INACTIVE_COUNT 14 { "vm.stats.vm.v_wire_count" }, #define V_WIRE_COUNT 15 { "vm.stats.vm.v_cache_count" }, #define V_CACHE_COUNT 16 { "vm.stats.vm.v_free_count" }, #define V_FREE_COUNT 17 { "vm.stats.vm.v_swappgsin" }, #define V_SWAPPGSIN 18 { "vm.stats.vm.v_swappgsout" }, #define V_SWAPPGSOUT 19 { "vfs.bufspace" }, #define VFS_BUFSPACE 20 { "kern.cp_time" }, #define K_CP_TIME 21 #ifdef HAS_SHOWTHREADS { "kern.proc.all" }, #else { "kern.proc.proc" }, #endif #define K_PROC 22 { NULL } }; /* these are for calculating cpu state percentages */ static long cp_time[CPUSTATES]; static long cp_old[CPUSTATES]; static long cp_diff[CPUSTATES]; /* these are for detailing the process states */ int process_states[8]; char *procstatenames[] = { "", " starting, ", " running, ", " sleeping, ", " stopped, ", " zombie, ", " waiting, ", " locked, ", NULL }; /* these are for detailing the cpu states */ int cpu_states[CPUSTATES]; char *cpustatenames[] = { "user", "nice", "system", "interrupt", "idle", NULL }; /* these are for detailing the kernel information */ int kernel_stats[9]; char *kernelnames[] = { " ctxsw, ", " trap, ", " intr, ", " soft, ", " fork, ", " flt, ", " pgin, ", " pgout, ", " fr", NULL }; /* these are for detailing the memory statistics */ long memory_stats[7]; char *memorynames[] = { "K Active, ", "K Inact, ", "K Wired, ", "K Cache, ", "K Buf, ", "K Free", NULL }; long swap_stats[7]; char *swapnames[] = { /* 0 1 2 3 4 5 */ "K Total, ", "K Used, ", "K Free, ", "% Inuse, ", "K In, ", "K Out", NULL }; /* * pbase points to the array that holds the kinfo_proc structures. pref * (pronounced p-ref) points to an array of kinfo_proc pointers and is where * we build up a list of processes we wish to display. Both pbase and pref are * potentially resized on every call to get_process_info. psize is the number * of procs for which we currently have space allocated. pref_len is the number * of valid pointers in pref (this is used by proc_owner). We start psize off * at -1 to ensure that space gets allocated on the first call to * get_process_info. */ static int psize = -1; static int pref_len; static struct kinfo_proc *pbase = NULL; static struct kinfo_proc **pref = NULL; /* this structure retains information from the proc array between samples */ struct save_proc { pid_t sp_pid; u_int64_t sp_runtime; long sp_vcsw; long sp_ivcsw; long sp_inblock; long sp_oublock; long sp_majflt; long sp_totalio; long sp_old_nvcsw; long sp_old_nivcsw; long sp_old_inblock; long sp_old_oublock; long sp_old_majflt; }; hash_table *procs; struct proc_field { char *name; int width; int rjust; int min_screenwidth; int (*format)(char *, int, struct kinfo_proc *); }; /* these are for getting the memory statistics */ static int pagesize; /* kept from getpagesize */ static int pageshift; /* log base 2 of the pagesize */ /* define pagetok in terms of pageshift */ #define pagetok(size) ((size) << pageshift) /* things that we track between updates */ static u_int ctxsws = 0; static u_int traps = 0; static u_int intrs = 0; static u_int softs = 0; static u_int64_t forks = 0; static u_int pfaults; static u_int pagein; static u_int pageout; static u_int tfreed; static int swappgsin = -1; static int swappgsout = -1; extern struct timeval timeout; static struct timeval lasttime = { 0, 0 }; static long elapsed_time; static long elapsed_msecs; /* things that we track during an update */ static long total_io; static int show_fullcmd; static struct handle handle; static int username_length; static int show_usernames; static int display_mode; static int *display_fields; #ifdef HAS_SHOWTHREADS static int show_threads = 0; #endif /* sorting orders. first is default */ char *ordernames[] = { "cpu", "size", "res", "time", "pri", "io", "pid", NULL }; /* compare routines */ int proc_compare(), compare_size(), compare_res(), compare_time(), compare_prio(), compare_io(), compare_pid(); int (*proc_compares[])() = { proc_compare, compare_size, compare_res, compare_time, compare_prio, compare_io, compare_pid, NULL }; /* swap related calculations */ static int mib_swapinfo[16]; static int *mib_swapinfo_idx; static int mib_swapinfo_size = 0; void swap_init() { size_t m; m = sizeof(mib_swapinfo) / sizeof(mib_swapinfo[0]); if (sysctlnametomib("vm.swap_info", mib_swapinfo, &m) != -1) { mib_swapinfo_size = m + 1; mib_swapinfo_idx = &(mib_swapinfo[m]); } } int swap_getdata(long long *retavail, long long *retfree) { int n; size_t size; long long total = 0; long long used = 0; struct xswdev xsw; n = 0; if (mib_swapinfo_size > 0) { *mib_swapinfo_idx = 0; while (size = sizeof(xsw), sysctl(mib_swapinfo, mib_swapinfo_size, &xsw, &size, NULL, 0) != -1) { dprintf("swap_getdata: swaparea %d: nblks %d, used %d\n", n, xsw.xsw_nblks, xsw.xsw_used); total += (long long)xsw.xsw_nblks; used += (long long)xsw.xsw_used; *mib_swapinfo_idx = ++n; } *retavail = pagetok(total); *retfree = pagetok(total) - pagetok(used); if (total > 0) { n = (int)((double)used * 100.0 / (double)total); } else { n = 0; } } else { *retavail = 0; *retfree = 0; } dprintf("swap_getdata: avail %lld, free %lld, %d%%\n", *retavail, *retfree, n); return(n); } /* * getkval(offset, ptr, size) - 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). * Return 0 on success, -1 on any kind of failure. */ static int getkval(unsigned long offset, int *ptr, int size) { if (kd != NULL) { if (kvm_read(kd, offset, (char *) ptr, size) == size) { return(0); } } return(-1); } int get_sysctl_mibs() { struct sysctl_mib *mp; size_t len; mp = mibs; while (mp->name != NULL) { len = MAXMIBLEN; if (sysctlnametomib(mp->name, mp->mib, &len) == -1) { message_error(" sysctlnametomib: %s", strerror(errno)); return -1; } mp->miblen = len; mp++; } return 0; } int get_sysctl(int idx, void *v, size_t l) { struct sysctl_mib *m; size_t len; m = &(mibs[idx]); len = l; if (sysctl(m->mib, m->miblen, v, &len, NULL, 0) == -1) { message_error(" sysctl: %s", strerror(errno)); return -1; } return len; } size_t get_sysctlsize(int idx) { size_t len; struct sysctl_mib *m; m = &(mibs[idx]); if (sysctl(m->mib, m->miblen, NULL, &len, NULL, 0) == -1) { message_error(" sysctl (size): %s", strerror(errno)); len = 0; } return len; } int fmt_pid(char *buf, int sz, struct kinfo_proc *pp) { return snprintf(buf, sz, "%6d", PP(pp, pid)); } int fmt_username(char *buf, int sz, struct kinfo_proc *pp) { return snprintf(buf, sz, "%-*.*s", username_length, username_length, username(PRUID(pp))); } int fmt_uid(char *buf, int sz, struct kinfo_proc *pp) { return snprintf(buf, sz, "%6d", PRUID(pp)); } int fmt_thr(char *buf, int sz, struct kinfo_proc *pp) { return snprintf(buf, sz, "%3d", PP(pp, numthreads)); } int fmt_pri(char *buf, int sz, struct kinfo_proc *pp) { #if OSMAJOR <= 4 return snprintf(buf, sz, "%3d", PP(pp, priority)); #else return snprintf(buf, sz, "%3d", PP(pp, pri.pri_level)); #endif } int fmt_nice(char *buf, int sz, struct kinfo_proc *pp) { return snprintf(buf, sz, "%4d", PP(pp, nice) - NZERO); } int fmt_size(char *buf, int sz, struct kinfo_proc *pp) { return snprintf(buf, sz, "%5s", format_k(PROCSIZE(pp))); } int fmt_res(char *buf, int sz, struct kinfo_proc *pp) { return snprintf(buf, sz, "%5s", format_k(pagetok(VP(pp, rssize)))); } int fmt_state(char *buf, int sz, struct kinfo_proc *pp) { int state; char status[16]; state = PP(pp, stat); switch(state) { case SRUN: if (smpmode && PP(pp, oncpu) != 0xff) sprintf(status, "CPU%d", PP(pp, oncpu)); else strcpy(status, "RUN"); break; case SSLEEP: if (EP(pp, wmesg) != NULL) { sprintf(status, "%.6s", EP(pp, wmesg)); break; } /* fall through */ default: if (state >= 0 && state < NUM_STATES) sprintf(status, "%.6s", state_abbrev[(unsigned char) state]); else sprintf(status, "?%-5d", state); break; } return snprintf(buf, sz, "%-6.6s", status); } int fmt_flags(char *buf, int sz, struct kinfo_proc *pp) { long flag; char chrs[12]; char *p; flag = PP(pp, flag); p = chrs; if (PP(pp, nice) < NZERO) *p++ = '<'; else if (PP(pp, nice) > NZERO) *p++ = 'N'; if (flag & P_TRACED) *p++ = 'X'; if (flag & P_WEXIT && PP(pp, stat) != SZOMB) *p++ = 'E'; if (flag & P_PPWAIT) *p++ = 'V'; if (flag & P_SYSTEM || PP(pp, lock) > 0) *p++ = 'L'; if (PP(pp, kiflag) & KI_SLEADER) *p++ = 's'; if (flag & P_CONTROLT) *p++ = '+'; if (flag & P_JAILED) *p++ = 'J'; *p = '\0'; return snprintf(buf, sz, "%-3.3s", chrs); } int fmt_c(char *buf, int sz, struct kinfo_proc *pp) { return snprintf(buf, sz, "%1x", PP(pp, lastcpu)); } int fmt_time(char *buf, int sz, struct kinfo_proc *pp) { return snprintf(buf, sz, "%6s", format_time((PP(pp, runtime) + 500000) / 1000000)); } int fmt_cpu(char *buf, int sz, struct kinfo_proc *pp) { return snprintf(buf, sz, "%5.2f%%", (double)PPCPU(pp) / 100.0); } int fmt_command(char *buf, int sz, struct kinfo_proc *pp) { int inmem; char cmd[MAX_COLS]; char *bufp; struct pargs pargs; int len; #if OSMAJOR <= 4 inmem = (PP(pp, flag) & P_INMEM); #else inmem = (PP(pp, sflag) & PS_INMEM); #endif if (show_fullcmd && inmem) { /* get the pargs structure */ if (getkval((unsigned long)PP(pp, args), (int *)&pargs, sizeof(pargs)) != -1) { /* determine workable length */ if ((len = pargs.ar_length) >= MAX_COLS) { len = MAX_COLS - 1; } /* get the string from that */ if (len > 0 && getkval((unsigned long)PP(pp, args) + sizeof(pargs.ar_ref) + sizeof(pargs.ar_length), (int *)cmd, len) != -1) { /* successfull retrieval: now convert nulls in to spaces */ bufp = cmd; while (len-- > 0) { if (*bufp == '\0') { *bufp = ' '; } bufp++; } /* null terminate cmd */ *--bufp = '\0'; /* format cmd as our answer */ return snprintf(buf, sz, "%s", cmd); } } } /* for anything else we just display comm */ return snprintf(buf, sz, inmem ? "%s" : "<%s>", printable(PP(pp, comm))); } int fmt_vcsw(char *buf, int sz, struct kinfo_proc *pp) { return snprintf(buf, sz, "%6ld", per_second(SP(pp, vcsw), elapsed_msecs)); } int fmt_ivcsw(char *buf, int sz, struct kinfo_proc *pp) { return snprintf(buf, sz, "%6ld", per_second(SP(pp, ivcsw), elapsed_msecs)); } int fmt_read(char *buf, int sz, struct kinfo_proc *pp) { return snprintf(buf, sz, "%6ld", per_second(SP(pp, inblock), elapsed_msecs)); } int fmt_write(char *buf, int sz, struct kinfo_proc *pp) { return snprintf(buf, sz, "%6ld", per_second(SP(pp, oublock), elapsed_msecs)); } int fmt_fault(char *buf, int sz, struct kinfo_proc *pp) { return snprintf(buf, sz, "%6ld", per_second(SP(pp, majflt), elapsed_msecs)); } int fmt_iototal(char *buf, int sz, struct kinfo_proc *pp) { return snprintf(buf, sz, "%6ld", per_second(SP(pp, totalio), elapsed_msecs)); } int fmt_iopct(char *buf, int sz, struct kinfo_proc *pp) { return snprintf(buf, sz, "%6.2f", (SP(pp, totalio) * 100.) / total_io); } struct proc_field proc_field[] = { { "PID", 6, 1, 0, fmt_pid }, { "USERNAME", 8, 0, 0, fmt_username }, #define FIELD_USERNAME 1 { "UID", 6, 1, 0, fmt_uid }, #define FIELD_UID 2 { "THR", 3, 1, 0, fmt_thr }, { "PRI", 3, 1, 0, fmt_pri }, { "NICE", 4, 1, 0, fmt_nice }, { "SIZE", 5, 1, 0, fmt_size }, { "RES", 5, 1, 0, fmt_res }, { "STATE", 6, 0, 0, fmt_state }, { "FLG", 3, 0, 84, fmt_flags }, { "C", 1, 0, 0, fmt_c }, { "TIME", 6, 1, 0, fmt_time }, { "CPU", 6, 1, 0, fmt_cpu }, { "COMMAND", 7, 0, 0, fmt_command }, { "VCSW", 6, 1, 0, fmt_vcsw }, { "IVCSW", 6, 1, 0, fmt_ivcsw }, { "READ", 6, 1, 0, fmt_read }, { "WRITE", 6, 1, 0, fmt_write }, { "FAULT", 6, 1, 0, fmt_fault }, { "TOTAL", 6, 1, 0, fmt_iototal }, { "PERCENT", 7, 1, 0, fmt_iopct }, { NULL, 0, 0, 0, NULL } }; #define MAX_FIELDS 24 static int mode0_display[MAX_FIELDS]; static int mode0thr_display[MAX_FIELDS]; static int mode1_display[MAX_FIELDS]; int field_index(char *col) { struct proc_field *fp; int i = 0; fp = proc_field; while (fp->name != NULL) { if (strcmp(col, fp->name) == 0) { return i; } fp++; i++; } return -1; } void field_subst(int *fp, int old, int new) { while (*fp != -1) { if (*fp == old) { *fp = new; } fp++; } } int machine_init(struct statics *statics) { int i = 0; size_t len; int *ip; struct timeval boottime; len = sizeof(smpmode); if ((sysctlbyname("machdep.smp_active", &smpmode, &len, NULL, 0) < 0 && sysctlbyname("smp.smp_active", &smpmode, &len, NULL, 0) < 0) || len != sizeof(smpmode)) { smpmode = 0; } smpmode = smpmode != 0; /* kvm_open the active kernel: its okay if this fails */ kd = kvm_open(NULL, NULL, NULL, O_RDONLY, NULL); /* get boot time */ len = sizeof(boottime); if (sysctlbyname("kern.boottime", &boottime, &len, NULL, 0) == -1) { /* we have no boottime to report */ boottime.tv_sec = -1; } pbase = NULL; pref = NULL; /* get the page size with "getpagesize" and calculate pageshift from it */ i = pagesize = getpagesize(); pageshift = 0; while (i > 1) { pageshift++; i >>= 1; } /* translate sysctl paths to mibs for faster access */ get_sysctl_mibs(); /* initialize swap stuff */ swap_init(); /* create the hash table that remembers proc data */ procs = hash_create(2039); /* we only need the amount of log(2)1024 for our conversion */ pageshift -= LOG1024; /* fill in the statics information */ statics->procstate_names = procstatenames; statics->cpustate_names = cpustatenames; statics->memory_names = memorynames; statics->kernel_names = kernelnames; statics->boottime = boottime.tv_sec; statics->swap_names = swapnames; statics->order_names = ordernames; statics->flags.warmup = 1; statics->modemax = 2; #ifdef HAS_SHOWTHREADS statics->flags.threads = 1; #endif /* we need kvm descriptor in order to show full commands */ statics->flags.fullcmds = kd != NULL; /* set up the display indices for mode0 */ ip = mode0_display; *ip++ = field_index("PID"); *ip++ = field_index("USERNAME"); #ifdef HAS_THREADS *ip++ = field_index("THR"); #endif *ip++ = field_index("PRI"); *ip++ = field_index("NICE"); *ip++ = field_index("SIZE"); *ip++ = field_index("RES"); *ip++ = field_index("STATE"); *ip++ = field_index("FLG"); if (smpmode) *ip++ = field_index("C"); *ip++ = field_index("TIME"); *ip++ = field_index("CPU"); *ip++ = field_index("COMMAND"); *ip = -1; #ifdef HAS_SHOWTHREADS /* set up the display indices for mode0 showing threads */ ip = mode0thr_display; *ip++ = field_index("PID"); *ip++ = field_index("USERNAME"); *ip++ = field_index("PRI"); *ip++ = field_index("NICE"); *ip++ = field_index("SIZE"); *ip++ = field_index("RES"); *ip++ = field_index("STATE"); *ip++ = field_index("FLG"); if (smpmode) *ip++ = field_index("C"); *ip++ = field_index("TIME"); *ip++ = field_index("CPU"); *ip++ = field_index("COMMAND"); *ip = -1; #endif /* set up the display indices for mode1 */ ip = mode1_display; *ip++ = field_index("PID"); *ip++ = field_index("USERNAME"); *ip++ = field_index("VCSW"); *ip++ = field_index("IVCSW"); *ip++ = field_index("READ"); *ip++ = field_index("WRITE"); *ip++ = field_index("FAULT"); *ip++ = field_index("TOTAL"); *ip++ = field_index("PERCENT"); *ip++ = field_index("COMMAND"); *ip = -1; /* all done! */ return(0); } char *format_header(char *uname_field) { return ""; } void get_vm_sum(struct vmmeter *sum) { #define GET_VM_STAT(v, s) (void)get_sysctl(v, &(sum->s), sizeof(sum->s)) GET_VM_STAT(V_SWTCH, v_swtch); GET_VM_STAT(V_TRAP, v_trap); GET_VM_STAT(V_INTR, v_intr); GET_VM_STAT(V_SOFT, v_soft); GET_VM_STAT(V_VFORKS, v_vforks); GET_VM_STAT(V_FORKS, v_forks); GET_VM_STAT(V_RFORKS, v_rforks); GET_VM_STAT(V_VM_FAULTS, v_vm_faults); GET_VM_STAT(V_SWAPIN, v_swapin); GET_VM_STAT(V_SWAPOUT, v_swapout); GET_VM_STAT(V_TFREE, v_tfree); GET_VM_STAT(V_VNODEIN, v_vnodein); GET_VM_STAT(V_VNODEOUT, v_vnodeout); GET_VM_STAT(V_ACTIVE_COUNT, v_active_count); GET_VM_STAT(V_INACTIVE_COUNT, v_inactive_count); GET_VM_STAT(V_WIRE_COUNT, v_wire_count); GET_VM_STAT(V_CACHE_COUNT, v_cache_count); GET_VM_STAT(V_FREE_COUNT, v_free_count); GET_VM_STAT(V_SWAPPGSIN, v_swappgsin); GET_VM_STAT(V_SWAPPGSOUT, v_swappgsout); } void get_system_info(struct system_info *si) { long total; struct timeval thistime; struct timeval timediff; /* timestamp and time difference */ gettimeofday(&thistime, 0); timersub(&thistime, &lasttime, &timediff); elapsed_time = timediff.tv_sec * 1000000 + timediff.tv_usec; elapsed_msecs = timediff.tv_sec * 1000 + timediff.tv_usec / 1000; /* get the load averages */ if (getloadavg(si->load_avg, NUM_AVERAGES) == -1) { /* failed: fill in with zeroes */ (void) memset(si->load_avg, 0, sizeof(si->load_avg)); } /* get the cp_time array */ (void)get_sysctl(K_CP_TIME, &cp_time, sizeof(cp_time)); /* convert cp_time counts to percentages */ total = percentages(CPUSTATES, cpu_states, cp_time, cp_old, cp_diff); /* sum memory & swap statistics */ { struct vmmeter sum; static unsigned int swap_delay = 0; static long long swapavail = 0; static long long swapfree = 0; static int bufspace = 0; get_vm_sum(&sum); /* get bufspace */ bufspace = 0; (void) get_sysctl(VFS_BUFSPACE, &bufspace, sizeof(bufspace)); /* kernel stats */ dprintf("kernel: swtch %d, trap %d, intr %d, soft %d, vforks %d\n", sum.v_swtch, sum.v_trap, sum.v_intr, sum.v_soft, sum.v_vforks); kernel_stats[0] = per_second(sum.v_swtch - ctxsws, elapsed_msecs); kernel_stats[1] = per_second(sum.v_trap - traps, elapsed_msecs); kernel_stats[2] = per_second(sum.v_intr - intrs, elapsed_msecs); kernel_stats[3] = per_second(sum.v_soft - softs, elapsed_msecs); kernel_stats[4] = per_second(sum.v_vforks + sum.v_forks + sum.v_rforks - forks, elapsed_msecs); kernel_stats[5] = per_second(sum.v_vm_faults - pfaults, elapsed_msecs); kernel_stats[6] = per_second(sum.v_swapin + sum.v_vnodein - pagein, elapsed_msecs); kernel_stats[7] = per_second(sum.v_swapout + sum.v_vnodeout - pageout, elapsed_msecs); kernel_stats[8] = per_second(sum.v_tfree - tfreed, elapsed_msecs); ctxsws = sum.v_swtch; traps = sum.v_trap; intrs = sum.v_intr; softs = sum.v_soft; forks = (u_int64_t)sum.v_vforks + sum.v_forks + sum.v_rforks; pfaults = sum.v_vm_faults; pagein = sum.v_swapin + sum.v_vnodein; pageout = sum.v_swapout + sum.v_vnodeout; tfreed = sum.v_tfree; /* convert memory stats to Kbytes */ memory_stats[0] = pagetok(sum.v_active_count); memory_stats[1] = pagetok(sum.v_inactive_count); memory_stats[2] = pagetok(sum.v_wire_count); memory_stats[3] = pagetok(sum.v_cache_count); memory_stats[4] = bufspace / 1024; memory_stats[5] = pagetok(sum.v_free_count); memory_stats[6] = -1; /* first interval */ if (swappgsin < 0) { swap_stats[4] = 0; swap_stats[5] = 0; } /* compute differences between old and new swap statistic */ else { swap_stats[4] = pagetok(sum.v_swappgsin - swappgsin); swap_stats[5] = pagetok(sum.v_swappgsout - swappgsout); } swappgsin = sum.v_swappgsin; swappgsout = sum.v_swappgsout; /* call CPU heavy swap_getdata() only for changes */ if (swap_stats[4] > 0 || swap_stats[5] > 0 || swap_delay == 0) { swap_stats[3] = swap_getdata(&swapavail, &swapfree); swap_stats[0] = swapavail; swap_stats[1] = swapavail - swapfree; swap_stats[2] = swapfree; } swap_delay = 1; swap_stats[6] = -1; } /* set arrays and strings */ si->cpustates = cpu_states; si->kernel = kernel_stats; si->memory = memory_stats; si->swap = swap_stats; si->last_pid = -1; lasttime = thistime; } caddr_t get_process_info(struct system_info *si, struct process_select *sel, int compare_index) { int i; int total_procs; int active_procs; struct kinfo_proc **prefp; struct kinfo_proc *pp; struct kinfo_proc *prev_pp = NULL; struct save_proc *savep; long proc_io; pid_t pid; size_t size; int nproc; /* these are copied out of sel for speed */ int show_idle; int show_self; int show_system; int show_uid; char *show_command; /* get proc table size and give it a boost */ nproc = (int)get_sysctlsize(K_PROC) / sizeof(struct kinfo_proc); nproc += nproc >> 4; size = nproc * sizeof(struct kinfo_proc); dprintf("get_process_info: nproc %d, psize %d, size %d\n", nproc, psize, size); /* make sure we have enough space allocated */ if (nproc > psize) { /* reallocate both pbase and pref */ pbase = (struct kinfo_proc *)realloc(pbase, size); pref = (struct kinfo_proc **)realloc(pref, sizeof(struct kinfo_proc *) * nproc); psize = nproc; } /* make sure we got the space we asked for */ if (pref == NULL || pbase == NULL) { /* abandon all hope */ message_error(" Out of memory!"); nproc = psize = 0; si->p_total = 0; si->p_active = 0; return NULL; } /* get all process information (threads, too) */ if (size > 0) { nproc = get_sysctl(K_PROC, pbase, size); if (nproc == -1) { nproc = 0; } else { nproc /= sizeof(struct kinfo_proc); } } /* 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_self = 0; show_system = sel->system; show_uid = sel->uid != -1; show_fullcmd = sel->fullcmd; show_command = sel->command; show_usernames = sel->usernames; display_mode = sel->mode; #ifdef HAS_SHOWTHREADS show_threads = sel->threads; #endif /* count up process states and get pointers to interesting procs */ total_procs = 0; active_procs = 0; total_io = 0; memset((char *)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 P_SYSTEM set are system * processes---these get ignored unless show_sysprocs is set. */ pid = PP(pp, pid); if (PP(pp, stat) != 0) { #ifdef HAS_SHOWTHREADS int is_thread; lwpid_t tid; /* get thread id */ tid = PP(pp, tid); /* is this just a thread? */ is_thread = (prev_pp != NULL && PP(prev_pp, pid) == pid); /* count this process and its state */ /* only count threads if we are showing them */ if (show_threads || !is_thread) { total_procs++; process_states[(unsigned char) PP(pp, stat)]++; } /* grab old data from hash */ if ((savep = hash_lookup_lwpid(procs, tid)) != NULL) { /* verify that this is not a new or different thread */ /* (freebsd reuses thread ids fairly quickly) */ /* pids must match and time can't have gone backwards */ if (pid != savep->sp_pid || PP(pp, runtime) < savep->sp_runtime) { /* not the same thread -- reuse the save_proc structure */ memset(savep, 0, sizeof(struct save_proc)); savep->sp_pid = pid; } } else { /* havent seen this one before */ savep = (struct save_proc *)calloc(1, sizeof(struct save_proc)); savep->sp_pid = pid; hash_add_lwpid(procs, tid, savep); } #else /* !HAS_SHOWTHREADS */ total_procs++; process_states[(unsigned char) PP(pp, stat)]++; /* grab old data from hash */ if ((savep = hash_lookup_pid(procs, pid)) == NULL) { /* havent seen this one before */ savep = (struct save_proc *)calloc(1, sizeof(struct save_proc)); savep->sp_pid = pid; hash_add_pid(procs, pid, savep); } #endif /* save the pointer to the sp struct */ SPPTR(pp) = (void *)savep; /* calculate %cpu */ PPCPU(pp) = ((PP(pp, runtime) - savep->sp_runtime) * 10000) / elapsed_time; dprintf("%d (%d): runtime %lld, saved_pid %d, saved_runtime %lld, elapsed_time %d, ppcpu %d\n", pid, PP(pp, tid), PP(pp, runtime), savep->sp_pid, savep->sp_runtime, elapsed_time, PPCPU(pp)); /* calculate io differences */ proc_io = 0; savep->sp_vcsw = (RP(pp, nvcsw) - savep->sp_old_nvcsw); savep->sp_ivcsw = (RP(pp, nivcsw) - savep->sp_old_nivcsw); proc_io += (savep->sp_inblock = (RP(pp, inblock) - savep->sp_old_inblock)); proc_io += (savep->sp_oublock = (RP(pp, oublock) - savep->sp_old_oublock)); proc_io += (savep->sp_majflt = (RP(pp, majflt) - savep->sp_old_majflt)); total_io += proc_io; savep->sp_totalio = proc_io; /* save data for next time */ savep->sp_runtime = PP(pp, runtime); savep->sp_old_nvcsw = RP(pp, nvcsw); savep->sp_old_nivcsw = RP(pp, nivcsw); savep->sp_old_inblock = RP(pp, inblock); savep->sp_old_oublock = RP(pp, oublock); savep->sp_old_majflt = RP(pp, majflt); /* is this one selected for viewing? */ if ((PP(pp, stat) != SZOMB) && (show_system || ((PP(pp, flag) & P_SYSTEM) == 0)) && (show_idle || (PP(pp, pctcpu) != 0) || (PP(pp, stat) == SRUN)) && (!show_uid || PRUID(pp) == (uid_t)sel->uid) && (show_command == NULL || strcasestr(PP(pp, comm), show_command) != NULL)) { #ifdef HAS_SHOWTHREADS /* yes, but make sure it isn't just a thread */ if (show_threads || !is_thread) { /* we will be showing this thread */ *prefp++ = pp; active_procs++; } else { /* we will not be showing this thread, but we need to roll up its cpu usage in to its process */ PP(prev_pp, pctcpu) += PP(pp, pctcpu); } #else /* !HAS_SHOWTHREADS */ /* we will be showing this process */ *prefp++ = pp; active_procs++; #endif } prev_pp = pp; } } dprintf("total_io: %d\n", total_io); if (total_io == 0) total_io = 1; /* if requested, sort the "interesting" processes */ if (active_procs > 1) { qsort((char *)pref, active_procs, sizeof(struct kinfo_proc *), proc_compares[compare_index]); } /* remember active and total counts */ si->p_total = total_procs; si->p_active = pref_len = active_procs; /* pass back a handle */ handle.next_proc = pref; handle.remaining = active_procs; return((caddr_t)&handle); } static char p_header[MAX_COLS]; char * format_process_header(struct process_select *sel, caddr_t handle, int count) { int cols; int n; int w; char *p; int *fi; struct kinfo_proc **kip; struct proc_field *fp; /* check for null handle */ if (handle == NULL) { return(""); } /* remember how many columns there are on the display */ cols = display_columns(); /* mode & threads dictate format */ fi = display_fields = sel->mode == 0 ? (sel->threads == 0 ? mode0_display : mode0thr_display) : mode1_display; /* set username field correctly */ if (!sel->usernames) { /* display uids */ field_subst(fi, FIELD_USERNAME, FIELD_UID); } else { /* display usernames */ field_subst(fi, FIELD_UID, FIELD_USERNAME); /* we also need to determine the longest username for column width */ /* calculate namelength from first "count" processes */ kip = ((struct handle *)handle)->next_proc; n = ((struct handle *)handle)->remaining; if (n > count) n = count; namelength = 0; while (n-- > 0) { w = strlen(username(PRUID(*kip))); if (w > namelength) namelength = w; kip++; } dprintf("format_process_header: namelength %d\n", namelength); /* place it in bounds */ if (namelength < 8) { namelength = 8; } /* set the column width */ proc_field[FIELD_USERNAME].width = username_length = namelength; } /* walk thru fields and construct header */ /* are we worried about overflow??? */ p = p_header; while (*fi != -1) { fp = &(proc_field[*fi++]); if (fp->min_screenwidth <= cols) { p += sprintf(p, fp->rjust ? "%*s" : "%-*s", fp->width, fp->name); *p++ = ' '; } } *--p = '\0'; return p_header; } static char fmt[MAX_COLS]; /* static area where result is built */ char * format_next_process(caddr_t handle, char *(*get_userid)(int)) { struct kinfo_proc *pp; struct handle *hp; struct proc_field *fp; int *fi; int i; int cols; char *p; int len; int x; /* find and remember the next proc structure */ hp = (struct handle *)handle; pp = *(hp->next_proc++); hp->remaining--; /* mode & threads dictate format */ fi = display_fields; /* screen width is a consideration, too */ cols = display_columns(); /* build output by field */ p = fmt; len = MAX_COLS; while ((i = *fi++) != -1) { fp = &(proc_field[i]); if (len > 0 && fp->min_screenwidth <= cols) { x = (*(fp->format))(p, len, pp); if (x >= len) { dprintf("format_next_process: formatter overflow: x %d, len %d, p %08x => %08x, fmt %08x - %08x\n", x, len, p, p + len, fmt, fmt + sizeof(fmt)); p += len; len = 0; } else { p += x; *p++ = ' '; len -= x + 1; } } } *--p = '\0'; /* return the result */ return(fmt); } /* comparison routines 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. */ static unsigned char sorted_state[] = { 0, /* not used */ 3, /* sleep */ 1, /* ABANDONED (WAIT) */ 6, /* run */ 5, /* start */ 2, /* zombie */ 4 /* stop */ }; #define ORDERKEY_PCTCPU \ if (lresult = (long) PPCPU(p2) - (long) PPCPU(p1), \ (result = lresult > 0 ? 1 : lresult < 0 ? -1 : 0) == 0) #define ORDERKEY_CPTICKS \ if ((result = PP(p2, runtime) > PP(p1, runtime) ? 1 : \ PP(p2, runtime) < PP(p1, runtime) ? -1 : 0) == 0) #define ORDERKEY_STATE \ if ((result = sorted_state[(unsigned char) PP(p2, stat)] - \ sorted_state[(unsigned char) PP(p1, stat)]) == 0) #if OSMAJOR <= 4 #define ORDERKEY_PRIO \ if ((result = PP(p2, priority) - PP(p1, priority)) == 0) #else #define ORDERKEY_PRIO \ if ((result = PP(p2, pri.pri_level) - PP(p1, pri.pri_level)) == 0) #endif #define ORDERKEY_RSSIZE \ if ((result = VP(p2, rssize) - VP(p1, rssize)) == 0) #define ORDERKEY_MEM \ if ( (result = PROCSIZE(p2) - PROCSIZE(p1)) == 0 ) #define ORDERKEY_IO \ if ( (result = SP(p2, totalio) - SP(p1, totalio)) == 0) #define ORDERKEY_PID \ if ( (result = PP(p1, pid) - PP(p2, pid)) == 0) /* compare_cpu - the comparison function for sorting by cpu percentage */ int proc_compare(struct proc **pp1, struct proc **pp2) { struct kinfo_proc *p1; struct kinfo_proc *p2; int result; pctcpu lresult; /* remove one level of indirection */ p1 = *(struct kinfo_proc **) pp1; p2 = *(struct kinfo_proc **) 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 proc **pp1, struct proc **pp2) { struct kinfo_proc *p1; struct kinfo_proc *p2; int result; pctcpu lresult; /* remove one level of indirection */ p1 = *(struct kinfo_proc **) pp1; p2 = *(struct kinfo_proc **) 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 proc **pp1, struct proc **pp2) { struct kinfo_proc *p1; struct kinfo_proc *p2; int result; pctcpu lresult; /* remove one level of indirection */ p1 = *(struct kinfo_proc **) pp1; p2 = *(struct kinfo_proc **) 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 proc **pp1, struct proc **pp2) { struct kinfo_proc *p1; struct kinfo_proc *p2; int result; pctcpu lresult; /* remove one level of indirection */ p1 = *(struct kinfo_proc **) pp1; p2 = *(struct kinfo_proc **) pp2; ORDERKEY_CPTICKS ORDERKEY_PCTCPU ORDERKEY_STATE ORDERKEY_PRIO ORDERKEY_RSSIZE ORDERKEY_MEM ; return(result); } /* compare_prio - the comparison function for sorting by priority */ int compare_prio(struct proc **pp1, struct proc **pp2) { struct kinfo_proc *p1; struct kinfo_proc *p2; int result; pctcpu lresult; /* remove one level of indirection */ p1 = *(struct kinfo_proc **) pp1; p2 = *(struct kinfo_proc **) pp2; ORDERKEY_PRIO ORDERKEY_CPTICKS ORDERKEY_PCTCPU ORDERKEY_STATE ORDERKEY_RSSIZE ORDERKEY_MEM ; return(result); } /* compare_io - the comparison function for sorting by io count */ int compare_io(struct proc **pp1, struct proc **pp2) { struct kinfo_proc *p1; struct kinfo_proc *p2; int result; pctcpu lresult; /* remove one level of indirection */ p1 = *(struct kinfo_proc **) pp1; p2 = *(struct kinfo_proc **) pp2; ORDERKEY_IO ORDERKEY_PCTCPU ORDERKEY_CPTICKS ORDERKEY_STATE ORDERKEY_PRIO ORDERKEY_RSSIZE ORDERKEY_MEM ; return(result); } /* compare_pid - the comparison function for sorting by process id */ int compare_pid(struct proc **pp1, struct proc **pp2) { struct kinfo_proc *p1; struct kinfo_proc *p2; int result; /* remove one level of indirection */ p1 = *(struct kinfo_proc **) pp1; p2 = *(struct kinfo_proc **) pp2; ORDERKEY_PID ; return(result); } /* * 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. */ int proc_owner(int pid) { int cnt; struct kinfo_proc **prefp; struct kinfo_proc *pp; prefp = pref; cnt = pref_len; while (--cnt >= 0) { pp = *prefp++; if (PP(pp, pid) == (pid_t)pid) { return((int)PRUID(pp)); } } return(-1); }