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
format_header(uname_field)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
get_system_info(si)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
get_process_info(si,sel,compare_index)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
format_next_process(handle,get_userid)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
getu(p,u)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
check_nlist(nlst)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
getkval(offset,ptr,size,refstr)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
proc_owner(pid)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