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 SGI machine running IRIX 6.2 and up 37 * 38 * DESCRIPTION: 39 * This is the machine-dependent module for IRIX as supplied by 40 * engineers at SGI. 41 * 42 * CFLAGS: -DHAVE_GETOPT -D_OLD_TERMIOS -DORDER 43 * 44 * AUTHOR: Sandeep Cariapa <cariapa@sgi.com> 45 * AUTHOR: Larry McVoy <lm@sgi.com> 46 * Sandeep did all the hard work; I ported to 6.2 and fixed up some formats. 47 * AUTHOR: John Schimmel <jes@sgi.com> 48 * He did the all irix merge. 49 * AUTHOR: Ariel Faigon <ariel@sgi.com> 50 * Ported to Ficus/Kudzu (IRIX 6.4+). 51 * Got rid of all nlist and different (elf64, elf32, COFF) kernel 52 * dependencies 53 * Various small fixes and enhancements: multiple CPUs, nicer formats. 54 * Added -DORDER process display ordering 55 * cleaned most -fullwarn'ings. 56 * Need -D_OLD_TERMIOS when compiling on IRIX 6.4 to work on 6.2 systems 57 * Support much bigger values in memory sizes (over Peta-byte) 58 * AUTHOR: William LeFebvre 59 * Converted to ANSI C and updated to new module interface 60 */ 61 62 #define _KMEMUSER 63 64 #include "config.h" 65 66 #include <sys/types.h> 67 #include <sys/time.h> 68 #include <sys/stat.h> 69 #include <sys/swap.h> 70 #include <sys/proc.h> 71 #include <sys/procfs.h> 72 #include <sys/sysinfo.h> 73 #include <sys/sysmp.h> 74 #include <sys/utsname.h> 75 #include <sys/schedctl.h> /* for < 6.4 NDPHIMAX et al. */ 76 #include <paths.h> 77 #include <assert.h> 78 #include <values.h> 79 #include <dirent.h> 80 #include <stdio.h> 81 #include <unistd.h> 82 #include <stdlib.h> 83 #include <errno.h> 84 #include <fcntl.h> 85 #include <dlfcn.h> 86 87 #include "top.h" 88 #include "machine.h" 89 #include "utils.h" 90 91 #define KMEM "/dev/kmem" 92 93 typedef double load_avg; 94 #define loaddouble(la) (la) 95 #define intload(i) ((double)(i)) 96 97 /* 98 * Structure for keeping track of CPU times from last time around 99 * the program. We keep these things in a hash table, which is 100 * recreated at every cycle. 101 */ 102 struct oldproc { 103 pid_t oldpid; 104 double oldtime; 105 double oldpct; 106 }; 107 static int oldprocs; /* size of table */ 108 static struct oldproc *oldbase; 109 #define HASH(x) ((x << 1) % oldprocs) 110 111 112 #define pagetok(pages) ((((uint64_t) pages) * pagesize) >> 10) 113 114 /* 115 * Ugly hack, save space and complexity of allocating and maintaining 116 * parallel arrays to the prpsinfo array: use spare space (pr_fill area) 117 * in prpsinfo structures to store %CPU calculated values 118 */ 119 #define D_align(addr) (((unsigned long)(addr) & ~0x0fU)) 120 #define percent_cpu(pp) (* (double *) D_align(&((pp)->pr_fill[0]))) 121 #define weighted_cpu(pp) (* (double *) D_align(&((pp)->pr_fill[4]))) 122 123 124 /* Username field to fill in starts at: */ 125 #define UNAME_START 16 126 127 /* 128 * These definitions control the format of the per-process area 129 */ 130 static char header[] = 131 " PID PGRP X PRI SIZE RES STATE TIME %WCPU %CPU COMMAND"; 132 /* 133 012345678901234567890123456789012345678901234567890123456789012345678901234567 134 10 20 30 40 50 60 70 135 */ 136 137 /* PID PGRP USER PRI SIZE RES STATE TIME %WCPU %CPU CMD */ 138 #define Proc_format \ 139 "%7d %7d %-8.8s %4.4s %6.6s %5.5s %-6.6s %6.6s %5.2f %5.2f %-.10s" 140 141 142 /* 143 * these are for detailing the cpu states 144 * Data is taken from the sysinfo structure (see <sys/sysinfo.h>) 145 * We rely on the following values: 146 * 147 * #define CPU_IDLE 0 148 * #define CPU_USER 1 149 * #define CPU_KERNEL 2 150 * #define CPU_WAIT 3 151 * #define CPU_SXBRK 4 152 * #define CPU_INTR 5 153 */ 154 #ifndef CPU_STATES /* defined only in 6.4 and up */ 155 # define CPU_STATES 6 156 #endif 157 158 int cpu_states[CPU_STATES]; 159 char *cpustatenames[] = { 160 "idle", "usr", "ker", "wait", "xbrk", "intr", 161 NULL 162 }; 163 164 /* these are for detailing the memory statistics */ 165 166 #define MEMSTATS 10 167 int memory_stats[MEMSTATS]; 168 char *memorynames[] = { 169 "K max, ", "K avail, ", "K free, ", "K swap, ", "K free swap", NULL 170 }; 171 172 char uname_str[40]; 173 double load[3]; 174 static char fmt[MAX_COLS + 2]; 175 int numcpus; 176 177 /* useful externals */ 178 extern int errno; 179 extern char *sys_errlist[]; 180 181 extern char *myname; 182 extern char *format_k(); 183 extern char *format_time(); 184 extern long percentages(); 185 186 static int kmem; 187 static unsigned long avenrun_offset; 188 189 static float irix_ver; /* for easy numeric comparison */ 190 191 static struct prpsinfo *pbase; 192 static struct prpsinfo **pref; 193 static struct oldproc *oldbase; 194 static int oldprocs; /* size of table */ 195 196 static DIR *procdir; 197 198 static int ptable_size; /* allocated process table size */ 199 static int nproc; /* estimated process table size */ 200 static int pagesize; 201 202 /* get_process_info passes back a handle. This is what it looks like: */ 203 struct handle { 204 struct prpsinfo **next_proc; /* points to next valid proc pointer */ 205 int remaining; /* number of pointers remaining */ 206 }; 207 208 static struct handle handle; 209 210 void getptable(struct prpsinfo *baseptr); 211 void size(int fd, struct prpsinfo *ps); 212 213 extern char *ordernames[]; 214 215 /* 216 * Process states letters are mapped into numbers 217 * 6.5 seems to have changed the semantics of prpsinfo.pr_state 218 * so we rely, (like ps does) on the char value pr_sname. 219 * The order we use here is what may be most interesting 220 * to top users: Most interesting state on top, least on bottom. 221 * 'S' (sleeping) is the most common case so I put it _after_ 222 * zombie, even though it is more "active" than zombie. 223 * 224 * State letters and their meanings: 225 * 226 * R Process is running (may not have a processor yet) 227 * I Process is in intermediate state of creation 228 * X Process is waiting for memory 229 * T Process is stopped 230 * Z Process is terminated and parent not waiting (zombie) 231 * S Process is sleeping, waiting for a resource 232 */ 233 234 /* abbreviated process states */ 235 static char *state_abbrev[] = 236 { "", "sleep", "zomb", "stop", "swap", "start", "ready", "run", NULL }; 237 238 /* Same but a little "wordier", used in CPU activity summary */ 239 int process_states[8]; /* per state counters */ 240 char *procstatenames[] = { 241 /* ready to run is considered running here */ 242 "", " sleeping, ", " zombie, ", " stopped, ", 243 " swapped, ", " starting, ", " ready, ", " running, ", 244 NULL 245 }; 246 247 #define S_RUNNING 7 248 #define S_READY 6 249 #define S_STARTING 5 250 #define S_SWAPPED 4 251 #define S_STOPPED 3 252 #define S_ZOMBIE 2 253 #define S_SLEEPING 1 254 255 #define IS_ACTIVE(pp) \ 256 (first_screen ? proc_state(pp) >= S_STARTING : percent_cpu(pp) > 0.0) 257 258 /* 259 * proc_state 260 * map the pr_sname value to an integer. 261 * used as an index into state_abbrev[] 262 * as well as an "order" key 263 */ 264 static int proc_state(struct prpsinfo *pp) 265 { 266 char psname = pp->pr_sname; 267 268 switch (psname) { 269 case 'R': return 270 (pp->pr_sonproc >= 0 && pp->pr_sonproc < numcpus) ? 271 S_RUNNING /* on a processor */ : S_READY; 272 case 'I': return S_STARTING; 273 case 'X': return S_SWAPPED; 274 case 'T': return S_STOPPED; 275 case 'Z': return S_ZOMBIE; 276 case 'S': return S_SLEEPING; 277 default : return 0; 278 } 279 } 280 281 282 /* 283 * To avoid nlist'ing the kernel (with all the different kernel type 284 * complexities), we estimate the size of the needed working process 285 * table by scanning /proc/pinfo and taking the number of entries 286 * multiplied by some reasonable factor. 287 * Assume current dir is _PATH_PROCFSPI 288 */ 289 static int active_proc_count() 290 { 291 DIR *dirp; 292 int pcnt; 293 294 if ((dirp = opendir(".")) == NULL) { 295 (void) fprintf(stderr, "%s: Unable to open %s\n", 296 myname, _PATH_PROCFSPI); 297 exit(1); 298 } 299 for (pcnt = 0; readdir(dirp) != NULL; pcnt++) 300 ; 301 closedir(dirp); 302 303 return pcnt; 304 } 305 306 /* 307 * allocate space for: 308 * proc structure array 309 * array of pointers to the above (used for sorting) 310 * array for storing per-process old CPU usage 311 */ 312 void 313 allocate_proc_tables() 314 { 315 int n_active = active_proc_count(); 316 317 if (pbase != NULL) /* && n_active < ptable_size */ 318 return; 319 320 /* Need to realloc if we exceed, but factor should be enough */ 321 nproc = n_active * 5; 322 oldprocs = 2 * nproc; 323 324 pbase = (struct prpsinfo *) 325 malloc(nproc * sizeof(struct prpsinfo)); 326 pref = (struct prpsinfo **) 327 malloc(nproc * sizeof(struct prpsinfo *)); 328 oldbase = (struct oldproc *) 329 malloc (oldprocs * sizeof(struct oldproc)); 330 331 ptable_size = nproc; 332 333 if (pbase == NULL || pref == NULL || oldbase == NULL) { 334 (void) fprintf(stderr, "%s: malloc: out of memory\n", myname); 335 exit (1); 336 } 337 } 338 339 int 340 machine_init(struct statics *statics) 341 { 342 struct oldproc *op, *endbase; 343 int pcnt = 0; 344 struct utsname utsname; 345 char tmpbuf[20]; 346 347 uname(&utsname); 348 irix_ver = (float) atof((const char *)utsname.release); 349 strncpy(tmpbuf, utsname.release, 9); 350 tmpbuf[9] = '\0'; 351 sprintf(uname_str, "%s %-.14s %s %s", 352 utsname.sysname, utsname.nodename, 353 tmpbuf, utsname.machine); 354 355 pagesize = getpagesize(); 356 357 if ((kmem = open(KMEM, O_RDONLY)) == -1) { 358 perror(KMEM); 359 return -1; 360 } 361 362 if (chdir(_PATH_PROCFSPI)) { 363 /* handy for later on when we're reading it */ 364 (void) fprintf(stderr, "%s: Unable to chdir to %s\n", 365 myname, _PATH_PROCFSPI); 366 return -1; 367 } 368 if ((procdir = opendir(".")) == NULL) { 369 (void) fprintf(stderr, "%s: Unable to open %s\n", 370 myname, _PATH_PROCFSPI); 371 return -1; 372 } 373 374 if ((avenrun_offset = sysmp(MP_KERNADDR, MPKA_AVENRUN)) == -1) { 375 perror("sysmp(MP_KERNADDR, MPKA_AVENRUN)"); 376 return -1; 377 } 378 379 allocate_proc_tables(); 380 381 oldprocs = 2 * nproc; 382 endbase = oldbase + oldprocs; 383 for (op = oldbase; op < endbase; op++) { 384 op->oldpid = -1; 385 } 386 387 statics->cpustate_names = cpustatenames; 388 statics->memory_names = memorynames; 389 statics->order_names = ordernames; 390 statics->procstate_names = procstatenames; 391 392 return (0); 393 } 394 395 char * 396 format_header(register char *uname_field) 397 398 { 399 register char *ptr; 400 401 ptr = header + UNAME_START; 402 while (*uname_field != '\0') { 403 *ptr++ = *uname_field++; 404 } 405 406 return (header); 407 } 408 409 void 410 get_system_info(struct system_info *si) 411 412 { 413 int i; 414 int avenrun[3]; 415 struct rminfo realmem; 416 struct sysinfo sysinfo; 417 static time_t cp_old [CPU_STATES]; 418 static time_t cp_diff[CPU_STATES]; /* for cpu state percentages */ 419 off_t fswap; /* current free swap in blocks */ 420 off_t tswap; /* total swap in blocks */ 421 422 (void) getkval(avenrun_offset, (int *) avenrun, sizeof(avenrun), "avenrun"); 423 424 for (i = 0; i < 3; i++) { 425 si->load_avg[i] = loaddouble(avenrun[i]); 426 si->load_avg[i] /= 1024.0; 427 } 428 429 if ((numcpus = sysmp(MP_NPROCS)) == -1) { 430 perror("sysmp(MP_NPROCS)"); 431 return; 432 } 433 434 if (sysmp(MP_SAGET, MPSA_RMINFO, &realmem, sizeof(realmem)) == -1) { 435 perror("sysmp(MP_SAGET,MPSA_RMINFO, ...)"); 436 return; 437 } 438 439 swapctl(SC_GETFREESWAP, &fswap); 440 swapctl(SC_GETSWAPTOT, &tswap); 441 442 memory_stats[0] = pagetok(realmem.physmem); 443 memory_stats[1] = pagetok(realmem.availrmem); 444 memory_stats[2] = pagetok(realmem.freemem); 445 memory_stats[3] = tswap / 2; 446 memory_stats[4] = fswap / 2; 447 448 if (sysmp(MP_SAGET,MPSA_SINFO, &sysinfo,sizeof(struct sysinfo)) == -1) { 449 perror("sysmp(MP_SAGET,MPSA_SINFO)"); 450 return; 451 } 452 (void) percentages(CPU_STATES, cpu_states, sysinfo.cpu, cp_old, cp_diff); 453 454 si->cpustates = cpu_states; 455 si->memory = memory_stats; 456 si->last_pid = -1; 457 458 return; 459 } 460 461 caddr_t 462 get_process_info(struct system_info *si, struct process_select *sel, int compare_index) 463 464 { 465 int i, total_procs, active_procs; 466 struct prpsinfo **prefp; 467 struct prpsinfo *pp; 468 int show_uid; 469 static char first_screen = 1; 470 471 /* read all the proc structures */ 472 getptable(pbase); 473 474 /* get a pointer to the states summary array */ 475 si->procstates = process_states; 476 477 /* set up flags which define what we are going to select */ 478 show_uid = sel->uid != -1; 479 480 /* count up process states and get pointers to interesting procs */ 481 total_procs = 0; 482 active_procs = 0; 483 (void) memset(process_states, 0, sizeof(process_states)); 484 prefp = pref; 485 486 for (pp = pbase, i = 0; i < nproc; pp++, i++) { 487 /* 488 * Place pointers to each valid proc structure in pref[]. 489 * Process slots that are actually in use have a non-zero 490 * status field. Processes with SSYS set are system 491 * processes---these get ignored unless show_system is set. 492 * Ariel: IRIX 6.4 had to redefine "system processes" 493 * They do not exist outside the kernel in new kernels. 494 * Now defining as uid==0 and ppid==1 (init children) 495 */ 496 if (pp->pr_state && 497 (sel->system || !(pp->pr_uid==0 && pp->pr_ppid==1))) { 498 total_procs++; 499 process_states[proc_state(pp)]++; 500 /* 501 * zombies are actually interesting (to avoid) 502 * although they are not active, so I leave them 503 * displayed. 504 */ 505 if (/* (! pp->pr_zomb) && */ 506 (sel->idle || IS_ACTIVE(pp)) && 507 (! show_uid || pp->pr_uid == (uid_t) sel->uid)) { 508 *prefp++ = pp; 509 active_procs++; 510 } 511 } 512 } 513 first_screen = 0; 514 515 /* if requested, sort the "interesting" processes */ 516 qsort((char *) pref, active_procs, sizeof(struct prpsinfo *), 517 proc_compares[compare_index]); 518 519 /* remember active and total counts */ 520 si->p_total = total_procs; 521 si->p_active = active_procs; 522 523 /* pass back a handle */ 524 handle.next_proc = pref; 525 handle.remaining = active_procs; 526 return ((caddr_t) &handle); 527 } 528 529 /* 530 * Added cpu_id to running processes, add 'ready' (to run) state 531 */ 532 static char * 533 format_state(struct prpsinfo *pp) 534 535 { 536 static char state_str[16]; 537 int state = proc_state(pp); 538 539 if (state == S_RUNNING) { 540 /* 541 * Alert: 6.2 (MP only?) binary incompatibility 542 * pp->pr_sonproc apparently (?) has a different 543 * offset on 6.2 machines... I've seen cases where 544 * a 6.4 compiled top running on 6.2 printed 545 * a garbage CPU-id. To be safe, I print the CPU-id 546 * only if it falls within range [0..numcpus-1] 547 */ 548 sprintf(state_str, "run/%d", pp->pr_sonproc); 549 return state_str; 550 } 551 552 /* default */ 553 return state_abbrev[state]; 554 } 555 556 static char * 557 format_prio(struct prpsinfo *pp) 558 559 { 560 static char prio_str[10]; 561 562 if (irix_ver < 6.4) { 563 /* 564 * Note: this is _compiled_ on 6.x where x >= 4 but I would like 565 * it to run on 6.2 6.3 as well (backward binary compatibility). 566 * Scheduling is completely different between these IRIX versions 567 * and some scheduling classes may even have different names. 568 * 569 * The solution: have more than one style of 'priority' depending 570 * on the OS version. 571 * 572 * See npri(1) + nice(2) + realtime(5) for scheduling classes, 573 * and priority values. 574 */ 575 if (pp->pr_pri <= NDPHIMIN) /* real time? */ 576 sprintf(prio_str, "+%d", pp->pr_pri); 577 else if (pp->pr_pri <= NDPNORMMIN) /* normal interactive */ 578 sprintf(prio_str, "%d", pp->pr_pri); 579 else /* batch: low prio */ 580 sprintf(prio_str, "b%d", pp->pr_pri); 581 582 } else { 583 584 /* copied from Kostadis's code */ 585 586 if (strcmp(pp->pr_clname, "RT") == 0) /* real time */ 587 sprintf(prio_str, "+%d", pp->pr_pri); 588 else if (strcmp(pp->pr_clname, "DL") == 0) /* unsupported ? */ 589 sprintf(prio_str, "d%d", pp->pr_pri); 590 else if (strcmp(pp->pr_clname, "GN") == 0) 591 sprintf(prio_str, "g%d", pp->pr_pri); 592 else if (strcmp(pp->pr_clname, "GB") == 0) 593 sprintf(prio_str, "p%d", pp->pr_pri); 594 595 else if (strcmp(pp->pr_clname, "WL") == 0) /* weightless */ 596 return "w"; 597 else if (strcmp(pp->pr_clname, "BC") == 0) 598 return "bc"; /* batch critical */ 599 else if (strcmp(pp->pr_clname, "B") == 0) 600 return "b"; /* batch */ 601 else 602 sprintf(prio_str, "%d", pp->pr_pri); 603 } 604 return prio_str; 605 } 606 607 static double 608 clip_percent(double pct) 609 610 { 611 if (pct < 0) { 612 return 0.0; 613 } else if (pct >= 100) { 614 return 99.99; 615 } 616 return pct; 617 } 618 619 char * 620 format_next_process(caddr_t handle, char *(*get_userid)()) 621 622 { 623 struct prpsinfo *pp; 624 struct handle *hp; 625 long cputime; 626 627 /* find and remember the next proc structure */ 628 hp = (struct handle *) handle; 629 pp = *(hp->next_proc++); 630 hp->remaining--; 631 632 /* get the process cpu usage since startup */ 633 cputime = pp->pr_time.tv_sec; 634 635 /* format this entry */ 636 sprintf(fmt, 637 Proc_format, 638 pp->pr_pid, 639 pp->pr_pgrp, 640 (*get_userid) (pp->pr_uid), 641 format_prio(pp), 642 format_k(pagetok(pp->pr_size)), 643 format_k(pagetok(pp->pr_rssize)), 644 format_state(pp), 645 format_time(cputime), 646 clip_percent(weighted_cpu(pp)), 647 clip_percent(percent_cpu(pp)), 648 printable(pp->pr_fname)); 649 650 /* return the result */ 651 return (fmt); 652 } 653 654 /* 655 * getkval(offset, ptr, size, refstr) - get a value out of the kernel. 656 * "offset" is the byte offset into the kernel for the desired value, 657 * "ptr" points to a buffer into which the value is retrieved, 658 * "size" is the size of the buffer (and the object to retrieve), 659 * "refstr" is a reference string used when printing error meessages, 660 * if "refstr" starts with a '!', then a failure on read will not 661 * be fatal (this may seem like a silly way to do things, but I 662 * really didn't want the overhead of another argument). 663 * 664 */ 665 666 int 667 getkval(unsigned long offset, int *ptr, int size, char *refstr) 668 669 { 670 if (lseek(kmem, (long) offset, SEEK_SET) == -1) { 671 if (*refstr == '!') 672 refstr++; 673 (void) fprintf(stderr, "%s: %s: lseek to %s: %s\n", 674 myname, KMEM, refstr, strerror(errno)); 675 exit(0); 676 } 677 if (read(kmem, (char *) ptr, size) == -1) { 678 if (*refstr == '!') 679 return (0); 680 else { 681 (void) fprintf(stderr, "%s: %s: reading %s: %s\n", 682 myname, KMEM, refstr, strerror(errno)); 683 exit(0); 684 } 685 } 686 return (1); 687 } 688 689 /* 690 * compare_K - comparison functions for "qsort" 691 * Compares the resource consumption of two processes using five 692 * distinct keys. The keys are: 693 * percent cpu, cpu ticks, state, resident set size, total virtual 694 * memory usage. The process states are ordered as follows (from least 695 * to most important): WAIT, zombie, sleep, stop, idle, run. 696 * Different comparison functions are used for different orderings. 697 */ 698 699 /* these are names given to allowed sorting orders -- first is default */ 700 char *ordernames[] = { 701 /* 702 * Aliases for user convenience/friendliness: 703 * mem == size 704 * rss == res 705 */ 706 "cpu", "size", "mem", "res", "rss", 707 "time", "state", "command", "prio", NULL 708 }; 709 710 /* forward definitions for comparison functions */ 711 int compare_cpu(struct prpsinfo **pp1, struct prpsinfo **pp2); 712 int compare_size(struct prpsinfo **pp1, struct prpsinfo **pp2); 713 int compare_res(struct prpsinfo **pp1, struct prpsinfo **pp2); 714 int compare_time(struct prpsinfo **pp1, struct prpsinfo **pp2); 715 int compare_state(struct prpsinfo **pp1, struct prpsinfo **pp2); 716 int compare_cmd(struct prpsinfo **pp1, struct prpsinfo **pp2); 717 int compare_prio(struct prpsinfo **pp1, struct prpsinfo **pp2); 718 719 int (*proc_compares[])() = { 720 compare_cpu, 721 compare_size, 722 compare_size, 723 compare_res, 724 compare_res, 725 compare_time, 726 compare_state, 727 compare_cmd, 728 compare_prio, 729 NULL 730 }; 731 732 733 /* 734 * The possible comparison expressions. These are defined in such a way 735 * that they can be merely listed in the source code to define the actual 736 * desired ordering. 737 */ 738 739 #define ORDERKEY_PCTCPU \ 740 if (dresult = percent_cpu(p2) - percent_cpu(p1),\ 741 (result = dresult > 0.0 ? 1 : dresult < 0.0 ? -1 : 0) == 0) 742 #define ORDERKEY_CPTICKS \ 743 if ((result = p2->pr_time.tv_sec - p1->pr_time.tv_sec) == 0) 744 #define ORDERKEY_STATE if ((result = proc_state(p2) - proc_state(p1)) == 0) 745 #define ORDERKEY_PRIO if ((result = p2->pr_oldpri - p1->pr_oldpri) == 0) 746 #define ORDERKEY_RSSIZE if ((result = p2->pr_rssize - p1->pr_rssize) == 0) 747 #define ORDERKEY_MEM if ((result = (p2->pr_size - p1->pr_size)) == 0) 748 #define ORDERKEY_CMD if ((result = strcmp(p1->pr_fname,p2->pr_fname)) == 0) 749 750 int compare_cpu(struct prpsinfo **pp1, struct prpsinfo **pp2) 751 { 752 struct prpsinfo *p1, *p2; 753 int result; 754 double dresult; 755 756 /* remove one level of indirection */ 757 p1 = *pp1; 758 p2 = *pp2; 759 /* 760 * order by various keys, resorting to the next one 761 * whenever there's a tie in comparisons 762 */ 763 ORDERKEY_PCTCPU 764 ORDERKEY_CPTICKS 765 ORDERKEY_STATE 766 ORDERKEY_PRIO 767 ORDERKEY_RSSIZE 768 ORDERKEY_MEM 769 ; 770 return (result); 771 } 772 773 int compare_size(struct prpsinfo **pp1, struct prpsinfo **pp2) 774 { 775 struct prpsinfo *p1, *p2; 776 int result; 777 double dresult; 778 779 /* remove one level of indirection */ 780 p1 = *pp1; 781 p2 = *pp2; 782 /* 783 * order by various keys, resorting to the next one 784 * whenever there's a tie in comparisons 785 */ 786 ORDERKEY_MEM 787 ORDERKEY_RSSIZE 788 ORDERKEY_PCTCPU 789 ORDERKEY_CPTICKS 790 ORDERKEY_STATE 791 ORDERKEY_PRIO 792 ; 793 return (result); 794 } 795 796 int compare_res(struct prpsinfo **pp1, struct prpsinfo **pp2) 797 { 798 struct prpsinfo *p1, *p2; 799 int result; 800 double dresult; 801 802 /* remove one level of indirection */ 803 p1 = *pp1; 804 p2 = *pp2; 805 /* 806 * order by various keys, resorting to the next one 807 * whenever there's a tie in comparisons 808 */ 809 ORDERKEY_RSSIZE 810 ORDERKEY_MEM 811 ORDERKEY_PCTCPU 812 ORDERKEY_CPTICKS 813 ORDERKEY_STATE 814 ORDERKEY_PRIO 815 ; 816 return (result); 817 } 818 819 int compare_time(struct prpsinfo **pp1, struct prpsinfo **pp2) 820 { 821 struct prpsinfo *p1, *p2; 822 int result; 823 double dresult; 824 825 /* remove one level of indirection */ 826 p1 = *pp1; 827 p2 = *pp2; 828 /* 829 * order by various keys, resorting to the next one 830 * whenever there's a tie in comparisons 831 */ 832 ORDERKEY_CPTICKS 833 ORDERKEY_RSSIZE 834 ORDERKEY_MEM 835 ORDERKEY_PCTCPU 836 ORDERKEY_STATE 837 ORDERKEY_PRIO 838 ; 839 return (result); 840 } 841 842 int compare_cmd(struct prpsinfo **pp1, struct prpsinfo **pp2) 843 { 844 struct prpsinfo *p1, *p2; 845 int result; 846 double dresult; 847 848 /* remove one level of indirection */ 849 p1 = *pp1; 850 p2 = *pp2; 851 /* 852 * order by various keys, resorting to the next one 853 * whenever there's a tie in comparisons 854 */ 855 ORDERKEY_CMD 856 ORDERKEY_PCTCPU 857 ORDERKEY_CPTICKS 858 ORDERKEY_RSSIZE 859 ; 860 return (result); 861 } 862 863 int compare_state(struct prpsinfo **pp1, struct prpsinfo **pp2) 864 { 865 struct prpsinfo *p1, *p2; 866 int result; 867 double dresult; 868 869 /* remove one level of indirection */ 870 p1 = *pp1; 871 p2 = *pp2; 872 /* 873 * order by various keys, resorting to the next one 874 * whenever there's a tie in comparisons 875 */ 876 ORDERKEY_STATE 877 ORDERKEY_PCTCPU 878 ORDERKEY_CPTICKS 879 ORDERKEY_RSSIZE 880 ; 881 return (result); 882 } 883 884 int compare_prio(struct prpsinfo **pp1, struct prpsinfo **pp2) 885 { 886 struct prpsinfo *p1, *p2; 887 int result; 888 double dresult; 889 890 /* remove one level of indirection */ 891 p1 = *pp1; 892 p2 = *pp2; 893 /* 894 * order by various keys, resorting to the next one 895 * whenever there's a tie in comparisons 896 */ 897 ORDERKEY_PRIO 898 ORDERKEY_PCTCPU 899 ; 900 return (result); 901 } 902 903 904 905 /* return the owner of the specified process. */ 906 uid_t 907 proc_owner(pid_t pid) 908 909 { 910 register struct prpsinfo *p; 911 int i; 912 913 for (i = 0, p = pbase; i < nproc; i++, p++) 914 if (p->pr_pid == pid) 915 return (p->pr_uid); 916 917 return (-1); 918 } 919 920 #ifdef DO_MAPSIZE 921 static void 922 size(int fd, struct prpsinfo *ps) 923 924 { 925 prmap_sgi_arg_t maparg; 926 struct prmap_sgi maps[256]; 927 int nmaps; 928 double sz; 929 int i; 930 931 maparg.pr_vaddr = (caddr_t) maps; 932 maparg.pr_size = sizeof maps; 933 if ((nmaps = ioctl(fd, PIOCMAP_SGI, &maparg)) == -1) { 934 /* XXX - this will be confusing */ 935 return; 936 } 937 for (i = 0, sz = 0; i < nmaps; ++i) { 938 sz += (double) maps[i].pr_wsize / MA_WSIZE_FRAC; 939 } 940 ps->pr_rssize = (long) sz; 941 } 942 #endif 943 944 /* get process table */ 945 void 946 getptable(struct prpsinfo *baseptr) 947 948 { 949 struct prpsinfo *currproc; /* ptr to current proc struct */ 950 int i, numprocs; 951 struct dirent *direntp; 952 struct oldproc *op, *endbase; 953 static struct timeval lasttime, thistime; 954 static double timediff, alpha, beta; 955 956 /* measure time between last call to getptable and current call */ 957 gettimeofday (&thistime, NULL); 958 959 /* 960 * To avoid divides, we keep times in nanoseconds. This is 961 * scaled by 1e7 rather than 1e9 so that when we divide we 962 * get percent. 963 */ 964 timediff = ((double) thistime.tv_sec * 1.0e7 - 965 (double) lasttime.tv_sec * 1.0e7) 966 + 967 ((double) thistime.tv_usec * 10 - 968 (double) lasttime.tv_usec * 10); 969 970 /* 971 * Under extreme load conditions, sca has experienced 972 * an assert(timediff > 0) failure here. His guess is that 973 * sometimes timed resets the time backwards and gettimeofday 974 * returns a lower number on a later call. 975 * To be on the safe side I fix it here by setting timediff 976 * to some arbitrary small value (in nanoseconds). 977 */ 978 if (timediff <= 0.0) timediff = 100.0; 979 980 lasttime = thistime; /* prepare for next round */ 981 982 /* 983 * constants for exponential decaying average. 984 * avg = alpha * new + beta * avg 985 * The goal is 50% decay in 30 sec. However if the sample period 986 * is greater than 30 sec, there's not a lot we can do. 987 */ 988 if (timediff < 30.0e7) { 989 alpha = 0.5 * (timediff / 15.0e7); 990 beta = 1.0 - alpha; 991 } else { 992 alpha = 0.5; 993 beta = 0.5; 994 } 995 assert(alpha >= 0); assert(alpha <= 1); 996 assert(beta >= 0); assert(beta <= 1); 997 998 endbase = oldbase + oldprocs; 999 currproc = baseptr; 1000 1001 for (numprocs = 0, rewinddir(procdir); direntp = readdir(procdir);) { 1002 int fd; 1003 1004 if ((fd = open(direntp->d_name, O_RDONLY)) < 0) 1005 continue; 1006 1007 currproc = baseptr + numprocs; 1008 1009 if (ioctl(fd, PIOCPSINFO, currproc) < 0) { 1010 (void) close(fd); 1011 continue; 1012 } 1013 1014 /* 1015 * SVR4 doesn't keep track of CPU% in the kernel, 1016 * so we have to do our own. 1017 * See if we've heard of this process before. 1018 * If so, compute % based on CPU since last time. 1019 */ 1020 op = oldbase + HASH (currproc->pr_pid); 1021 for (;;) { 1022 if (op->oldpid == -1) /* not there */ 1023 break; 1024 if (op->oldpid == currproc->pr_pid) { 1025 /* found old data */ 1026 percent_cpu(currproc) = 1027 ((currproc->pr_time.tv_sec * 1.0e9 + 1028 currproc->pr_time.tv_nsec) 1029 - op->oldtime) / timediff; 1030 1031 weighted_cpu(currproc) = 1032 op->oldpct * beta + 1033 percent_cpu(currproc) * alpha; 1034 1035 break; 1036 } 1037 op++; /* try next entry in hash table */ 1038 if (op == endbase) /* table wrap around */ 1039 op = oldbase; 1040 } 1041 1042 /* Otherwise, it's new, so use all of its CPU time */ 1043 if (op->oldpid == -1) { 1044 if (lasttime.tv_sec) { 1045 percent_cpu(currproc) = 1046 (currproc->pr_time.tv_sec * 1.0e9 + 1047 currproc->pr_time.tv_nsec) / timediff; 1048 1049 weighted_cpu(currproc) = percent_cpu(currproc); 1050 } else { 1051 /* first screen -- no difference is possible */ 1052 percent_cpu(currproc) = 0.0; 1053 weighted_cpu(currproc) = 0.0; 1054 } 1055 } 1056 1057 #ifdef DO_MAPSIZE 1058 size(fd, currproc); 1059 #endif 1060 numprocs++; 1061 (void) close(fd); 1062 1063 /* 1064 * Bug: in case process count grew so dramatically 1065 * as to exceed to table size. We give up on a full scan. 1066 * the chances of this to happen are extremely slim due to 1067 * the big factor we're using. getting nproc from nlist 1068 * is not worth the headache. realloc wouldn't work either 1069 * because we have pointers to the proc table so we cannot 1070 * move it around. 1071 */ 1072 if (numprocs >= ptable_size) { 1073 fprintf(stderr, 1074 "preallocated proc table size (%d) exceeded, " 1075 "skipping some processes\n", ptable_size); 1076 break; 1077 } 1078 } 1079 nproc = numprocs; 1080 1081 /* 1082 * Save current CPU time for next time around 1083 * For the moment recreate the hash table each time, as the code 1084 * is easier that way. 1085 */ 1086 oldprocs = 2 * nproc; 1087 endbase = oldbase + oldprocs; 1088 1089 for (op = oldbase; op < endbase; op++) 1090 op->oldpid = -1; 1091 1092 for (i = 0, currproc = baseptr; i < nproc; i++, currproc++) { 1093 1094 /* find an empty spot */ 1095 op = oldbase + HASH (currproc->pr_pid); 1096 for (;;) { 1097 if (op->oldpid == -1) 1098 break; 1099 op++; 1100 if (op == endbase) 1101 op = oldbase; 1102 } 1103 op->oldpid = currproc->pr_pid; 1104 op->oldtime = (currproc->pr_time.tv_sec * 1.0e9 + 1105 currproc->pr_time.tv_nsec); 1106 op->oldpct = weighted_cpu(currproc); 1107 } 1108 } 1109 1110