xref: /netbsd/external/bsd/top/dist/machine/m_sunos4.c (revision 6550d01e)
1 /*
2  * Copyright (c) 1984 through 2008, William LeFebvre
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions are met:
7  *
8  *     * Redistributions of source code must retain the above copyright
9  * notice, this list of conditions and the following disclaimer.
10  *
11  *     * Redistributions in binary form must reproduce the above
12  * copyright notice, this list of conditions and the following disclaimer
13  * in the documentation and/or other materials provided with the
14  * distribution.
15  *
16  *     * Neither the name of William LeFebvre nor the names of other
17  * contributors may be used to endorse or promote products derived from
18  * this software without specific prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
23  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
24  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
26  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31  */
32 
33 /*
34  * top - a top users display for Unix
35  *
36  * SYNOPSIS:  any Sun running SunOS version 4.x
37  *
38  * DESCRIPTION:
39  * This is the machine-dependent module for SunOS 4.x.
40  * This makes top work on the following systems:
41  *	SunOS 4.0
42  *	SunOS 4.0.1
43  *	SunOS 4.0.2 (including 386i architecture)
44  *	SunOS 4.0.3
45  *	SunOS 4.1
46  *	SunOS 4.1.1
47  *	SunOS 4.1.2 (including MP architectures)
48  *	SunOS 4.1.3 (including MP architectures)
49  *      SunOS 4.1.3_U1 (including MP architectures)
50  *      SunOS 4.1.4 (including MP architectures)
51  *	Solbourne OS/MP PRIOR to 4.1A
52  *
53  * LIBS:  -lkvm
54  *
55  * CFLAGS: -DHAVE_GETOPT -DORDER
56  *
57  * AUTHOR:  William LeFebvre <wnl@groupsys.com>
58  * Solbourne support by David MacKenzie <djm@eng.umd.edu>
59  */
60 
61 /*
62  * #ifdef MULTIPROCESSOR means Sun MP.
63  * #ifdef solbourne is for Solbourne.
64  */
65 
66 #include "config.h"
67 #include <sys/types.h>
68 #include <sys/signal.h>
69 
70 /* make sure param.h gets loaded with KERNEL defined to get PZERO & NZERO */
71 #define KERNEL
72 #include <sys/param.h>
73 #undef KERNEL
74 
75 #include <stdio.h>
76 #include <kvm.h>
77 #include <nlist.h>
78 #include <math.h>
79 #include <sys/dir.h>
80 #include <sys/user.h>
81 #include <sys/proc.h>
82 #include <sys/dk.h>
83 #include <sys/vm.h>
84 #include <sys/file.h>
85 #include <sys/time.h>
86 #include <vm/page.h>
87 
88 #ifdef solbourne
89 #include <sys/syscall.h>
90 #endif
91 
92 /* Older versions of SunOS don't have a typedef for pid_t.
93    Hopefully this will catch all those cases without causing other problems.
94  */
95 #ifndef __sys_stdtypes_h
96 typedef int pid_t;
97 #endif
98 
99 #include "top.h"
100 #include "machine.h"
101 #include "utils.h"
102 
103 /* declarations for load_avg */
104 #include "loadavg.h"
105 
106 /* get_process_info passes back a handle.  This is what it looks like: */
107 
108 struct handle
109 {
110     struct proc **next_proc;	/* points to next valid proc pointer */
111     int remaining;		/* number of pointers remaining */
112 };
113 
114 /* define what weighted cpu is.  */
115 #define weighted_cpu(pct, pp) ((pp)->p_time == 0 ? 0.0 : \
116 			 ((pct) / (1.0 - exp((pp)->p_time * logcpu))))
117 
118 /* what we consider to be process size: */
119 #define PROCSIZE(pp) ((pp)->p_tsize + (pp)->p_dsize + (pp)->p_ssize)
120 
121 /* definitions for indices in the nlist array */
122 #define X_AVENRUN	0
123 #define X_CCPU		1
124 #define X_MPID		2
125 #define X_NPROC		3
126 #define X_PROC		4
127 #define X_TOTAL		5
128 #define X_CP_TIME	6
129 #define X_PAGES		7
130 #define X_EPAGES	8
131 
132 static struct nlist nlst[] = {
133 #ifdef i386
134     { "avenrun" },		/* 0 */
135     { "ccpu" },			/* 1 */
136     { "mpid" },			/* 2 */
137     { "nproc" },		/* 3 */
138     { "proc" },			/* 4 */
139     { "total" },		/* 5 */
140     { "cp_time" },		/* 6 */
141     { "pages" },		/* 7 */
142     { "epages" },		/* 8 */
143 #else
144     { "_avenrun" },		/* 0 */
145     { "_ccpu" },		/* 1 */
146     { "_mpid" },		/* 2 */
147     { "_nproc" },		/* 3 */
148     { "_proc" },		/* 4 */
149     { "_total" },		/* 5 */
150     { "_cp_time" },		/* 6 */
151     { "_pages" },		/* 7 */
152     { "_epages" },		/* 8 */
153 #ifdef MULTIPROCESSOR
154     { "_ncpu" },
155 #define X_NCPU		9
156     { "_xp_time" },
157 #define X_XP_TIME	10
158 #endif
159 #endif
160     { 0 }
161 };
162 
163 /*
164  *  These definitions control the format of the per-process area
165  */
166 
167 static char header[] =
168   "  PID X        PRI NICE  SIZE   RES STATE   TIME   WCPU    CPU COMMAND";
169 /* 0123456   -- field to fill in starts at header+6 */
170 #define UNAME_START 6
171 
172 #define Proc_format \
173 	"%5d %-8.8s %3d %4d %5s %5s %-5s %-6s %5.2f%% %5.2f%% %s"
174 
175 
176 /* process state names for the "STATE" column of the display */
177 /* the extra nulls in the string "run" are for adding a slash and
178    the processor number when needed */
179 
180 char *state_abbrev[] =
181 {
182     "", "sleep", "WAIT", "run\0\0\0", "start", "zomb", "stop"
183 };
184 
185 /* values that we stash away in _init and use in later routines */
186 
187 static double logcpu;
188 kvm_t *kd;
189 
190 /* these are retrieved from the kernel in _init */
191 
192 static unsigned long proc;
193 static          int  nproc;
194 static load_avg ccpu;
195 static unsigned long pages;
196 static unsigned long epages;
197 static          int  ncpu = 0;
198 
199 /* these are offsets obtained via nlist and used in the get_ functions */
200 
201 static unsigned long mpid_offset;
202 static unsigned long avenrun_offset;
203 static unsigned long total_offset;
204 static unsigned long cp_time_offset;
205 #ifdef MULTIPROCESSOR
206 static unsigned long xp_time_offset;
207 #endif
208 
209 /* these are for calculating cpu state percentages */
210 
211 static long cp_time[CPUSTATES];
212 static long cp_old[CPUSTATES];
213 static long cp_diff[CPUSTATES];
214 #ifdef MULTIPROCESSOR
215 static long xp_time[NCPU][XPSTATES];
216 /* for now we only accumulate spin time, but extending this to pick up
217    other stuff in xp_time is trivial.  */
218 static long xp_old[NCPU];
219 #endif
220 
221 /* these are for detailing the process states */
222 
223 int process_states[7];
224 char *procstatenames[] = {
225     "", " sleeping, ", " ABANDONED, ", " running, ", " starting, ",
226     " zombie, ", " stopped, ",
227     NULL
228 };
229 
230 /* these are for detailing the cpu states */
231 
232 int cpu_states[5];
233 char *cpustatenames[] = {
234     "user", "nice", "system", "idle",
235 #ifdef MULTIPROCESSOR
236     "spin",
237 #define XCP_SPIN 4
238 #endif
239     NULL
240 };
241 
242 /* these are for detailing the memory statistics */
243 
244 long memory_stats[4];
245 char *memorynames[] = {
246     "K available, ", "K in use, ", "K free, ", "K locked", NULL
247 };
248 
249 /* these are names given to allowed sorting orders -- first is default */
250 char *ordernames[] =
251 {"cpu", "size", "res", NULL};
252 
253 /* forward definitions for comparison functions */
254 int compare_cpu();
255 int compare_size();
256 int compare_res();
257 
258 int (*proc_compares[])() = {
259     compare_cpu,
260     compare_size,
261     compare_res,
262     NULL };
263 
264 
265 /* these are for keeping track of the proc array */
266 
267 static int bytes;
268 static int pref_len;
269 static struct proc *pbase;
270 static struct proc **pref;
271 
272 /* these are for getting the memory statistics */
273 
274 static struct page *physpage;
275 static int bytesize;
276 static int count;
277 static int pageshift;		/* log base 2 of the pagesize */
278 
279 /* define pagetok in terms of pageshift */
280 
281 #define pagetok(size) ((size) << pageshift)
282 
283 /* useful externals */
284 extern int errno;
285 extern char *sys_errlist[];
286 
287 long lseek();
288 long time();
289 
290 machine_init(statics)
291 
292 struct statics *statics;
293 
294 {
295     register int i;
296     register int pagesize;
297 
298     /* initialize the kernel interface */
299     if ((kd = kvm_open(NULL, NULL, NULL, O_RDONLY, "top")) == NULL)
300     {
301 	perror("kvm_open");
302 	return(-1);
303     }
304 
305     /* get the list of symbols we want to access in the kernel */
306     if ((i = kvm_nlist(kd, nlst)) < 0)
307     {
308 	fprintf(stderr, "top: nlist failed\n");
309 	return(-1);
310     }
311 
312 #ifdef MULTIPROCESSOR
313     /* were ncpu and xp_time not found in the nlist? */
314     if (i > 0 && nlst[X_NCPU].n_type == 0 && nlst[X_XP_TIME].n_type == 0)
315     {
316 	/* we were compiled on an MP system but we are not running on one */
317 	/* so we will pretend this didn't happen and set ncpu = 1 */
318 	i -= 2;
319 	ncpu = 1;
320     }
321 #endif
322 
323 #ifdef solbourne
324     {
325 	unsigned int status, type;
326 
327 	/* Get the number of CPUs on this system.  */
328 	syscall(SYS_getcpustatus, &status, &ncpu, &type);
329     }
330 #endif
331 
332     /* make sure they were all found */
333     if (i > 0 && check_nlist(nlst) > 0)
334     {
335 	return(-1);
336     }
337 
338     /* get the symbol values out of kmem */
339     (void) getkval(nlst[X_PROC].n_value,   (int *)(&proc),	sizeof(proc),
340 	    nlst[X_PROC].n_name);
341     (void) getkval(nlst[X_NPROC].n_value,  &nproc,		sizeof(nproc),
342 	    nlst[X_NPROC].n_name);
343     (void) getkval(nlst[X_CCPU].n_value,   (int *)(&ccpu),	sizeof(ccpu),
344 	    nlst[X_CCPU].n_name);
345     (void) getkval(nlst[X_PAGES].n_value,  (int *)(&pages),	sizeof(pages),
346 	    nlst[X_PAGES].n_name);
347     (void) getkval(nlst[X_EPAGES].n_value, (int *)(&epages),	sizeof(epages),
348 	    nlst[X_EPAGES].n_name);
349 #ifdef MULTIPROCESSOR
350     if (ncpu == 0)
351     {
352 	/* if ncpu > 0 then we are not really on an MP system */
353 	(void) getkval(nlst[X_NCPU].n_value,   (int *)(&ncpu),	sizeof(ncpu),
354 		       nlst[X_NCPU].n_name);
355     }
356 #endif
357 
358     /* stash away certain offsets for later use */
359     mpid_offset = nlst[X_MPID].n_value;
360     avenrun_offset = nlst[X_AVENRUN].n_value;
361     total_offset = nlst[X_TOTAL].n_value;
362     cp_time_offset = nlst[X_CP_TIME].n_value;
363 #ifdef MULTIPROCESSOR
364     xp_time_offset = nlst[X_XP_TIME].n_value;
365 #endif
366 
367     /* this is used in calculating WCPU -- calculate it ahead of time */
368     logcpu = log(loaddouble(ccpu));
369 
370     /* allocate space for proc structure array and array of pointers */
371     bytes = nproc * sizeof(struct proc);
372     pbase = (struct proc *)malloc(bytes);
373     pref  = (struct proc **)malloc(nproc * sizeof(struct proc *));
374 
375     /* Just in case ... */
376     if (pbase == (struct proc *)NULL || pref == (struct proc **)NULL)
377     {
378 	fprintf(stderr, "top: can't allocate sufficient memory\n");
379 	return(-1);
380     }
381 
382     /* allocate a table to hold all the page structs */
383     bytesize = epages - pages;
384     count = bytesize / sizeof(struct page);
385     physpage = (struct page *)malloc(epages - pages);
386     if (physpage == NULL)
387     {
388 	fprintf(stderr, "top: can't allocate sufficient memory\n");
389 	return(-1);
390     }
391 
392     /* get the page size with "getpagesize" and calculate pageshift from it */
393     pagesize = getpagesize();
394     pageshift = 0;
395     while (pagesize > 1)
396     {
397 	pageshift++;
398 	pagesize >>= 1;
399     }
400 
401     /* we only need the amount of log(2)1024 for our conversion */
402     pageshift -= LOG1024;
403 
404 #if defined(MULTIPROCESSOR) || defined(solbourne)
405     /* add a slash to the "run" state abbreviation */
406     if (ncpu > 1)
407     {
408 	state_abbrev[SRUN][3] = '/';
409     }
410 #endif
411 
412     /* fill in the statics information */
413     statics->procstate_names = procstatenames;
414     statics->cpustate_names = cpustatenames;
415     statics->memory_names = memorynames;
416     statics->order_names = ordernames;
417 
418     /* all done! */
419     return(0);
420 }
421 
422 char *format_header(uname_field)
423 
424 register char *uname_field;
425 
426 {
427     register char *ptr;
428 
429     ptr = header + UNAME_START;
430     while (*uname_field != '\0')
431     {
432 	*ptr++ = *uname_field++;
433     }
434 
435     return(header);
436 }
437 
438 void
439 get_system_info(si)
440 
441 struct system_info *si;
442 
443 {
444     load_avg avenrun[3];
445     long total;
446 #ifdef MULTIPROCESSOR
447     long half_total;
448 #endif
449 
450     /* get the cp_time array */
451     (void) getkval(cp_time_offset, (int *)cp_time, sizeof(cp_time),
452 		   "_cp_time");
453 
454 #ifdef MULTIPROCESSOR
455     /* get the xp_time array as well */
456     if (ncpu > 1)
457     {
458 	(void) getkval(xp_time_offset, (int *)xp_time, sizeof(xp_time),
459 		       "_xp_time");
460     }
461 #endif
462 
463     /* get load average array */
464     (void) getkval(avenrun_offset, (int *)avenrun, sizeof(avenrun),
465 		   "_avenrun");
466 
467     /* get mpid -- process id of last process */
468     (void) getkval(mpid_offset, &(si->last_pid), sizeof(si->last_pid),
469 		   "_mpid");
470 
471     /* get the array of physpage descriptors */
472     (void) getkval(pages, (int *)physpage, bytesize, "array _page");
473 
474     /* convert load averages to doubles */
475     {
476 	register int i;
477 	register double *infoloadp;
478 	register load_avg *sysloadp;
479 
480 	infoloadp = si->load_avg;
481 	sysloadp = avenrun;
482 	for (i = 0; i < 3; i++)
483 	{
484 	    *infoloadp++ = loaddouble(*sysloadp++);
485 	}
486     }
487 
488     /* convert cp_time counts to percentages */
489     total = percentages(CPUSTATES, cpu_states, cp_time, cp_old, cp_diff);
490 
491 #ifdef MULTIPROCESSOR
492     /* calculate spin time from all processors */
493     if (ncpu > 1)
494     {
495 	register int c;
496 	register int i;
497 	register long sum;
498 	register long change;
499 
500 	/* collect differences for each processor and add them */
501 	sum = 0;
502 	for (i = 0; i < ncpu; i++)
503 	{
504 	    c = xp_time[i][XP_SPIN];
505 	    change = c - xp_old[i];
506 	    if (change < 0)
507 	    {
508 		/* counter wrapped */
509 		change = (long)((unsigned long)c -
510 				(unsigned long)xp_old[i]);
511 	    }
512 	    sum += change;
513 	    xp_old[i] = c;
514 	}
515 
516 	/*
517 	 *  NOTE:  I am assuming that the ticks found in xp_time are
518 	 *  already included in the ticks accumulated in cp_time.  To
519 	 *  get an accurate reflection, therefore, we have to subtract
520 	 *  the spin time from the system time and recompute those two
521 	 *  percentages.
522 	 */
523 	half_total = total / 2l;
524 	cp_diff[CP_SYS] -= sum;
525 	cpu_states[CP_SYS] = (int)((cp_diff[CP_SYS] * 1000 + half_total) /
526 				   total);
527 	cpu_states[XCP_SPIN] = (int)((sum * 1000 + half_total) / total);
528     }
529 #endif
530 
531     /* sum memory statistics */
532     {
533 	register struct page *pp;
534 	register int cnt;
535 	register int inuse;
536 	register int free;
537 	register int locked;
538 
539 	/* bop thru the array counting page types */
540 	pp = physpage;
541 	inuse = free = locked = 0;
542 	for (cnt = count; --cnt >= 0; pp++)
543 	{
544 	    if (pp->p_free)
545 	    	free++;
546 	    else if (pp->p_lock || pp->p_keepcnt > 0)
547 	    	locked++;
548 	    else
549 	    	inuse++;
550 	}
551 
552 	/* convert memory stats to Kbytes */
553 	memory_stats[0] = pagetok(inuse + free);
554 	memory_stats[1] = pagetok(inuse);
555 	memory_stats[2] = pagetok(free);
556 	memory_stats[3] = pagetok(locked);
557     }
558 
559     /* set arrays and strings */
560     si->cpustates = cpu_states;
561     si->memory = memory_stats;
562 }
563 
564 static struct handle handle;
565 
566 caddr_t get_process_info(si, sel, compare_index)
567 
568 struct system_info *si;
569 struct process_select *sel;
570 int compare_index;
571 
572 {
573     register int i;
574     register int total_procs;
575     register int active_procs;
576     register struct proc **prefp;
577     register struct proc *pp;
578 
579     /* these are copied out of sel for speed */
580     int show_idle;
581     int show_system;
582     int show_uid;
583     int show_command;
584 
585     /* read all the proc structures in one fell swoop */
586     (void) getkval(proc, (int *)pbase, bytes, "proc array");
587 
588     /* get a pointer to the states summary array */
589     si->procstates = process_states;
590 
591     /* set up flags which define what we are going to select */
592     show_idle = sel->idle;
593     show_system = sel->system;
594     show_uid = sel->uid != -1;
595     show_command = sel->command != NULL;
596 
597     /* count up process states and get pointers to interesting procs */
598     total_procs = 0;
599     active_procs = 0;
600     bzero((char *)process_states, sizeof(process_states));
601     prefp = pref;
602     for (pp = pbase, i = 0; i < nproc; pp++, i++)
603     {
604 	/*
605 	 *  Place pointers to each valid proc structure in pref[].
606 	 *  Process slots that are actually in use have a non-zero
607 	 *  status field.  Processes with SSYS set are system
608 	 *  processes---these get ignored unless show_sysprocs is set.
609 	 */
610 	if (pp->p_stat != 0 &&
611 	    (show_system || ((pp->p_flag & SSYS) == 0)))
612 	{
613 	    total_procs++;
614 	    process_states[pp->p_stat]++;
615 	    if ((pp->p_stat != SZOMB) &&
616 		(show_idle || (pp->p_pctcpu != 0) || (pp->p_stat == SRUN)) &&
617 		(!show_uid || pp->p_uid == (uid_t)sel->uid))
618 	    {
619 		*prefp++ = pp;
620 		active_procs++;
621 	    }
622 	}
623     }
624 
625     /* if requested, sort the "interesting" processes */
626     qsort((char *)pref, active_procs, sizeof(struct proc *),
627 	  proc_compares[compare_index]);
628 
629     /* remember active and total counts */
630     si->p_total = total_procs;
631     si->p_active = pref_len = active_procs;
632 
633     /* pass back a handle */
634     handle.next_proc = pref;
635     handle.remaining = active_procs;
636     return((caddr_t)&handle);
637 }
638 
639 char fmt[MAX_COLS];		/* static area where result is built */
640 
641 char *format_next_process(handle, get_userid)
642 
643 caddr_t handle;
644 char *(*get_userid)();
645 
646 {
647     register struct proc *pp;
648     register long cputime;
649     register double pct;
650     struct user u;
651     struct handle *hp;
652 
653     /* find and remember the next proc structure */
654     hp = (struct handle *)handle;
655     pp = *(hp->next_proc++);
656     hp->remaining--;
657 
658     /* get the process's user struct and set cputime */
659     if (getu(pp, &u) == -1)
660     {
661 	(void) strcpy(u.u_comm, "<swapped>");
662 	cputime = 0;
663     }
664     else
665     {
666 	/* set u_comm for system processes */
667 	if (u.u_comm[0] == '\0')
668 	{
669 	    if (pp->p_pid == 0)
670 	    {
671 		(void) strcpy(u.u_comm, "Swapper");
672 	    }
673 	    else if (pp->p_pid == 2)
674 	    {
675 		(void) strcpy(u.u_comm, "Pager");
676 	    }
677 	}
678 
679 	cputime = u.u_ru.ru_utime.tv_sec + u.u_ru.ru_stime.tv_sec;
680     }
681 
682     /* calculate the base for cpu percentages */
683     pct = pctdouble(pp->p_pctcpu);
684 
685 #ifdef MULTIPROCESSOR
686     /*
687      *  If there is more than one cpu then add the processor number to
688      *  the "run/" string.  Note that this will only show up if the
689      *  process is in the run state.  Also note:  when they
690      *  start making Suns with more than 9 processors this will break
691      *  since the string will then be more than 5 characters.
692      */
693     if (ncpu > 1)
694     {
695 	state_abbrev[SRUN][4] = (pp->p_cpuid & 0xf) + '0';
696     }
697 #endif
698 #ifdef solbourne
699     if (ncpu > 1)
700       {
701 	state_abbrev[SRUN][4] = (pp->p_lastcpu) + '0';
702       }
703 #endif
704 
705     /* format this entry */
706     sprintf(fmt,
707 	    Proc_format,
708 	    pp->p_pid,
709 	    (*get_userid)(pp->p_uid),
710 	    pp->p_pri - PZERO,
711 	    pp->p_nice - NZERO,
712 	    format_k(pagetok(PROCSIZE(pp))),
713 	    format_k(pagetok(pp->p_rssize)),
714 	    state_abbrev[pp->p_stat],
715 	    format_time(cputime),
716 	    100.0 * weighted_cpu(pct, pp),
717 	    100.0 * pct,
718 	    printable(u.u_comm));
719 
720     /* return the result */
721     return(fmt);
722 }
723 
724 /*
725  *  getu(p, u) - get the user structure for the process whose proc structure
726  *	is pointed to by p.  The user structure is put in the buffer pointed
727  *	to by u.  Return 0 if successful, -1 on failure (such as the process
728  *	being swapped out).
729  */
730 
731 getu(p, u)
732 
733 register struct proc *p;
734 struct user *u;
735 
736 {
737     register struct user *lu;
738 
739     lu = kvm_getu(kd, p);
740     if (lu == NULL)
741     {
742 	return(-1);
743     }
744     else
745     {
746 	*u = *lu;
747 	return(0);
748     }
749 }
750 
751 /*
752  * check_nlist(nlst) - checks the nlist to see if any symbols were not
753  *		found.  For every symbol that was not found, a one-line
754  *		message is printed to stderr.  The routine returns the
755  *		number of symbols NOT found.
756  */
757 
758 int check_nlist(nlst)
759 
760 register struct nlist *nlst;
761 
762 {
763     register int i;
764 
765     /* check to see if we got ALL the symbols we requested */
766     /* this will write one line to stderr for every symbol not found */
767 
768     i = 0;
769     while (nlst->n_name != NULL)
770     {
771 #ifdef i386
772 	if (nlst->n_value == 0)
773 #else
774 	if (nlst->n_type == 0)
775 #endif
776 	{
777 	    /* this one wasn't found */
778 	    fprintf(stderr, "kernel: no symbol named `%s'\n", nlst->n_name);
779 	    i = 1;
780 	}
781 	nlst++;
782     }
783 
784     return(i);
785 }
786 
787 
788 /*
789  *  getkval(offset, ptr, size, refstr) - get a value out of the kernel.
790  *	"offset" is the byte offset into the kernel for the desired value,
791  *  	"ptr" points to a buffer into which the value is retrieved,
792  *  	"size" is the size of the buffer (and the object to retrieve),
793  *  	"refstr" is a reference string used when printing error meessages,
794  *	    if "refstr" starts with a '!', then a failure on read will not
795  *  	    be fatal (this may seem like a silly way to do things, but I
796  *  	    really didn't want the overhead of another argument).
797  *
798  */
799 
800 getkval(offset, ptr, size, refstr)
801 
802 unsigned long offset;
803 int *ptr;
804 int size;
805 char *refstr;
806 
807 {
808     if (kvm_read(kd, offset, ptr, size) != size)
809     {
810 	if (*refstr == '!')
811 	{
812 	    return(0);
813 	}
814 	else
815 	{
816 	    fprintf(stderr, "top: kvm_read for %s: %s\n",
817 		refstr, sys_errlist[errno]);
818 	    quit(23);
819 	    /*NOTREACHED*/
820 	}
821     }
822     return(1);
823 }
824 
825 /* comparison routines for qsort */
826 
827 /*
828  * There are currently four possible comparison routines.  main selects
829  * one of these by indexing in to the array proc_compares.
830  *
831  * Possible keys are defined as macros below.  Currently these keys are
832  * defined:  percent cpu, cpu ticks, process state, resident set size,
833  * total virtual memory usage.  The process states are ordered as follows
834  * (from least to most important):  WAIT, zombie, sleep, stop, start, run.
835  * The array declaration below maps a process state index into a number
836  * that reflects this ordering.
837  */
838 
839 /* First, the possible comparison keys.  These are defined in such a way
840    that they can be merely listed in the source code to define the actual
841    desired ordering.
842  */
843 
844 #define ORDERKEY_PCTCPU  if (lresult = p2->p_pctcpu - p1->p_pctcpu,\
845 			     (result = lresult > 0 ? 1 : lresult < 0 ? -1 : 0) == 0)
846 #define ORDERKEY_CPTICKS if ((result = p2->p_cpticks - p1->p_cpticks) == 0)
847 #define ORDERKEY_STATE   if ((result = sorted_state[p2->p_stat] - \
848 			      sorted_state[p1->p_stat])  == 0)
849 #define ORDERKEY_PRIO    if ((result = p2->p_pri - p1->p_pri) == 0)
850 #define ORDERKEY_RSSIZE  if ((result = p2->p_rssize - p1->p_rssize) == 0)
851 #define ORDERKEY_MEM     if ((result = PROCSIZE(p2) - PROCSIZE(p1)) == 0)
852 
853 /* Now the array that maps process state to a weight */
854 
855 static unsigned char sorted_state[] =
856 {
857     0,	/* not used		*/
858     3,	/* sleep		*/
859     1,	/* ABANDONED (WAIT)	*/
860     6,	/* run			*/
861     5,	/* start		*/
862     2,	/* zombie		*/
863     4	/* stop			*/
864 };
865 
866 /* compare_cpu - the comparison function for sorting by cpu percentage */
867 
868 compare_cpu(pp1, pp2)
869 
870 struct proc **pp1;
871 struct proc **pp2;
872 
873 {
874     register struct proc *p1;
875     register struct proc *p2;
876     register int result;
877     register pctcpu lresult;
878 
879     /* remove one level of indirection */
880     p1 = *pp1;
881     p2 = *pp2;
882 
883     ORDERKEY_PCTCPU
884     ORDERKEY_CPTICKS
885     ORDERKEY_STATE
886     ORDERKEY_PRIO
887     ORDERKEY_RSSIZE
888     ORDERKEY_MEM
889     ;
890 
891     return(result);
892 }
893 
894 /* compare_size - the comparison function for sorting by total memory usage */
895 
896 compare_size(pp1, pp2)
897 
898 struct proc **pp1;
899 struct proc **pp2;
900 
901 {
902     register struct proc *p1;
903     register struct proc *p2;
904     register int result;
905     register pctcpu lresult;
906 
907     /* remove one level of indirection */
908     p1 = *pp1;
909     p2 = *pp2;
910 
911     ORDERKEY_MEM
912     ORDERKEY_RSSIZE
913     ORDERKEY_PCTCPU
914     ORDERKEY_CPTICKS
915     ORDERKEY_STATE
916     ORDERKEY_PRIO
917     ;
918 
919     return(result);
920 }
921 
922 /* compare_res - the comparison function for sorting by resident set size */
923 
924 compare_res(pp1, pp2)
925 
926 struct proc **pp1;
927 struct proc **pp2;
928 
929 {
930     register struct proc *p1;
931     register struct proc *p2;
932     register int result;
933     register pctcpu lresult;
934 
935     /* remove one level of indirection */
936     p1 = *pp1;
937     p2 = *pp2;
938 
939     ORDERKEY_RSSIZE
940     ORDERKEY_MEM
941     ORDERKEY_PCTCPU
942     ORDERKEY_CPTICKS
943     ORDERKEY_STATE
944     ORDERKEY_PRIO
945     ;
946 
947     return(result);
948 }
949 
950 /*
951  * proc_owner(pid) - returns the uid that owns process "pid", or -1 if
952  *		the process does not exist.
953  *		It is EXTREMLY IMPORTANT that this function work correctly.
954  *		If top runs setuid root (as in SVR4), then this function
955  *		is the only thing that stands in the way of a serious
956  *		security problem.  It validates requests for the "kill"
957  *		and "renice" commands.
958  */
959 
960 int proc_owner(pid)
961 
962 int pid;
963 
964 {
965     register int cnt;
966     register struct proc **prefp;
967     register struct proc *pp;
968 
969     prefp = pref;
970     cnt = pref_len;
971     while (--cnt >= 0)
972     {
973 	if ((pp = *prefp++)->p_pid == (pid_t)pid)
974 	{
975 	    return((int)pp->p_uid);
976 	}
977     }
978     return(-1);
979 }
980