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 96typedef 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 108struct 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 132static 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 167static 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 180char *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 187static double logcpu; 188kvm_t *kd; 189 190/* these are retrieved from the kernel in _init */ 191 192static unsigned long proc; 193static int nproc; 194static load_avg ccpu; 195static unsigned long pages; 196static unsigned long epages; 197static int ncpu = 0; 198 199/* these are offsets obtained via nlist and used in the get_ functions */ 200 201static unsigned long mpid_offset; 202static unsigned long avenrun_offset; 203static unsigned long total_offset; 204static unsigned long cp_time_offset; 205#ifdef MULTIPROCESSOR 206static unsigned long xp_time_offset; 207#endif 208 209/* these are for calculating cpu state percentages */ 210 211static long cp_time[CPUSTATES]; 212static long cp_old[CPUSTATES]; 213static long cp_diff[CPUSTATES]; 214#ifdef MULTIPROCESSOR 215static 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. */ 218static long xp_old[NCPU]; 219#endif 220 221/* these are for detailing the process states */ 222 223int process_states[7]; 224char *procstatenames[] = { 225 "", " sleeping, ", " ABANDONED, ", " running, ", " starting, ", 226 " zombie, ", " stopped, ", 227 NULL 228}; 229 230/* these are for detailing the cpu states */ 231 232int cpu_states[5]; 233char *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 244long memory_stats[4]; 245char *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 */ 250char *ordernames[] = 251{"cpu", "size", "res", NULL}; 252 253/* forward definitions for comparison functions */ 254int compare_cpu(); 255int compare_size(); 256int compare_res(); 257 258int (*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 267static int bytes; 268static int pref_len; 269static struct proc *pbase; 270static struct proc **pref; 271 272/* these are for getting the memory statistics */ 273 274static struct page *physpage; 275static int bytesize; 276static int count; 277static 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 */ 284extern int errno; 285extern char *sys_errlist[]; 286 287long lseek(); 288long time(); 289 290machine_init(statics) 291 292struct 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 422char *format_header(uname_field) 423 424register 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 438void 439get_system_info(si) 440 441struct 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 564static struct handle handle; 565 566caddr_t get_process_info(si, sel, compare_index) 567 568struct system_info *si; 569struct process_select *sel; 570int 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 639char fmt[MAX_COLS]; /* static area where result is built */ 640 641char *format_next_process(handle, get_userid) 642 643caddr_t handle; 644char *(*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 731getu(p, u) 732 733register struct proc *p; 734struct 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 758int check_nlist(nlst) 759 760register 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 800getkval(offset, ptr, size, refstr) 801 802unsigned long offset; 803int *ptr; 804int size; 805char *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 855static 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 868compare_cpu(pp1, pp2) 869 870struct proc **pp1; 871struct 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 896compare_size(pp1, pp2) 897 898struct proc **pp1; 899struct 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 924compare_res(pp1, pp2) 925 926struct proc **pp1; 927struct 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 960int proc_owner(pid) 961 962int 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