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