machine.c revision 1.13
11558Srgrimes/* $OpenBSD: machine.c,v 1.13 1998/09/20 06:19:14 niklas Exp $ */ 21558Srgrimes 31558Srgrimes/* 41558Srgrimes * top - a top users display for Unix 51558Srgrimes * 61558Srgrimes * SYNOPSIS: For an OpenBSD system 71558Srgrimes * 81558Srgrimes * DESCRIPTION: 91558Srgrimes * This is the machine-dependent module for OpenBSD 101558Srgrimes * Tested on: 111558Srgrimes * i386 121558Srgrimes * 131558Srgrimes * LIBS: -lkvm 141558Srgrimes * 151558Srgrimes * TERMCAP: -ltermlib 161558Srgrimes * 171558Srgrimes * CFLAGS: -DHAVE_GETOPT -DORDER 181558Srgrimes * 191558Srgrimes * AUTHOR: Thorsten Lockert <tholo@sigmasoft.com> 201558Srgrimes * Adapted from BSD4.4 by Christos Zoulas <christos@ee.cornell.edu> 211558Srgrimes * Patch for process wait display by Jarl F. Greipsland <jarle@idt.unit.no> 221558Srgrimes * Patch for -DORDER by Kenneth Stailey <kstailey@disclosure.com> 231558Srgrimes */ 241558Srgrimes 251558Srgrimes#include <sys/types.h> 261558Srgrimes#include <sys/signal.h> 271558Srgrimes#include <sys/param.h> 281558Srgrimes 291558Srgrimes#define DOSWAP 301558Srgrimes 311558Srgrimes#include <stdio.h> 321558Srgrimes#include <stdlib.h> 331558Srgrimes#include <string.h> 341558Srgrimes#include <limits.h> 351558Srgrimes#include <err.h> 361558Srgrimes#include <nlist.h> 371558Srgrimes#include <math.h> 381558Srgrimes#include <kvm.h> 391558Srgrimes#include <unistd.h> 401558Srgrimes#include <sys/errno.h> 411558Srgrimes#include <sys/sysctl.h> 421558Srgrimes#include <sys/dir.h> 431558Srgrimes#include <sys/dkstat.h> 4415770Swollman#include <sys/file.h> 451558Srgrimes#include <sys/time.h> 4615770Swollman#include <sys/resource.h> 4715770Swollman 4818286Sbde#ifdef DOSWAP 491558Srgrimes#include <err.h> 501558Srgrimes#include <sys/map.h> 511558Srgrimes#include <sys/conf.h> 521558Srgrimes#endif 531558Srgrimes 541558Srgrimesstatic int check_nlist __P((struct nlist *)); 551558Srgrimesstatic int getkval __P((unsigned long, int *, int, char *)); 561558Srgrimesstatic int swapmode __P((int *, int *)); 571558Srgrimes 581558Srgrimes#include "top.h" 591558Srgrimes#include "display.h" 601558Srgrimes#include "machine.h" 611558Srgrimes#include "utils.h" 621558Srgrimes 631558Srgrimes/* get_process_info passes back a handle. This is what it looks like: */ 641558Srgrimes 651558Srgrimesstruct handle 669336Sdfr{ 6714024Smarkm struct kinfo_proc **next_proc; /* points to next valid proc pointer */ 681558Srgrimes int remaining; /* number of pointers remaining */ 691558Srgrimes}; 701558Srgrimes 711558Srgrimes/* declarations for load_avg */ 729336Sdfr#include "loadavg.h" 739336Sdfr 741558Srgrimes#define PP(pp, field) ((pp)->kp_proc . field) 759336Sdfr#define EP(pp, field) ((pp)->kp_eproc . field) 761558Srgrimes#define VP(pp, field) ((pp)->kp_eproc.e_vm . field) 771558Srgrimes 781558Srgrimes/* what we consider to be process size: */ 791558Srgrimes#define PROCSIZE(pp) (VP((pp), vm_tsize) + VP((pp), vm_dsize) + VP((pp), vm_ssize)) 801558Srgrimes 811558Srgrimes/* definitions for indices in the nlist array */ 821558Srgrimes#define X_CP_TIME 0 831558Srgrimes#define X_HZ 1 841558Srgrimes 851558Srgrimes#ifdef DOSWAP 861558Srgrimes#define VM_SWAPMAP 2 871558Srgrimes#define VM_NSWAPMAP 3 881558Srgrimes#define VM_SWDEVT 4 8915770Swollman#define VM_NSWAP 5 901558Srgrimes#define VM_NSWDEV 6 911558Srgrimes#define VM_DMMAX 7 921558Srgrimes#define VM_NISWAP 8 931558Srgrimes#define VM_NISWDEV 9 949336Sdfr#endif 954065Swollman 964065Swollmanstatic struct nlist nlst[] = { 974065Swollman { "_cp_time" }, /* 0 */ 984065Swollman { "_hz" }, /* 1 */ 994065Swollman#ifdef DOSWAP 1009336Sdfr { "_swapmap" }, /* 2 */ 1019336Sdfr { "_nswapmap" }, /* 3 */ 1029336Sdfr { "_swdevt" }, /* 4 */ 1034065Swollman { "_nswap" }, /* 5 */ 1044065Swollman { "_nswdev" }, /* 6 */ 1054065Swollman { "_dmmax" }, /* 7 */ 1064065Swollman { "_niswap" }, /* 8 */ 1074065Swollman { "_niswdev" }, /* 9 */ 1089230Skarl#endif 1094065Swollman { 0 } 1101558Srgrimes}; 1111558Srgrimes 1121558Srgrimes/* 1131558Srgrimes * These definitions control the format of the per-process area 1144065Swollman */ 1154065Swollman 1164065Swollmanstatic char header[] = 1174065Swollman " PID X PRI NICE SIZE RES STATE WAIT TIME CPU COMMAND"; 1189336Sdfr/* 0123456 -- field to fill in starts at header+6 */ 1194065Swollman#define UNAME_START 6 1204065Swollman 1219336Sdfr#define Proc_format \ 1229336Sdfr "%5d %-8.8s %3d %4d %5s %5s %-5s %-6.6s %6s %5.2f%% %.14s" 1239336Sdfr 1244065Swollman 1254065Swollman/* process state names for the "STATE" column of the display */ 1264065Swollman/* the extra nulls in the string "run" are for adding a slash and 1274065Swollman the processor number when needed */ 1284065Swollman 1294065Swollmanchar *state_abbrev[] = 1304065Swollman{ 1319230Skarl "", "start", "run\0\0\0", "sleep", "stop", "zomb", 1321558Srgrimes}; 1331558Srgrimes 1349336Sdfr 1359336Sdfrstatic kvm_t *kd; 1369336Sdfr 1379336Sdfr/* these are retrieved from the kernel in _init */ 1389336Sdfr 1399336Sdfrstatic int hz; 1409336Sdfr 1419336Sdfr/* these are offsets obtained via nlist and used in the get_ functions */ 1421558Srgrimes 1431558Srgrimesstatic unsigned long cp_time_offset; 1441558Srgrimes 1451558Srgrimes/* these are for calculating cpu state percentages */ 1461558Srgrimesstatic long cp_time[CPUSTATES]; 1471558Srgrimesstatic long cp_old[CPUSTATES]; 1489336Sdfrstatic long cp_diff[CPUSTATES]; 1491558Srgrimes 1509336Sdfr/* these are for detailing the process states */ 1511558Srgrimes 1521558Srgrimesint process_states[7]; 1539336Sdfrchar *procstatenames[] = { 1549336Sdfr "", " starting, ", " running, ", " idle, ", " stopped, ", " zombie, ", 1551558Srgrimes NULL 1561558Srgrimes}; 1571558Srgrimes 1581558Srgrimes/* these are for detailing the cpu states */ 1591558Srgrimes 1601558Srgrimesint cpu_states[CPUSTATES]; 1611558Srgrimeschar *cpustatenames[] = { 1621558Srgrimes "user", "nice", "system", "interrupt", "idle", NULL 1631558Srgrimes}; 1649336Sdfr 1659336Sdfr/* these are for detailing the memory statistics */ 1669336Sdfr 1679336Sdfrint memory_stats[8]; 1689336Sdfrchar *memorynames[] = { 1691558Srgrimes "Real: ", "K/", "K act/tot ", "Free: ", "K ", 1701558Srgrimes#ifdef DOSWAP 1711558Srgrimes "Swap: ", "K/", "K used/tot", 1721558Srgrimes#endif 1731558Srgrimes NULL 1741558Srgrimes}; 1759336Sdfr 1769336Sdfr#ifdef ORDER 1779230Skarl/* these are names given to allowed sorting orders -- first is default */ 1781558Srgrimes 1799336Sdfrchar *ordernames[] = {"cpu", "size", "res", "time", "pri", NULL}; 1801558Srgrimes#endif 1811558Srgrimes 1829336Sdfr/* these are for keeping track of the proc array */ 1839336Sdfr 1849336Sdfrstatic int nproc; 1859336Sdfrstatic int onproc = -1; 1869336Sdfrstatic int pref_len; 1879336Sdfrstatic struct kinfo_proc *pbase; 1889336Sdfrstatic struct kinfo_proc **pref; 1899336Sdfr 1909336Sdfr/* these are for getting the memory statistics */ 1919336Sdfr 1921558Srgrimesstatic int pageshift; /* log base 2 of the pagesize */ 1931558Srgrimes 1941558Srgrimes/* define pagetok in terms of pageshift */ 1951558Srgrimes 1961558Srgrimes#define pagetok(size) ((size) << pageshift) 1971558Srgrimes 1981558Srgrimesint 19918286Sbdemachine_init(statics) 2001558Srgrimes 2011558Srgrimesstruct statics *statics; 2021558Srgrimes 2031558Srgrimes{ 2041558Srgrimes register int i = 0; 2051558Srgrimes register int pagesize; 2061558Srgrimes char errbuf[_POSIX2_LINE_MAX]; 2071558Srgrimes 2081558Srgrimes if ((kd = kvm_openfiles(NULL, NULL, NULL, O_RDONLY, errbuf)) == NULL) { 2091558Srgrimes warnx("%s", errbuf); 2101558Srgrimes return(-1); 2111558Srgrimes } 2124065Swollman 2131558Srgrimes setegid(getgid()); 2142999Swollman setgid(getgid()); 2159336Sdfr 2161558Srgrimes /* get the list of symbols we want to access in the kernel */ 2171558Srgrimes if (kvm_nlist(kd, nlst) <= 0) { 2181558Srgrimes warnx("nlist failed"); 2191558Srgrimes return(-1); 2209336Sdfr } 2219336Sdfr 2229336Sdfr /* make sure they were all found */ 2239336Sdfr if (i > 0 && check_nlist(nlst) > 0) 2249336Sdfr return(-1); 2259336Sdfr 2261558Srgrimes /* get the symbol values out of kmem */ 2271558Srgrimes (void) getkval(nlst[X_HZ].n_value, (int *)(&hz), sizeof(hz), 2281558Srgrimes nlst[X_HZ].n_name); 2294065Swollman 2301558Srgrimes /* stash away certain offsets for later use */ 2311558Srgrimes cp_time_offset = nlst[X_CP_TIME].n_value; 2321558Srgrimes 2339336Sdfr pbase = NULL; 2341558Srgrimes pref = NULL; 2359336Sdfr onproc = -1; 2369336Sdfr nproc = 0; 2379336Sdfr 2381558Srgrimes /* get the page size with "getpagesize" and calculate pageshift from it */ 2391558Srgrimes pagesize = getpagesize(); 2401558Srgrimes pageshift = 0; 2411558Srgrimes while (pagesize > 1) 2421558Srgrimes { 2431558Srgrimes pageshift++; 2441558Srgrimes pagesize >>= 1; 2451558Srgrimes } 2461558Srgrimes 2471558Srgrimes /* we only need the amount of log(2)1024 for our conversion */ 2481558Srgrimes pageshift -= LOG1024; 2491558Srgrimes 2501558Srgrimes /* fill in the statics information */ 2511558Srgrimes statics->procstate_names = procstatenames; 2521558Srgrimes statics->cpustate_names = cpustatenames; 2531558Srgrimes statics->memory_names = memorynames; 2541558Srgrimes#ifdef ORDER 2551558Srgrimes statics->order_names = ordernames; 2561558Srgrimes#endif 2571558Srgrimes 2581558Srgrimes /* all done! */ 2591558Srgrimes return(0); 2601558Srgrimes} 2611558Srgrimes 2621558Srgrimeschar *format_header(uname_field) 2631558Srgrimes 2641558Srgrimesregister char *uname_field; 2659336Sdfr 2661558Srgrimes{ 2679336Sdfr register char *ptr; 2681558Srgrimes 2691558Srgrimes ptr = header + UNAME_START; 2701558Srgrimes while (*uname_field != '\0') 2719336Sdfr { 2729336Sdfr *ptr++ = *uname_field++; 2739336Sdfr } 2749336Sdfr 2759336Sdfr return(header); 2769336Sdfr} 2779336Sdfr 2781558Srgrimesvoid 2791558Srgrimesget_system_info(si) 2801558Srgrimes 2819336Sdfrstruct system_info *si; 2821558Srgrimes 2831558Srgrimes{ 2841558Srgrimes int total; 2851558Srgrimes 2861558Srgrimes /* get the cp_time array */ 2871558Srgrimes (void) getkval(cp_time_offset, (int *)cp_time, sizeof(cp_time), 2881558Srgrimes "_cp_time"); 2891558Srgrimes 2901558Srgrimes /* convert load averages to doubles */ 2911558Srgrimes { 2921558Srgrimes register int i; 2931558Srgrimes register double *infoloadp; 2949336Sdfr struct loadavg sysload; 2951558Srgrimes size_t size = sizeof(sysload); 2969336Sdfr static int mib[] = { CTL_VM, VM_LOADAVG }; 2971558Srgrimes 2981558Srgrimes if (sysctl(mib, 2, &sysload, &size, NULL, 0) < 0) { 2991558Srgrimes warn("sysctl failed"); 3001558Srgrimes bzero(&total, sizeof(total)); 3011558Srgrimes } 3021558Srgrimes 3039336Sdfr infoloadp = si->load_avg; 3044065Swollman for (i = 0; i < 3; i++) 3054065Swollman *infoloadp++ = ((double) sysload.ldavg[i]) / sysload.fscale; 3064065Swollman } 3074065Swollman 3084065Swollman /* convert cp_time counts to percentages */ 3094065Swollman total = percentages(CPUSTATES, cpu_states, cp_time, cp_old, cp_diff); 3104065Swollman 3114065Swollman /* sum memory statistics */ 3124065Swollman { 3139336Sdfr struct vmtotal total; 3144065Swollman size_t size = sizeof(total); 3154065Swollman static int mib[] = { CTL_VM, VM_METER }; 3164065Swollman 3179336Sdfr /* get total -- systemwide main memory usage structure */ 3189336Sdfr if (sysctl(mib, 2, &total, &size, NULL, 0) < 0) { 3199336Sdfr warn("sysctl failed"); 3209336Sdfr bzero(&total, sizeof(total)); 3219336Sdfr } 3229336Sdfr /* convert memory stats to Kbytes */ 3234065Swollman memory_stats[0] = -1; 3244065Swollman memory_stats[1] = pagetok(total.t_arm); 3254065Swollman memory_stats[2] = pagetok(total.t_rm); 3264065Swollman memory_stats[3] = -1; 3274065Swollman memory_stats[4] = pagetok(total.t_free); 3284065Swollman memory_stats[5] = -1; 3294065Swollman#ifdef DOSWAP 3309336Sdfr if (!swapmode(&memory_stats[6], &memory_stats[7])) { 3314065Swollman memory_stats[6] = 0; 3324065Swollman memory_stats[7] = 0; 3339336Sdfr } 3344065Swollman#endif 3359336Sdfr } 3369336Sdfr 3379230Skarl /* set arrays and strings */ 3389230Skarl si->cpustates = cpu_states; 3394065Swollman si->memory = memory_stats; 3409336Sdfr si->last_pid = -1; 3419336Sdfr} 3429336Sdfr 3431558Srgrimesstatic struct handle handle; 3441558Srgrimes 3451558Srgrimescaddr_t get_process_info(si, sel, compare) 3461558Srgrimes 3471558Srgrimesstruct system_info *si; 3481558Srgrimesstruct process_select *sel; 3491558Srgrimesint (*compare) __P((const void *, const void *)); 3501558Srgrimes 3511558Srgrimes{ 3521558Srgrimes register int i; 3539336Sdfr register int total_procs; 3541558Srgrimes register int active_procs; 3551558Srgrimes register struct kinfo_proc **prefp; 3561558Srgrimes register struct kinfo_proc *pp; 3571558Srgrimes 3581558Srgrimes /* these are copied out of sel for speed */ 3591558Srgrimes int show_idle; 3601558Srgrimes int show_system; 3611558Srgrimes int show_uid; 3621558Srgrimes int show_command; 3631558Srgrimes 3641558Srgrimes 3651558Srgrimes if ((pbase = kvm_getprocs(kd, KERN_PROC_ALL, 0, &nproc)) == NULL) { 3661558Srgrimes warnx("%s", kvm_geterr(kd)); 3671558Srgrimes quit(23); 3681558Srgrimes } 3691558Srgrimes if (nproc > onproc) 3701558Srgrimes pref = (struct kinfo_proc **) realloc(pref, sizeof(struct kinfo_proc *) 3711558Srgrimes * (onproc = nproc)); 3721558Srgrimes if (pref == NULL) { 3739336Sdfr warnx("Out of memory."); 3741558Srgrimes quit(23); 3751558Srgrimes } 3761558Srgrimes /* get a pointer to the states summary array */ 3771558Srgrimes si->procstates = process_states; 3781558Srgrimes 3791558Srgrimes /* set up flags which define what we are going to select */ 3801558Srgrimes show_idle = sel->idle; 3811558Srgrimes show_system = sel->system; 3821558Srgrimes show_uid = sel->uid != -1; 3831558Srgrimes show_command = sel->command != NULL; 3841558Srgrimes 3851558Srgrimes /* count up process states and get pointers to interesting procs */ 3861558Srgrimes total_procs = 0; 3871558Srgrimes active_procs = 0; 3881558Srgrimes memset((char *)process_states, 0, sizeof(process_states)); 3891558Srgrimes prefp = pref; 3901558Srgrimes for (pp = pbase, i = 0; i < nproc; pp++, i++) 3911558Srgrimes { 3921558Srgrimes /* 3931558Srgrimes * Place pointers to each valid proc structure in pref[]. 3941558Srgrimes * Process slots that are actually in use have a non-zero 3951558Srgrimes * status field. Processes with SSYS set are system 3969336Sdfr * processes---these get ignored unless show_sysprocs is set. 3979336Sdfr */ 3989336Sdfr if (PP(pp, p_stat) != 0 && 3991558Srgrimes (show_system || ((PP(pp, p_flag) & P_SYSTEM) == 0))) 4001558Srgrimes { 4011558Srgrimes total_procs++; 4021558Srgrimes process_states[(unsigned char) PP(pp, p_stat)]++; 4031558Srgrimes if ((PP(pp, p_stat) != SZOMB) && 4041558Srgrimes (show_idle || (PP(pp, p_pctcpu) != 0) || 4051558Srgrimes (PP(pp, p_stat) == SRUN)) && 4061558Srgrimes (!show_uid || EP(pp, e_pcred.p_ruid) == (uid_t)sel->uid)) 4075966Sdg { 4081558Srgrimes *prefp++ = pp; 4091558Srgrimes active_procs++; 4101558Srgrimes } 4111558Srgrimes } 4121558Srgrimes } 4131558Srgrimes 4142999Swollman /* if requested, sort the "interesting" processes */ 4159336Sdfr if (compare != NULL) 4162999Swollman { 4172999Swollman qsort((char *)pref, active_procs, sizeof(struct kinfo_proc *), compare); 4182999Swollman } 41915770Swollman 4202999Swollman /* remember active and total counts */ 4212999Swollman si->p_total = total_procs; 4222999Swollman si->p_active = pref_len = active_procs; 42315770Swollman 42415770Swollman /* pass back a handle */ 4252999Swollman handle.next_proc = pref; 42615770Swollman handle.remaining = active_procs; 4279336Sdfr return((caddr_t)&handle); 4289336Sdfr} 4299336Sdfr 4301558Srgrimeschar fmt[MAX_COLS]; /* static area where result is built */ 4311558Srgrimes 4321558Srgrimeschar *format_next_process(handle, get_userid) 4331558Srgrimes 4341558Srgrimescaddr_t handle; 4351558Srgrimeschar *(*get_userid)(); 4361558Srgrimes 4371558Srgrimes{ 4381558Srgrimes register struct kinfo_proc *pp; 4391558Srgrimes register int cputime; 4401558Srgrimes register double pct; 4411558Srgrimes struct handle *hp; 4421558Srgrimes char waddr[sizeof(void *) * 2 + 3]; /* Hexify void pointer */ 4431558Srgrimes char *p_wait; 4441558Srgrimes 4451558Srgrimes /* find and remember the next proc structure */ 4461558Srgrimes hp = (struct handle *)handle; 4471558Srgrimes pp = *(hp->next_proc++); 4481558Srgrimes hp->remaining--; 4491558Srgrimes 4501558Srgrimes 4511558Srgrimes /* get the process's user struct and set cputime */ 4521558Srgrimes if ((PP(pp, p_flag) & P_INMEM) == 0) { 4531558Srgrimes /* 4549336Sdfr * Print swapped processes as <pname> 4551558Srgrimes */ 4561558Srgrimes char *comm = PP(pp, p_comm); 4571558Srgrimes#define COMSIZ sizeof(PP(pp, p_comm)) 4581558Srgrimes char buf[COMSIZ]; 4591558Srgrimes (void) strncpy(buf, comm, COMSIZ); 4601558Srgrimes comm[0] = '<'; 4619336Sdfr (void) strncpy(&comm[1], buf, COMSIZ - 2); 4629336Sdfr comm[COMSIZ - 2] = '\0'; 4639336Sdfr (void) strncat(comm, ">", COMSIZ - 1); 4649336Sdfr comm[COMSIZ - 1] = '\0'; 4659336Sdfr } 4661558Srgrimes 4671558Srgrimes cputime = (PP(pp, p_uticks) + PP(pp, p_sticks) + PP(pp, p_iticks)) / hz; 4686043Sdfr 4696043Sdfr /* calculate the base for cpu percentages */ 4706043Sdfr pct = pctdouble(PP(pp, p_pctcpu)); 4716043Sdfr 4721558Srgrimes if (PP(pp, p_wchan)) 4731558Srgrimes if (PP(pp, p_wmesg)) 4749336Sdfr p_wait = EP(pp, e_wmesg); 4759336Sdfr else { 4769336Sdfr snprintf(waddr, sizeof(waddr), "%lx", 4779336Sdfr (unsigned long)(PP(pp, p_wchan)) & ~KERNBASE); 4789336Sdfr p_wait = waddr; 4799336Sdfr } 4809336Sdfr else 4819336Sdfr p_wait = "-"; 4821558Srgrimes 4839336Sdfr /* format this entry */ 4849336Sdfr snprintf(fmt, MAX_COLS, 4859336Sdfr Proc_format, 4869336Sdfr PP(pp, p_pid), 4879336Sdfr (*get_userid)(EP(pp, e_pcred.p_ruid)), 4889336Sdfr PP(pp, p_priority) - PZERO, 4899336Sdfr PP(pp, p_nice) - NZERO, 4909336Sdfr format_k(pagetok(PROCSIZE(pp))), 4919336Sdfr format_k(pagetok(VP(pp, vm_rssize))), 4929336Sdfr (PP(pp, p_stat) == SSLEEP && PP(pp, p_slptime) > MAXSLP) 4939336Sdfr ? "idle" : state_abbrev[(unsigned char) PP(pp, p_stat)], 4949336Sdfr p_wait, 4959336Sdfr format_time(cputime), 4969336Sdfr 100.0 * pct, 4979336Sdfr printable(PP(pp, p_comm))); 4989336Sdfr 4999336Sdfr /* return the result */ 5009336Sdfr return(fmt); 5019336Sdfr} 5029336Sdfr 5039336Sdfr 5049336Sdfr/* 5059336Sdfr * check_nlist(nlst) - checks the nlist to see if any symbols were not 5069336Sdfr * found. For every symbol that was not found, a one-line 5079336Sdfr * message is printed to stderr. The routine returns the 5089336Sdfr * number of symbols NOT found. 5099336Sdfr */ 5109336Sdfr 5119336Sdfrstatic int check_nlist(nlst) 5129336Sdfr 5139336Sdfrregister struct nlist *nlst; 5149336Sdfr 5159336Sdfr{ 5169336Sdfr register int i; 5179336Sdfr 5189336Sdfr /* check to see if we got ALL the symbols we requested */ 5199336Sdfr /* this will write one line to stderr for every symbol not found */ 5209336Sdfr 5219336Sdfr i = 0; 5229336Sdfr while (nlst->n_name != NULL) 5239336Sdfr { 5249336Sdfr if (nlst->n_type == 0) 5259336Sdfr { 5269336Sdfr /* this one wasn't found */ 5279336Sdfr (void) fprintf(stderr, "kernel: no symbol named `%s'\n", 5289336Sdfr nlst->n_name); 5299336Sdfr i = 1; 5301558Srgrimes } 5311558Srgrimes nlst++; 5321558Srgrimes } 5331558Srgrimes 5341558Srgrimes return(i); 5351558Srgrimes} 5361558Srgrimes 5371558Srgrimes 5381558Srgrimes/* 5391558Srgrimes * getkval(offset, ptr, size, refstr) - get a value out of the kernel. 5401558Srgrimes * "offset" is the byte offset into the kernel for the desired value, 5411558Srgrimes * "ptr" points to a buffer into which the value is retrieved, 5421558Srgrimes * "size" is the size of the buffer (and the object to retrieve), 5431558Srgrimes * "refstr" is a reference string used when printing error meessages, 5441558Srgrimes * if "refstr" starts with a '!', then a failure on read will not 5451558Srgrimes * be fatal (this may seem like a silly way to do things, but I 5461558Srgrimes * really didn't want the overhead of another argument). 5471558Srgrimes * 5481558Srgrimes */ 5491558Srgrimes 5509336Sdfrstatic int getkval(offset, ptr, size, refstr) 5511558Srgrimes 5529336Sdfrunsigned long offset; 5531558Srgrimesint *ptr; 5541558Srgrimesint size; 5551558Srgrimeschar *refstr; 5561558Srgrimes 5571558Srgrimes{ 5581558Srgrimes if (kvm_read(kd, offset, (char *) ptr, size) != size) 5591558Srgrimes { 5601558Srgrimes if (*refstr == '!') 5611558Srgrimes { 5621558Srgrimes return(0); 5631558Srgrimes } 5641558Srgrimes else 5651558Srgrimes { 5661558Srgrimes warn("kvm_read for %s", refstr); 5671558Srgrimes quit(23); 5681558Srgrimes } 5691558Srgrimes } 5701558Srgrimes return(1); 5711558Srgrimes} 5721558Srgrimes 5731558Srgrimes/* comparison routine for qsort */ 5741558Srgrimes 5751558Srgrimesstatic unsigned char sorted_state[] = 5761558Srgrimes{ 5771558Srgrimes 0, /* not used */ 5781558Srgrimes 4, /* start */ 5791558Srgrimes 5, /* run */ 5801558Srgrimes 2, /* sleep */ 5811558Srgrimes 3, /* stop */ 5821558Srgrimes 1 /* zombie */ 5831558Srgrimes}; 5841558Srgrimes 5851558Srgrimes#ifdef ORDER 5861558Srgrimes 5871558Srgrimes/* 5881558Srgrimes * proc_compares - comparison functions for "qsort" 5891558Srgrimes */ 5901558Srgrimes 5911558Srgrimes/* 5921558Srgrimes * First, the possible comparison keys. These are defined in such a way 5931558Srgrimes * that they can be merely listed in the source code to define the actual 5941558Srgrimes * desired ordering. 5951558Srgrimes */ 5961558Srgrimes 5971558Srgrimes 5981558Srgrimes#define ORDERKEY_PCTCPU \ 5991558Srgrimes if (lresult = (pctcpu)PP(p2, p_pctcpu) - (pctcpu)PP(p1, p_pctcpu), \ 6001558Srgrimes (result = lresult > 0 ? 1 : lresult < 0 ? -1 : 0) == 0) 6011558Srgrimes#define ORDERKEY_CPUTIME \ 6021558Srgrimes if ((result = PP(p2, p_rtime.tv_sec) - PP(p1, p_rtime.tv_sec)) == 0) \ 6031558Srgrimes if ((result = PP(p2, p_rtime.tv_usec) - \ 6041558Srgrimes PP(p1, p_rtime.tv_usec)) == 0) 6051558Srgrimes#define ORDERKEY_STATE \ 6061558Srgrimes if ((result = sorted_state[(unsigned char) PP(p2, p_stat)] - \ 6071558Srgrimes sorted_state[(unsigned char) PP(p1, p_stat)]) == 0) 6081558Srgrimes#define ORDERKEY_PRIO \ 6091558Srgrimes if ((result = PP(p2, p_priority) - PP(p1, p_priority)) == 0) 6101558Srgrimes#define ORDERKEY_RSSIZE \ 6112776Sphk if ((result = VP(p2, vm_rssize) - VP(p1, vm_rssize)) == 0) 6122776Sphk#define ORDERKEY_MEM \ 6132776Sphk if ((result = PROCSIZE(p2) - PROCSIZE(p1)) == 0) 6142776Sphk 6152776Sphk 6162776Sphk/* compare_cpu - the comparison function for sorting by cpu percentage */ 6179336Sdfr 6182776Sphkint 6192776Sphkcompare_cpu(v1, v2) 6201558Srgrimes 6211558Srgrimesconst void *v1, *v2; 6221558Srgrimes 6231558Srgrimes{ 6242776Sphk register struct proc **pp1 = (struct proc **)v1; 6251558Srgrimes register struct proc **pp2 = (struct proc **)v2; 6261558Srgrimes register struct kinfo_proc *p1; 6271558Srgrimes register struct kinfo_proc *p2; 6281558Srgrimes register int result; 6291558Srgrimes register pctcpu lresult; 6309336Sdfr 6311558Srgrimes /* remove one level of indirection */ 6329336Sdfr p1 = *(struct kinfo_proc **) pp1; 6339336Sdfr p2 = *(struct kinfo_proc **) pp2; 6349336Sdfr 6359336Sdfr ORDERKEY_PCTCPU 6369336Sdfr ORDERKEY_CPUTIME 6379336Sdfr ORDERKEY_STATE 6389336Sdfr ORDERKEY_PRIO 6391558Srgrimes ORDERKEY_RSSIZE 6401558Srgrimes ORDERKEY_MEM 6411558Srgrimes ; 6421558Srgrimes return(result); 6439230Skarl} 6449230Skarl 6459336Sdfr/* compare_size - the comparison function for sorting by total memory usage */ 6461558Srgrimes 6471558Srgrimesint 6481558Srgrimescompare_size(v1, v2) 6491558Srgrimes 6501558Srgrimesconst void *v1, *v2; 6511558Srgrimes 6529336Sdfr{ 6539336Sdfr register struct proc **pp1 = (struct proc **)v1; 6549336Sdfr register struct proc **pp2 = (struct proc **)v2; 6559336Sdfr register struct kinfo_proc *p1; 6569336Sdfr register struct kinfo_proc *p2; 6579336Sdfr register int result; 6589336Sdfr register pctcpu lresult; 6591558Srgrimes 6604822Sats /* remove one level of indirection */ 6611558Srgrimes p1 = *(struct kinfo_proc **) pp1; 6621558Srgrimes p2 = *(struct kinfo_proc **) pp2; 6631558Srgrimes 6641558Srgrimes ORDERKEY_MEM 6659336Sdfr ORDERKEY_RSSIZE 6669336Sdfr ORDERKEY_PCTCPU 6679336Sdfr ORDERKEY_CPUTIME 6689336Sdfr ORDERKEY_STATE 6699336Sdfr ORDERKEY_PRIO 6701558Srgrimes ; 6711558Srgrimes 6721558Srgrimes return(result); 6731558Srgrimes} 6741558Srgrimes 6751558Srgrimes/* compare_res - the comparison function for sorting by resident set size */ 6761558Srgrimes 6771558Srgrimesint 6781558Srgrimescompare_res(v1, v2) 6791558Srgrimes 6801558Srgrimesconst void *v1, *v2; 6811558Srgrimes 6821558Srgrimes{ 6831558Srgrimes register struct proc **pp1 = (struct proc **)v1; 6841558Srgrimes register struct proc **pp2 = (struct proc **)v2; 6851558Srgrimes register struct kinfo_proc *p1; 6861558Srgrimes register struct kinfo_proc *p2; 6871558Srgrimes register int result; 6881558Srgrimes register pctcpu lresult; 6891558Srgrimes 6901558Srgrimes /* remove one level of indirection */ 6911558Srgrimes p1 = *(struct kinfo_proc **) pp1; 6921558Srgrimes p2 = *(struct kinfo_proc **) pp2; 6931558Srgrimes 6941558Srgrimes ORDERKEY_RSSIZE 6951558Srgrimes ORDERKEY_MEM 6961558Srgrimes ORDERKEY_PCTCPU 6971558Srgrimes ORDERKEY_CPUTIME 6981558Srgrimes ORDERKEY_STATE 6991558Srgrimes ORDERKEY_PRIO 7001558Srgrimes ; 7011558Srgrimes 7021558Srgrimes return(result); 7031558Srgrimes} 7045966Sdg 7051558Srgrimes/* compare_time - the comparison function for sorting by CPU time */ 7061558Srgrimes 7071558Srgrimesint 7081558Srgrimescompare_time(v1, v2) 7091558Srgrimes 7101558Srgrimesconst void *v1, *v2; 7111558Srgrimes 7121558Srgrimes{ 7131558Srgrimes register struct proc **pp1 = (struct proc **)v1; 7141558Srgrimes register struct proc **pp2 = (struct proc **)v2; 7151558Srgrimes register struct kinfo_proc *p1; 7161558Srgrimes register struct kinfo_proc *p2; 7171558Srgrimes register int result; 7181558Srgrimes register pctcpu lresult; 7199336Sdfr 7209336Sdfr /* remove one level of indirection */ 7211558Srgrimes p1 = *(struct kinfo_proc **) pp1; 7221558Srgrimes p2 = *(struct kinfo_proc **) pp2; 7231558Srgrimes 7241558Srgrimes ORDERKEY_CPUTIME 7251558Srgrimes ORDERKEY_PCTCPU 7261558Srgrimes ORDERKEY_STATE 7271558Srgrimes ORDERKEY_PRIO 7281558Srgrimes ORDERKEY_MEM 7291558Srgrimes ORDERKEY_RSSIZE 7301558Srgrimes ; 7311558Srgrimes 7321558Srgrimes return(result); 7331558Srgrimes} 7341558Srgrimes 7351558Srgrimes/* compare_prio - the comparison function for sorting by CPU time */ 7361558Srgrimes 7371558Srgrimesint 7381558Srgrimescompare_prio(v1, v2) 7399336Sdfr 7401558Srgrimesconst void *v1, *v2; 7419336Sdfr 7429336Sdfr{ 7439336Sdfr register struct proc **pp1 = (struct proc **)v1; 7449336Sdfr register struct proc **pp2 = (struct proc **)v2; 7451558Srgrimes register struct kinfo_proc *p1; 7461558Srgrimes register struct kinfo_proc *p2; 7471558Srgrimes register int result; 7489336Sdfr register pctcpu lresult; 7499336Sdfr 7509336Sdfr /* remove one level of indirection */ 7519336Sdfr p1 = *(struct kinfo_proc **) pp1; 7529336Sdfr p2 = *(struct kinfo_proc **) pp2; 7539336Sdfr 7549336Sdfr ORDERKEY_PRIO 7559336Sdfr ORDERKEY_PCTCPU 7569336Sdfr ORDERKEY_CPUTIME 7579336Sdfr ORDERKEY_STATE 7589336Sdfr ORDERKEY_RSSIZE 7599336Sdfr ORDERKEY_MEM 7609336Sdfr ; 7619336Sdfr 7629336Sdfr return(result); 7639336Sdfr} 7649336Sdfr 7659336Sdfrint (*proc_compares[])() = { 7669336Sdfr compare_cpu, 7679336Sdfr compare_size, 7689336Sdfr compare_res, 7699336Sdfr compare_time, 7709336Sdfr compare_prio, 7719336Sdfr NULL 7729336Sdfr}; 7739336Sdfr#else 7749336Sdfr/* 7759336Sdfr * proc_compare - comparison function for "qsort" 7769336Sdfr * Compares the resource consumption of two processes using five 7771558Srgrimes * distinct keys. The keys (in descending order of importance) are: 7781558Srgrimes * percent cpu, cpu ticks, state, resident set size, total virtual 77918286Sbde * memory usage. The process states are ordered as follows (from least 7801558Srgrimes * to most important): zombie, sleep, stop, start, run. The array 7811558Srgrimes * declaration below maps a process state index into a number that 7829336Sdfr * reflects this ordering. 7839336Sdfr */ 7849336Sdfr 7859336Sdfrint 7869336Sdfrproc_compare(v1, v2) 7871558Srgrimes 7881558Srgrimesconst void *v1, *v2; 789 790{ 791 register struct proc **pp1 = (struct proc **)v1; 792 register struct proc **pp2 = (struct proc **)v2; 793 register struct kinfo_proc *p1; 794 register struct kinfo_proc *p2; 795 register int result; 796 register pctcpu lresult; 797 798 /* remove one level of indirection */ 799 p1 = *(struct kinfo_proc **) pp1; 800 p2 = *(struct kinfo_proc **) pp2; 801 802 /* compare percent cpu (pctcpu) */ 803 if ((lresult = PP(p2, p_pctcpu) - PP(p1, p_pctcpu)) == 0) 804 { 805 /* use CPU usage to break the tie */ 806 if ((result = PP(p2, p_rtime).tv_sec - PP(p1, p_rtime).tv_sec) == 0) 807 { 808 /* use process state to break the tie */ 809 if ((result = sorted_state[(unsigned char) PP(p2, p_stat)] - 810 sorted_state[(unsigned char) PP(p1, p_stat)]) == 0) 811 { 812 /* use priority to break the tie */ 813 if ((result = PP(p2, p_priority) - PP(p1, p_priority)) == 0) 814 { 815 /* use resident set size (rssize) to break the tie */ 816 if ((result = VP(p2, vm_rssize) - VP(p1, vm_rssize)) == 0) 817 { 818 /* use total memory to break the tie */ 819 result = PROCSIZE(p2) - PROCSIZE(p1); 820 } 821 } 822 } 823 } 824 } 825 else 826 { 827 result = lresult < 0 ? -1 : 1; 828 } 829 830 return(result); 831} 832#endif 833 834/* 835 * proc_owner(pid) - returns the uid that owns process "pid", or -1 if 836 * the process does not exist. 837 * It is EXTREMLY IMPORTANT that this function work correctly. 838 * If top runs setuid root (as in SVR4), then this function 839 * is the only thing that stands in the way of a serious 840 * security problem. It validates requests for the "kill" 841 * and "renice" commands. 842 */ 843 844int proc_owner(pid) 845 846pid_t pid; 847 848{ 849 register int cnt; 850 register struct kinfo_proc **prefp; 851 register struct kinfo_proc *pp; 852 853 prefp = pref; 854 cnt = pref_len; 855 while (--cnt >= 0) 856 { 857 pp = *prefp++; 858 if (PP(pp, p_pid) == pid) 859 { 860 return((int)EP(pp, e_pcred.p_ruid)); 861 } 862 } 863 return(-1); 864} 865 866#ifdef DOSWAP 867/* 868 * swapmode is based on a program called swapinfo written 869 * by Kevin Lahey <kml@rokkaku.atl.ga.us>. 870 */ 871 872#define SVAR(var) __STRING(var) /* to force expansion */ 873#define KGET(idx, var) \ 874 KGET1(idx, &var, sizeof(var), SVAR(var)) 875#define KGET1(idx, p, s, msg) \ 876 KGET2(nlst[idx].n_value, p, s, msg) 877#define KGET2(addr, p, s, msg) \ 878 if (kvm_read(kd, (u_long)(addr), p, s) != s) \ 879 warnx("cannot read %s: %s", msg, kvm_geterr(kd)) 880 881static int 882swapmode(used, total) 883int *used; 884int *total; 885{ 886 int nswap, nswdev, dmmax, nswapmap, niswap, niswdev; 887 int s, e, i, l, nfree; 888 struct swdevt *sw; 889 long *perdev; 890 struct map *swapmap, *kswapmap; 891 struct mapent *mp, *freemp; 892 893 KGET(VM_NSWAP, nswap); 894 KGET(VM_NSWDEV, nswdev); 895 KGET(VM_DMMAX, dmmax); 896 KGET(VM_NSWAPMAP, nswapmap); 897 KGET(VM_SWAPMAP, kswapmap); /* kernel `swapmap' is a pointer */ 898 if (nswap == 0) { 899 *used = 0; 900 *total = 0; 901 return (1); 902 } 903 if ((sw = malloc(nswdev * sizeof(*sw))) == NULL || 904 (perdev = malloc(nswdev * sizeof(*perdev))) == NULL || 905 (freemp = mp = malloc(nswapmap * sizeof(*mp))) == NULL) 906 err(1, "malloc"); 907 KGET1(VM_SWDEVT, sw, nswdev * sizeof(*sw), "swdevt"); 908 KGET2((long)kswapmap, mp, nswapmap * sizeof(*mp), "swapmap"); 909 910 /* Supports sequential swap */ 911 if (nlst[VM_NISWAP].n_value != 0) { 912 KGET(VM_NISWAP, niswap); 913 KGET(VM_NISWDEV, niswdev); 914 } else { 915 niswap = nswap; 916 niswdev = nswdev; 917 } 918 919 /* First entry in map is `struct map'; rest are mapent's. */ 920 swapmap = (struct map *)mp; 921 if (nswapmap != swapmap->m_limit - (struct mapent *)kswapmap) 922 errx(1, "panic: nswapmap goof"); 923 924 /* Count up swap space. */ 925 nfree = 0; 926 memset(perdev, 0, nswdev * sizeof(*perdev)); 927 for (mp++; mp->m_addr != 0; mp++) { 928 s = mp->m_addr; /* start of swap region */ 929 e = mp->m_addr + mp->m_size; /* end of region */ 930 nfree += mp->m_size; 931 932 /* 933 * Swap space is split up among the configured disks. 934 * 935 * For interleaved swap devices, the first dmmax blocks 936 * of swap space some from the first disk, the next dmmax 937 * blocks from the next, and so on up to niswap blocks. 938 * 939 * Sequential swap devices follow the interleaved devices 940 * (i.e. blocks starting at niswap) in the order in which 941 * they appear in the swdev table. The size of each device 942 * will be a multiple of dmmax. 943 * 944 * The list of free space joins adjacent free blocks, 945 * ignoring device boundries. If we want to keep track 946 * of this information per device, we'll just have to 947 * extract it ourselves. We know that dmmax-sized chunks 948 * cannot span device boundaries (interleaved or sequential) 949 * so we loop over such chunks assigning them to devices. 950 */ 951 i = -1; 952 while (s < e) { /* XXX this is inefficient */ 953 int bound = roundup(s+1, dmmax); 954 955 if (bound > e) 956 bound = e; 957 if (bound <= niswap) { 958 /* Interleaved swap chunk. */ 959 if (i == -1) 960 i = (s / dmmax) % niswdev; 961 perdev[i] += bound - s; 962 if (++i >= niswdev) 963 i = 0; 964 } else { 965 /* Sequential swap chunk. */ 966 if (i < niswdev) { 967 i = niswdev; 968 l = niswap + sw[i].sw_nblks; 969 } 970 while (s >= l) { 971 /* XXX don't die on bogus blocks */ 972 if (i == nswdev-1) 973 break; 974 l += sw[++i].sw_nblks; 975 } 976 perdev[i] += bound - s; 977 } 978 s = bound; 979 } 980 } 981 982 *total = 0; 983 for (i = 0; i < nswdev; i++) { 984 int xsize, xfree; 985 986 xsize = sw[i].sw_nblks; 987 xfree = perdev[i]; 988 *total += xsize; 989 } 990 991 /* 992 * If only one partition has been set up via swapon(8), we don't 993 * need to bother with totals. 994 */ 995#if DEV_BSHIFT < 10 996 *used = (*total - nfree) >> (10 - DEV_BSHIFT); 997 *total >>= 10 - DEV_BSHIFT; 998#elif DEV_BSHIFT > 10 999 *used = (*total - nfree) >> (DEV_BSHIFT - 10); 1000 *total >>= DEV_BSHIFT - 10; 1001#endif 1002 free (sw); free (freemp); free (perdev); 1003 return 1; 1004} 1005#endif 1006