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: For Intel based System V Release 5 (Unixware7) 37 * 38 * DESCRIPTION: 39 * System V release 5 for i[3456]86 40 * Works for: 41 * i586-sco-sysv5uw7 i386 SCO UNIX_SVR5 (UnixWare 7) 42 * 43 * LIBS: -lelf -lmas 44 * 45 * CFLAGS: -DHAVE_GETOPT -DORDER 46 * 47 * AUTHORS: Mike Hopkirk <hops@sco.com> 48 * David Cutter <dpc@grail.com> 49 * Andrew Herbert <andrew@werple.apana.org.au> 50 * Robert Boucher <boucher@sofkin.ca> 51 */ 52 53 /* build config 54 * SHOW_NICE - process nice fields don't seem to be being updated so changed 55 * default to display # of threads in use instead. 56 * define this to display nice fields (values always 0) 57 * #define SHOW_NICE 1 58 */ 59 60 #define _KMEMUSER 61 #define prpsinfo psinfo 62 #include <sys/procfs.h> 63 64 #define pr_state pr_lwp.pr_state 65 #define pr_nice pr_lwp.pr_nice 66 #define pr_pri pr_lwp.pr_pri 67 #define pr_onpro pr_lwp.pr_onpro 68 #define ZOMBIE(p) ((p)->pr_nlwp == 0) 69 #define SIZE_K(p) pagetok((p)->pr_size) 70 #define RSS_K(p) pagetok((p)->pr_rssize) 71 72 73 #include <stdio.h> 74 #include <fcntl.h> 75 #include <unistd.h> 76 #include <stdlib.h> 77 #include <errno.h> 78 #include <dirent.h> 79 #include <nlist.h> 80 #include <string.h> 81 #include <sys/types.h> 82 #include <sys/param.h> 83 #include <sys/proc.h> 84 #include <sys/sysmacros.h> 85 #include <vm/anon.h> 86 #include <sys/priocntl.h> 87 #include <sys/tspriocntl.h> 88 #include <sys/var.h> 89 90 #include "top.h" 91 #include "machine.h" 92 #include "utils.h" 93 94 #define UNIX "/stand/unix" 95 #define KMEM "/dev/kmem" 96 #define PROCFS "/proc" 97 #define CPUSTATES 5 98 99 #ifndef PRIO_MAX 100 #define PRIO_MAX 20 101 #endif 102 #ifndef PRIO_MIN 103 #define PRIO_MIN -20 104 #endif 105 106 #ifndef FSCALE 107 #define FSHIFT 8 /* bits to right of fixed binary point */ 108 #define FSCALE (1<<FSHIFT) 109 #endif 110 111 #define loaddouble(x) ((double)x/FSCALE) 112 #define pagetok(size) ((size) * pagesz) >> LOG1024 113 114 /* definitions for the index in the nlist array */ 115 #define X_AVENRUN 0 116 #define X_V 1 117 #define X_MPID 2 118 119 static struct nlist nlst[] = 120 { 121 {"avenrun"}, /* 0 */ 122 {"v"}, /* 1 */ 123 {"nextpid"}, /* 2 */ 124 {NULL} 125 }; 126 127 static unsigned long avenrun_offset; 128 static unsigned long mpid_offset; 129 130 static unsigned int pagesz; 131 132 static void reallocproc(int n); 133 static int maxprocs; 134 135 /* get_process_info passes back a handle. This is what it looks like: */ 136 137 struct handle 138 { 139 struct prpsinfo **next_proc;/* points to next valid proc pointer */ 140 int remaining; /* number of pointers remaining */ 141 }; 142 143 /* 144 * These definitions control the format of the per-process area 145 */ 146 147 static char header[] = 148 #ifdef SHOW_NICE 149 " PID X PRI NICE SIZE RES STATE TIME CPU COMMAND"; 150 #else 151 " PID X PRI THR SIZE RES STATE TIME CPU COMMAND"; 152 #endif 153 /* 0123456 -- field to fill in starts at header+6 */ 154 #define UNAME_START 6 155 #define Proc_format \ 156 "%5d %-8.8s %3d %4d %5s %5s %-5s %6s %8.4f%% %.16s" 157 158 char *state_abbrev[] = 159 {"oncpu", "run", "sleep", "stop", "idle", "zombie"}; 160 161 #define sZOMB 5 162 int process_states[8]; 163 char *procstatenames[] = 164 { 165 " on cpu, ", " running, ", " sleeping, ", " stopped, ", 166 " idling ", " zombie, ", 167 NULL 168 }; 169 170 int cpu_states[CPUSTATES]; 171 char *cpustatenames[] = 172 {"idle", "user", "kernel", "wait", NULL}; 173 174 175 /* these are for detailing the memory statistics */ 176 long memory_stats[5]; 177 char *memorynames[] = 178 {"K phys, ", "K used, ", "K free, ", "K swapUsed, ", "K swapFree", NULL}; 179 180 /* these are names given to allowed sorting orders -- first is default */ 181 char *ordernames[] = 182 {"state", "cpu", "size", "res", "time", "pid", "uid", "rpid", "ruid", NULL}; 183 184 /* forward definitions for comparison functions */ 185 int proc_compare(); 186 int compare_cpu(); 187 int compare_size(); 188 int compare_res(); 189 int compare_time(); 190 int compare_pid(); 191 int compare_uid(); 192 int compare_rpid(); 193 int compare_ruid(); 194 195 int (*proc_compares[])() = { 196 proc_compare, 197 compare_cpu, 198 compare_size, 199 compare_res, 200 compare_time, 201 compare_pid, 202 compare_uid, 203 compare_rpid, 204 compare_ruid, 205 NULL }; 206 207 208 static int kmem = -1; 209 static int nproc; 210 static int bytes; 211 static struct prpsinfo *pbase; 212 static struct prpsinfo **pref; 213 static DIR *procdir; 214 215 /* useful externals */ 216 extern int errno; 217 extern char *sys_errlist[]; 218 extern char *myname; 219 extern long percentages (); 220 extern int check_nlist (); 221 extern int getkval (); 222 extern void perror (); 223 extern void getptable (); 224 extern void quit (); 225 extern int nlist (); 226 227 /* fwd dcls */ 228 static int kmet_init(void ); 229 static int get_cpustates(int *new); 230 231 232 int 233 machine_init (struct statics *statics) 234 { 235 static struct var v; 236 int i; 237 238 /* fill in the statics information */ 239 statics->procstate_names = procstatenames; 240 statics->cpustate_names = cpustatenames; 241 statics->memory_names = memorynames; 242 statics->order_names = ordernames; 243 244 /* get the list of symbols we want to access in the kernel */ 245 if (nlist (UNIX, nlst)) 246 { 247 (void) fprintf (stderr, "Unable to nlist %s\n", UNIX); 248 return (-1); 249 } 250 251 /* make sure they were all found */ 252 if (check_nlist (nlst) > 0) 253 return (-1); 254 255 /* open kernel memory */ 256 if ((kmem = open (KMEM, O_RDONLY)) == -1) 257 { 258 perror (KMEM); 259 return (-1); 260 } 261 262 v.v_proc=200; /* arbitrary default */ 263 /* get the symbol values out of kmem */ 264 /* NPROC Tuning parameter for max number of processes */ 265 (void) getkval (nlst[X_V].n_value, &v, sizeof (struct var), nlst[X_V].n_name); 266 nproc = v.v_proc; 267 maxprocs = nproc; 268 269 /* stash away certain offsets for later use */ 270 mpid_offset = nlst[X_MPID].n_value; 271 avenrun_offset = nlst[X_AVENRUN].n_value; 272 273 /* allocate space for proc structure array and array of pointers */ 274 bytes = nproc * sizeof (struct prpsinfo); 275 pbase = (struct prpsinfo *) malloc (bytes); 276 pref = (struct prpsinfo **) malloc (nproc * sizeof (struct prpsinfo *)); 277 278 pagesz = sysconf(_SC_PAGESIZE); 279 280 281 /* Just in case ... */ 282 if (pbase == (struct prpsinfo *) NULL || pref == (struct prpsinfo **) NULL) 283 { 284 (void) fprintf (stderr, "%s: can't allocate sufficient memory\n", myname); 285 return (-1); 286 } 287 288 if (!(procdir = opendir (PROCFS))) 289 { 290 (void) fprintf (stderr, "Unable to open %s\n", PROCFS); 291 return (-1); 292 } 293 294 if (chdir (PROCFS)) 295 { /* handy for later on when we're reading it */ 296 (void) fprintf (stderr, "Unable to chdir to %s\n", PROCFS); 297 return (-1); 298 } 299 300 301 kmet_init(); 302 303 /* all done! */ 304 return (0); 305 } 306 307 char * 308 format_header (char *uname_field) 309 { 310 register char *ptr; 311 312 ptr = header + UNAME_START; 313 while (*uname_field != '\0') 314 *ptr++ = *uname_field++; 315 316 return (header); 317 } 318 319 void 320 get_system_info (struct system_info *si) 321 { 322 long avenrun[3]; 323 long mem; 324 static time_t cp_old[CPUSTATES]; 325 static time_t cp_diff[CPUSTATES]; /* for cpu state percentages */ 326 register int i; 327 static long swap_total; 328 static long swap_free; 329 int new_states[CPUSTATES]; 330 331 get_cpustates(new_states); 332 333 /* convert cp_time counts to percentages */ 334 (void) percentages (CPUSTATES, cpu_states, new_states, cp_old, cp_diff); 335 336 337 si->last_pid = -1; 338 /* get mpid -- process id of last process 339 * svr5 is nextpid - next pid to be assigned (already incremented) 340 */ 341 (void) getkval (mpid_offset, &(si->last_pid), sizeof (si->last_pid), 342 "nextpid"); 343 (si->last_pid)--; /* so we shld decrement for display */ 344 345 346 /* get load average array */ 347 (void) getkval (avenrun_offset, (int *) avenrun, sizeof (avenrun), "avenrun"); 348 /* convert load averages to doubles */ 349 for (i = 0; i < 3; i++) 350 si->load_avg[i] = loaddouble(avenrun[i]); 351 352 mem = sysconf(_SC_TOTAL_MEMORY); /* physical mem */ 353 memory_stats[0] = pagetok (mem); 354 355 mem = kmet_get_freemem(); /* free mem */ 356 memory_stats[2] = pagetok (mem); 357 358 /* mem = sysconf(_SC_GENERAL_MEMORY); */ 359 memory_stats[1] = memory_stats[0] - memory_stats[2]; /* active */ 360 361 get_swapinfo(&swap_total, &swap_free); 362 memory_stats[3] = pagetok(swap_total - swap_free); 363 memory_stats[4] = pagetok(swap_free); 364 365 366 /* set arrays and strings */ 367 si->cpustates = cpu_states; 368 si->memory = memory_stats; 369 } 370 371 static struct handle handle; 372 373 caddr_t 374 get_process_info ( 375 struct system_info *si, 376 struct process_select *sel, 377 int idx) 378 { 379 register int i; 380 register int total_procs; 381 register int active_procs; 382 register struct prpsinfo **prefp; 383 register struct prpsinfo *pp; 384 385 /* these are copied out of sel for speed */ 386 int show_idle; 387 int show_system; 388 int show_uid; 389 390 /* Get current number of processes */ 391 392 /* read all the proc structures */ 393 getptable (pbase); 394 395 /* get a pointer to the states summary array */ 396 si->procstates = process_states; 397 398 /* set up flags which define what we are going to select */ 399 show_idle = sel->idle; 400 show_system = sel->system; 401 show_uid = sel->uid != -1; 402 403 nproc = kmet_get_nproc(); 404 405 /* count up process states and get pointers to interesting procs */ 406 total_procs = 0; 407 active_procs = 0; 408 (void) memset (process_states, 0, sizeof (process_states)); 409 prefp = pref; 410 411 for (pp = pbase, i = 0; i < nproc; pp++, i++) 412 { 413 /* 414 * Place pointers to each valid proc structure in pref[]. 415 * Process slots that are actually in use have a non-zero 416 * status field. Processes with PR_ISSYS set are system 417 * processes---these get ignored unless show_sysprocs is set. 418 */ 419 if ((pp->pr_state >= SONPROC && pp->pr_state <= SIDL) && 420 (show_system || ((pp->pr_flag & PR_ISSYS) == 0))) 421 { 422 total_procs++; 423 process_states[pp->pr_state]++; 424 if ((!ZOMBIE(pp)) && 425 (show_idle || (pp->pr_state == SRUN) || (pp->pr_state == SONPROC)) && 426 (!show_uid || pp->pr_uid == (uid_t) sel->uid)) 427 { 428 *prefp++ = pp; 429 active_procs++; 430 } 431 if (ZOMBIE(pp)) 432 process_states[sZOMB]++; /* invented */ 433 434 } 435 } 436 437 /* if requested, sort the "interesting" processes */ 438 qsort ((char *) pref, active_procs, sizeof (struct prpsinfo *), 439 proc_compares[idx]); 440 441 /* remember active and total counts */ 442 si->p_total = total_procs; 443 si->P_ACTIVE = active_procs; 444 445 /* pass back a handle */ 446 handle.next_proc = pref; 447 handle.remaining = active_procs; 448 return ((caddr_t) & handle); 449 } 450 451 /* 452 * cpu percentage calculation is as fm ps.c 453 * seems to be ratio of (sys+user time used)/(elapsed time) 454 * i.e percent of cpu utilised when on cpu 455 */ 456 static double percent_cpu( struct prpsinfo *pp) 457 { 458 static time_t tim = 0L; 459 time_t starttime; 460 time_t ctime; 461 time_t etime; 462 463 /* if (tim == 0L) */ 464 tim = time((time_t *) 0); 465 starttime = pp->pr_start.tv_sec; 466 if (pp->pr_start.tv_nsec > 500000000) 467 starttime++; 468 etime = (tim - starttime); 469 ctime = pp->pr_time.tv_sec; 470 if (pp->pr_time.tv_nsec > 500000000) 471 ctime++; 472 if (etime) 473 { 474 /* return (float)(ctime * 100) / (unsigned)etime; */ 475 /* this was ocasionally giving vals >100 for some 476 * unknown reason so the below normalises it 477 */ 478 479 double pct; 480 pct = (float)(ctime * 100) / (unsigned)etime; 481 return (pct < 100.0) ? pct : 100.00; 482 } 483 return 0.00; 484 } 485 486 487 char fmt[MAX_COLS]; /* static area where result is built */ 488 489 char * 490 format_next_process ( 491 caddr_t handle, 492 char *(*get_userid) ()) 493 { 494 register struct prpsinfo *pp; 495 struct handle *hp; 496 register long cputime; 497 register double pctcpu; 498 499 /* find and remember the next proc structure */ 500 hp = (struct handle *) handle; 501 pp = *(hp->next_proc++); 502 hp->remaining--; 503 504 /* get the cpu usage and calculate the cpu percentages */ 505 cputime = pp->pr_time.tv_sec; 506 pctcpu = percent_cpu(pp); 507 508 509 /* format this entry */ 510 (void) sprintf (fmt, 511 Proc_format, 512 pp->pr_pid, 513 (*get_userid) (pp->pr_uid), 514 pp->pr_pri, 515 #ifdef SHOW_NICE 516 pp->pr_nice, 517 #else 518 (u_short)pp->pr_nlwp < 999 ? (u_short)pp->pr_nlwp : 999, 519 #endif 520 format_k(SIZE_K(pp)), 521 format_k(RSS_K(pp)), 522 (ZOMBIE(pp)) ? state_abbrev[sZOMB] 523 : state_abbrev[pp->pr_state], 524 format_time(cputime), 525 /* 100.0 * */ pctcpu, 526 printable(pp->pr_fname)); 527 528 /* return the result */ 529 return (fmt); 530 } 531 532 /* 533 * check_nlist(nlst) - checks the nlist to see if any symbols were not 534 * found. For every symbol that was not found, a one-line 535 * message is printed to stderr. The routine returns the 536 * number of symbols NOT found. 537 */ 538 int 539 check_nlist (register struct nlist *nlst) 540 { 541 register int i; 542 543 /* check to see if we got ALL the symbols we requested */ 544 /* this will write one line to stderr for every symbol not found */ 545 546 i = 0; 547 while (nlst->n_name != NULL) 548 { 549 if (nlst->n_value == 0) 550 { 551 /* this one wasn't found */ 552 (void) fprintf (stderr, "kernel: no symbol named `%s'\n", nlst->n_name); 553 i = 1; 554 } 555 nlst++; 556 } 557 return (i); 558 } 559 560 561 /* 562 * getkval(offset, ptr, size, refstr) - get a value out of the kernel. 563 * "offset" is the byte offset into the kernel for the desired value, 564 * "ptr" points to a buffer into which the value is retrieved, 565 * "size" is the size of the buffer (and the object to retrieve), 566 * "refstr" is a reference string used when printing error meessages, 567 * if "refstr" starts with a '!', then a failure on read will not 568 * be fatal (this may seem like a silly way to do things, but I 569 * really didn't want the overhead of another argument). 570 * 571 */ 572 int 573 getkval ( 574 unsigned long offset, 575 int *ptr, 576 int size, 577 char *refstr) 578 { 579 if (lseek (kmem, (long) offset, 0) == -1) 580 { 581 if (*refstr == '!') 582 refstr++; 583 (void) fprintf (stderr, "%s: lseek to %s: %s\n", 584 myname, refstr, sys_errlist[errno]); 585 quit (22); 586 } 587 if (read (kmem, (char *) ptr, size) == -1) 588 if (*refstr == '!') 589 /* we lost the race with the kernel, process isn't in memory */ 590 return (0); 591 else 592 { 593 (void) fprintf (stderr, "%s: reading %s: %s\n", 594 myname, refstr, sys_errlist[errno]); 595 quit (23); 596 } 597 return (1); 598 } 599 600 /* ----------------- comparison routines for qsort ---------------- */ 601 602 /* First, the possible comparison keys. These are defined in such a way 603 that they can be merely listed in the source code to define the actual 604 desired ordering. 605 */ 606 607 #define ORDERKEY_PCTCPU if (dresult = percent_cpu (p2) - percent_cpu (p1),\ 608 (result = dresult > 0.0 ? 1 : \ 609 dresult < 0.0 ? -1 : 0) == 0) 610 611 #define ORDERKEY_CPTICKS if ((result = p2->pr_time.tv_sec - p1->pr_time.tv_sec) == 0) 612 #define ORDERKEY_STATE if ((result = (long) (sorted_state[p2->pr_state] - \ 613 sorted_state[p1->pr_state])) == 0) 614 615 #define ORDERKEY_PRIO if ((result = p2->pr_pri - p1->pr_pri) == 0) 616 #define ORDERKEY_RSSIZE if ((result = p2->pr_rssize - p1->pr_rssize) == 0) 617 #define ORDERKEY_MEM if ((result = (p2->pr_size - p1->pr_size)) == 0) 618 619 #define ORDERKEY_PID if ((result = (p2->pr_pid - p1->pr_pid)) == 0) 620 #define ORDERKEY_UID if ((result = (p2->pr_uid - p1->pr_uid)) == 0) 621 #define ORDERKEY_RPID if ((result = (p1->pr_pid - p2->pr_pid)) == 0) 622 #define ORDERKEY_RUID if ((result = (p1->pr_uid - p2->pr_uid)) == 0) 623 624 /* states enum {SONPROC, SRUN, SSLEEP, SSTOP, SIDL} */ 625 unsigned char sorted_state[] = 626 { 627 7, /* onproc */ 628 6, /* run */ 629 5, /* sleep */ 630 4, /* stop */ 631 3, /* idle */ 632 2, /* zombie */ 633 0, /* unused */ 634 0 /* unused */ 635 }; 636 637 #if 0 638 /* 639 * proc_compare - original singleton comparison function for "qsort" 640 * Compares the resource consumption of two processes using five 641 * distinct keys. The keys (in descending order of importance) are: 642 * percent cpu, cpu ticks, state, resident set size, total virtual 643 * memory usage. The process states are ordered as follows (from least 644 * to most important): WAIT, zombie, sleep, stop, start, run. The 645 * array declaration below maps a process state index into a number 646 * that reflects this ordering. 647 */ 648 /* default comparison rtn */ 649 int 650 original_proc_compare ( 651 struct prpsinfo **pp1, 652 struct prpsinfo **pp2) 653 { 654 register struct prpsinfo *p1; 655 register struct prpsinfo *p2; 656 register long result; 657 double dresult; 658 659 /* remove one level of indirection */ 660 p1 = *pp1; 661 p2 = *pp2; 662 663 /* compare percent cpu (pctcpu) */ 664 dresult = percent_cpu(p2) - percent_cpu (p1); 665 result = dresult > 0.0 ? 1 : 666 dresult < 0.0 ? -1 : 0; 667 if (result) 668 { 669 /* use cpticks to break the tie */ 670 if ((result = p2->pr_time.tv_sec - p1->pr_time.tv_sec) == 0) 671 { 672 /* use process state to break the tie */ 673 if ((result = (long) (sorted_state[p2->pr_state] - 674 sorted_state[p1->pr_state])) == 0) 675 { 676 /* use priority to break the tie */ 677 if ((result = p2->pr_pri - p1->pr_pri) == 0) 678 { 679 /* use resident set size (rssize) to break the tie */ 680 if ((result = p2->pr_rssize - p1->pr_rssize) == 0) 681 { 682 /* use total memory to break the tie */ 683 result = (p2->pr_size - p1->pr_size); 684 } 685 } 686 } 687 } 688 } 689 return (result); 690 } 691 #endif /* original comparison rtn */ 692 693 /* compare_state - comparison function for sorting by state,pri,time,size */ 694 int 695 proc_compare ( 696 struct prpsinfo **pp1, 697 struct prpsinfo **pp2) 698 { 699 register struct prpsinfo *p1; 700 register struct prpsinfo *p2; 701 register long result; 702 double dresult; 703 704 /* remove one level of indirection */ 705 p1 = *pp1; 706 p2 = *pp2; 707 708 ORDERKEY_STATE 709 ORDERKEY_PRIO 710 ORDERKEY_CPTICKS 711 ORDERKEY_RSSIZE 712 ORDERKEY_MEM 713 ORDERKEY_PCTCPU 714 ; 715 716 return (result); 717 } 718 719 720 /* compare_cpu - the comparison function for sorting by cpu % (deflt) */ 721 int 722 compare_cpu ( 723 struct prpsinfo **pp1, 724 struct prpsinfo **pp2) 725 { 726 register struct prpsinfo *p1; 727 register struct prpsinfo *p2; 728 register long result; 729 double dresult; 730 731 /* remove one level of indirection */ 732 p1 = *pp1; 733 p2 = *pp2; 734 735 ORDERKEY_PCTCPU 736 ORDERKEY_CPTICKS 737 ORDERKEY_STATE 738 ORDERKEY_PRIO 739 ORDERKEY_RSSIZE 740 ORDERKEY_MEM 741 ; 742 743 return (result); 744 } 745 746 /* compare_size - the comparison function for sorting by total memory usage */ 747 int 748 compare_size ( 749 struct prpsinfo **pp1, 750 struct prpsinfo **pp2) 751 { 752 register struct prpsinfo *p1; 753 register struct prpsinfo *p2; 754 register long result; 755 double dresult; 756 757 /* remove one level of indirection */ 758 p1 = *pp1; 759 p2 = *pp2; 760 761 ORDERKEY_MEM 762 ORDERKEY_RSSIZE 763 ORDERKEY_PCTCPU 764 ORDERKEY_CPTICKS 765 ORDERKEY_STATE 766 ORDERKEY_PRIO 767 ; 768 769 return (result); 770 } 771 772 /* compare_res - the comparison function for sorting by resident set size */ 773 int 774 compare_res ( 775 struct prpsinfo **pp1, 776 struct prpsinfo **pp2) 777 { 778 register struct prpsinfo *p1; 779 register struct prpsinfo *p2; 780 register long result; 781 double dresult; 782 783 /* remove one level of indirection */ 784 p1 = *pp1; 785 p2 = *pp2; 786 787 ORDERKEY_RSSIZE 788 ORDERKEY_MEM 789 ORDERKEY_PCTCPU 790 ORDERKEY_CPTICKS 791 ORDERKEY_STATE 792 ORDERKEY_PRIO 793 ; 794 795 return (result); 796 } 797 798 /* compare_time - the comparison function for sorting by total cpu time */ 799 int 800 compare_time ( 801 struct prpsinfo **pp1, 802 struct prpsinfo **pp2) 803 { 804 register struct prpsinfo *p1; 805 register struct prpsinfo *p2; 806 register long result; 807 double dresult; 808 809 /* remove one level of indirection */ 810 p1 = *pp1; 811 p2 = *pp2; 812 813 ORDERKEY_CPTICKS 814 ORDERKEY_PCTCPU 815 ORDERKEY_STATE 816 ORDERKEY_PRIO 817 ORDERKEY_MEM 818 ORDERKEY_RSSIZE 819 ; 820 821 return (result); 822 } 823 824 /* compare_pid - the comparison function for sorting by pid */ 825 int 826 compare_pid ( 827 struct prpsinfo **pp1, 828 struct prpsinfo **pp2) 829 { 830 register struct prpsinfo *p1; 831 register struct prpsinfo *p2; 832 register long result; 833 double dresult; 834 835 /* remove one level of indirection */ 836 p1 = *pp1; 837 p2 = *pp2; 838 839 ORDERKEY_PID 840 ORDERKEY_CPTICKS 841 ORDERKEY_PCTCPU 842 ORDERKEY_STATE 843 ORDERKEY_PRIO 844 ORDERKEY_MEM 845 ORDERKEY_RSSIZE 846 ; 847 848 return (result); 849 } 850 851 /* compare_uid - the comparison function for sorting by user ID */ 852 int 853 compare_uid ( 854 struct prpsinfo **pp1, 855 struct prpsinfo **pp2) 856 { 857 register struct prpsinfo *p1; 858 register struct prpsinfo *p2; 859 register long result; 860 double dresult; 861 862 /* remove one level of indirection */ 863 p1 = *pp1; 864 p2 = *pp2; 865 866 ORDERKEY_UID 867 ORDERKEY_CPTICKS 868 ORDERKEY_PCTCPU 869 ORDERKEY_STATE 870 ORDERKEY_PRIO 871 ORDERKEY_MEM 872 ORDERKEY_RSSIZE 873 ; 874 875 return (result); 876 } 877 878 /* compare_rpid - the comparison function for sorting by pid ascending */ 879 int 880 compare_rpid ( 881 struct prpsinfo **pp1, 882 struct prpsinfo **pp2) 883 { 884 register struct prpsinfo *p1; 885 register struct prpsinfo *p2; 886 register long result; 887 double dresult; 888 889 /* remove one level of indirection */ 890 p1 = *pp1; 891 p2 = *pp2; 892 893 ORDERKEY_RPID 894 ORDERKEY_CPTICKS 895 ORDERKEY_PCTCPU 896 ORDERKEY_STATE 897 ORDERKEY_PRIO 898 ORDERKEY_MEM 899 ORDERKEY_RSSIZE 900 ; 901 902 return (result); 903 } 904 905 /* compare_uid - the comparison function for sorting by user ID ascending */ 906 int 907 compare_ruid ( 908 struct prpsinfo **pp1, 909 struct prpsinfo **pp2) 910 { 911 register struct prpsinfo *p1; 912 register struct prpsinfo *p2; 913 register long result; 914 double dresult; 915 916 /* remove one level of indirection */ 917 p1 = *pp1; 918 p2 = *pp2; 919 920 ORDERKEY_RUID 921 ORDERKEY_CPTICKS 922 ORDERKEY_PCTCPU 923 ORDERKEY_STATE 924 ORDERKEY_PRIO 925 ORDERKEY_MEM 926 ORDERKEY_RSSIZE 927 ; 928 929 return (result); 930 } 931 932 933 /* ---------------- helper rtns ---------------- */ 934 935 /* 936 * get process table 937 */ 938 void 939 getptable (struct prpsinfo *baseptr) 940 { 941 struct prpsinfo *currproc; /* pointer to current proc structure */ 942 int numprocs = 0; 943 struct dirent *direntp; 944 945 currproc = baseptr; 946 for (rewinddir (procdir); direntp = readdir (procdir);) 947 { 948 int fd; 949 char buf[30]; 950 951 sprintf(buf,"%s/psinfo", direntp->d_name); 952 953 if ((fd = open (buf, O_RDONLY)) < 0) 954 continue; 955 956 if (read(fd, currproc, sizeof(psinfo_t)) != sizeof(psinfo_t)) 957 { 958 (void) close (fd); 959 continue; 960 } 961 962 numprocs++; 963 currproc++; 964 965 (void) close (fd); 966 967 /* Atypical place for growth */ 968 if (numprocs >= maxprocs) 969 { 970 reallocproc(2 * numprocs); 971 currproc = (struct prpsinfo *) 972 ((char *)baseptr + sizeof(psinfo_t) * numprocs); 973 } 974 975 } 976 977 if (nproc != numprocs) 978 nproc = numprocs; 979 } 980 981 /* return the owner of the specified process, for use in commands.c as we're 982 running setuid root */ 983 int 984 proc_owner (int pid) 985 { 986 register struct prpsinfo *p; 987 int i; 988 for (i = 0, p = pbase; i < nproc; i++, p++) 989 if (p->pr_pid == (pid_t)pid) 990 return ((int)(p->pr_uid)); 991 992 return (-1); 993 } 994 995 int 996 setpriority (int dummy, int who, int niceval) 997 { 998 int scale; 999 int prio; 1000 pcinfo_t pcinfo; 1001 pcparms_t pcparms; 1002 tsparms_t *tsparms; 1003 1004 strcpy (pcinfo.pc_clname, "TS"); 1005 if (priocntl (0, 0, PC_GETCID, (caddr_t) & pcinfo) == -1) 1006 return (-1); 1007 1008 prio = niceval; 1009 if (prio > PRIO_MAX) 1010 prio = PRIO_MAX; 1011 else if (prio < PRIO_MIN) 1012 prio = PRIO_MIN; 1013 1014 tsparms = (tsparms_t *) pcparms.pc_clparms; 1015 scale = ((tsinfo_t *) pcinfo.pc_clinfo)->ts_maxupri; 1016 tsparms->ts_uprilim = tsparms->ts_upri = -(scale * prio) / 20; 1017 pcparms.pc_cid = pcinfo.pc_cid; 1018 1019 if (priocntl (P_PID, who, PC_SETPARMS, (caddr_t) & pcparms) == -1) 1020 return (-1); 1021 1022 return (0); 1023 } 1024 1025 1026 get_swapinfo(long *total, long *fr) 1027 { 1028 register int cnt, i; 1029 register long t, f; 1030 struct swaptable *swt; 1031 struct swapent *ste; 1032 static char path[256]; 1033 1034 /* get total number of swap entries */ 1035 cnt = swapctl(SC_GETNSWP, 0); 1036 1037 /* allocate enough space to hold count + n swapents */ 1038 swt = (struct swaptable *)malloc(sizeof(int) + 1039 cnt * sizeof(struct swapent)); 1040 if (swt == NULL) 1041 { 1042 *total = 0; 1043 *fr = 0; 1044 return; 1045 } 1046 swt->swt_n = cnt; 1047 1048 /* fill in ste_path pointers: we don't care about the paths, so we point 1049 them all to the same buffer */ 1050 ste = &(swt->swt_ent[0]); 1051 i = cnt; 1052 while (--i >= 0) 1053 { 1054 ste++->ste_path = path; 1055 } 1056 1057 /* grab all swap info */ 1058 swapctl(SC_LIST, swt); 1059 1060 /* walk thru the structs and sum up the fields */ 1061 t = f = 0; 1062 ste = &(swt->swt_ent[0]); 1063 i = cnt; 1064 while (--i >= 0) 1065 { 1066 /* dont count slots being deleted */ 1067 if (!(ste->ste_flags & ST_INDEL) ) 1068 { 1069 t += ste->ste_pages; 1070 f += ste->ste_free; 1071 } 1072 ste++; 1073 } 1074 1075 /* fill in the results */ 1076 *total = t; 1077 *fr = f; 1078 free(swt); 1079 } 1080 1081 1082 /* 1083 * When we reach a proc limit, we need to realloc the stuff. 1084 */ 1085 static void reallocproc(int n) 1086 { 1087 int bytes; 1088 struct oldproc *op, *endbase; 1089 1090 if (n < maxprocs) 1091 return; 1092 1093 maxprocs = n; 1094 1095 /* allocate space for proc structure array and array of pointers */ 1096 bytes = maxprocs * sizeof(psinfo_t) ; 1097 pbase = (struct prpsinfo *) realloc(pbase, bytes); 1098 pref = (struct prpsinfo **) realloc(pref, 1099 maxprocs * sizeof(struct prpsinfo *)); 1100 1101 /* Just in case ... */ 1102 if (pbase == (struct prpsinfo *) NULL || pref == (struct prpsinfo **) NULL) 1103 { 1104 fprintf (stderr, "%s: can't allocate sufficient memory\n", myname); 1105 quit(1); 1106 } 1107 } 1108 1109 /* ---------------------------------------------------------------- */ 1110 /* Access kernel Metrics 1111 * SVR5 uses metreg inteface to Kernel statistics (metrics) 1112 * see /usr/include/mas.h, /usr/include/metreg.h 1113 */ 1114 1115 #include <sys/mman.h> 1116 #include <sys/dl.h> 1117 #include <mas.h> 1118 #include <metreg.h> 1119 1120 static int md; /* metric descriptor handle */ 1121 static uint32 ncpu; /* number of processors in system */ 1122 1123 /* fwd dcls */ 1124 static uint32 kmet_get_cpu( int type, char *desc); 1125 static void kmet_verify( 1126 uint32 md, metid_t id, units_t units, type_t mettype, 1127 uint32 metsz, uint32 nobj, uint32 nlocs, resource_t res_id, 1128 uint32 ressz ) ; 1129 1130 1131 static int get_cpustates(int *new) 1132 { 1133 new[0] = (int)kmet_get_cpu( MPC_CPU_IDLE, "idle"); 1134 new[1] = (int)kmet_get_cpu( MPC_CPU_USR, "usr"); 1135 new[2] = (int)kmet_get_cpu( MPC_CPU_SYS, "sys"); 1136 new[3] = (int)kmet_get_cpu( MPC_CPU_WIO, "wio"); 1137 } 1138 1139 1140 /* initialises kernel metrics access and gets #cpus */ 1141 static int kmet_init() 1142 { 1143 uint32 *ncpu_p; 1144 1145 /* open (and map in) the metric access file and assoc data structures */ 1146 if( ( md = mas_open( MAS_FILE, MAS_MMAP_ACCESS ) ) < 0 ) 1147 { 1148 (void)fprintf(stderr,"mas_open failed\n"); 1149 mas_perror(); 1150 quit(10); 1151 } 1152 1153 /* verify the NCPU metric is everything we expect */ 1154 kmet_verify(md, NCPU, CPUS, CONFIGURABLE, sizeof(short), 1155 1, 1, MAS_SYSTEM, sizeof(uint32) ); 1156 1157 /* get the number of cpu's on the system */ 1158 if( (ncpu_p = (uint32 *)mas_get_met( md, NCPU, 0 )) == NULL ) 1159 { 1160 (void)fprintf(stderr,"mas_get_met of ncpu failed\n"); 1161 mas_perror(); 1162 quit(12); 1163 } 1164 ncpu = (uint32)(*(short *)ncpu_p); 1165 1166 /* check that MPC_CPU_IDLE is of the form we expect 1167 * ( paranoically we should check the rest as well but ... ) 1168 */ 1169 kmet_verify( md, MPC_CPU_IDLE, TIX, PROFILE, sizeof(uint32), 1170 1, ncpu, NCPU, sizeof(short) ); 1171 1172 kmet_verify( md, PROCUSE, PROCESSES, COUNT, sizeof(uint32), 1173 1, 1, MAS_SYSTEM, sizeof(uint32) ); 1174 nproc = kmet_get_nproc(); 1175 1176 return 0; 1177 } 1178 1179 /* done with kernel metrics access */ 1180 static int 1181 kmet_done() 1182 { 1183 if ( mas_close( md ) < 0 ) 1184 { 1185 (void)fprintf(stderr,"mas_close failed\n"); 1186 mas_perror(); 1187 quit(14); 1188 } 1189 } 1190 1191 1192 static uint32 1193 kmet_get_cpu( int type, char *desc) 1194 { 1195 int i; 1196 uint32 r=0, rtot=0 ; 1197 1198 for (i=0; i <ncpu; i++) 1199 { 1200 r=*(uint32 *)mas_get_met( md, (metid_t)type, 0 ); 1201 if ( !r) 1202 { 1203 (void)fprintf(stderr,"mas_get_met of %s failed\n", desc); 1204 mas_perror(); 1205 quit(12); 1206 } 1207 rtot += r; /* sum them for multi cpus */ 1208 } 1209 return rtot /* /ncpu */ ; 1210 } 1211 1212 static int 1213 kmet_get_freemem() 1214 { 1215 dl_t *fm_p, fm, fmc, denom; 1216 time_t td1; 1217 static time_t td0; 1218 static dl_t fm_old; 1219 1220 1221 td1 = time(NULL); 1222 if ((fm_p = (dl_t *)mas_get_met( md, FREEMEM, 0 )) == NULL ) 1223 { 1224 (void)fprintf(stderr,"mas_get_met of freemem failed\n"); 1225 mas_perror(); 1226 quit(12); 1227 } 1228 fm = *fm_p; 1229 1230 denom.dl_hop = 0; 1231 denom.dl_lop = (long) (td1 - td0); 1232 td0 = td1; 1233 1234 /* calculate the freemem difference divided by the time diff 1235 * giving the freemem in that time sample 1236 * (new - old) / (time_between_samples) 1237 */ 1238 fmc = lsub(fm, fm_old); 1239 fm_old = fm; 1240 1241 fmc = ldivide(fmc, denom); 1242 return fmc.dl_lop; 1243 } 1244 1245 /* 1246 * return # of processes currently executing on system 1247 */ 1248 static int 1249 kmet_get_nproc() 1250 { 1251 uint32 *p; 1252 if ((p = (uint32 *)mas_get_met( md, PROCUSE, 0 )) == NULL ) 1253 { 1254 (void)fprintf(stderr,"mas_get_met of procuse failed\n"); 1255 mas_perror(); 1256 quit(11); 1257 } 1258 nproc = (int)*p; 1259 } 1260 1261 1262 /* 1263 * Function: kmet_verify 1264 * renamed from mas_usrtime example verify_met() fm Doug Souders 1265 * 1266 * Description: Verify the registration data associated with this metric 1267 * match what are expected. Cautious consumer applications 1268 * should do this sort of verification before using metrics. 1269 */ 1270 static void 1271 kmet_verify( 1272 uint32 md, /* metric descriptor */ 1273 metid_t id, /* metric id number */ 1274 units_t units, /* expected units of metric */ 1275 type_t mettype, /* expected type of metric */ 1276 uint32 metsz, /* expected object size of metric */ 1277 uint32 nobj, /* expected number of array elements */ 1278 uint32 nlocs, /* expected number of instances */ 1279 resource_t res_id, /* expected resource id number */ 1280 uint32 ressz /* expected resource object size */ 1281 ) 1282 { 1283 1284 char *name; /* the name of the metric */ 1285 units_t *units_p; /* the units of the metric */ 1286 type_t *mettype_p; /* type field of the metric */ 1287 uint32 *objsz_p; /* size of each element in met */ 1288 uint32 *nobj_p; /* num of elements >1 then array*/ 1289 uint32 *nlocs_p; /* total number of instances */ 1290 uint32 *status_p; /* status word (update|avail) */ 1291 resource_t *resource_p; /* the resource list of the met */ 1292 uint32 *resval_p; /* pointer to resource */ 1293 uint32 *ressz_p; /* size of the resource met */ 1294 1295 if (!(name = mas_get_met_name( md, id ))) 1296 { 1297 (void)fprintf(stderr,"mas_get_met_name failed\n"); 1298 mas_perror(); 1299 quit(11); 1300 } 1301 1302 if (!(status_p = mas_get_met_status( md, id ))) 1303 { 1304 (void)fprintf(stderr,"mas_get_met_status of %s failed\n", 1305 name ); 1306 mas_perror(); 1307 quit(11); 1308 } 1309 if ( *status_p != MAS_AVAILABLE ) 1310 { 1311 (void)fprintf(stderr,"unexpected status word for %s\n" 1312 "- expected %u got %u\n", 1313 name, MAS_AVAILABLE, *status_p ); 1314 quit(11); 1315 } 1316 if (!(units_p = mas_get_met_units( md, id ))) 1317 { 1318 (void)fprintf(stderr,"mas_get_met_units of %s failed\n", 1319 name ); 1320 mas_perror(); 1321 quit(11); 1322 } 1323 if (units != *units_p ) 1324 { 1325 (void)fprintf(stderr,"unexpected units for %s\n" 1326 "- expected %u got %u\n", 1327 name, units, *units_p ); 1328 quit(11); 1329 } 1330 1331 if (!(mettype_p = mas_get_met_type( md, id ))) 1332 { 1333 (void)fprintf(stderr,"mas_get_met_type of %s failed\n", 1334 name ); 1335 mas_perror(); 1336 quit(11); 1337 } 1338 if (mettype != *mettype_p ) 1339 { 1340 (void)fprintf(stderr,"unexpected metric type for %s\n" 1341 "- expected %u got %u\n", 1342 name, mettype , *mettype_p ); 1343 quit(11); 1344 } 1345 1346 if (!(objsz_p = mas_get_met_objsz( md, id ))) 1347 { 1348 (void)fprintf(stderr,"mas_get_met_objsz of %s failed\n", name ); 1349 mas_perror(); 1350 quit(11); 1351 } 1352 if (*objsz_p != metsz ) 1353 { 1354 (void)fprintf(stderr,"unexpected object size for %s\n" 1355 "- expected %u got %u\n", 1356 name, metsz, *objsz_p ); 1357 quit(11); 1358 } 1359 1360 if (!(nobj_p = mas_get_met_nobj( md, id ))) 1361 { 1362 (void)fprintf(stderr,"mas_get_met_nobj of %s failed\n", name ); 1363 mas_perror(); 1364 quit(11); 1365 } 1366 if (nobj != *nobj_p ) 1367 { 1368 (void)fprintf(stderr,"unexpected number of objects for %s\n" 1369 "- expected %u got %u\n", 1370 name, nobj, *nobj_p ); 1371 quit(11); 1372 } 1373 1374 /* get the number of instances that libmas thinks it knows about */ 1375 if (!(nlocs_p = mas_get_met_nlocs( md, id ))) 1376 { 1377 (void)fprintf(stderr,"mas_get_met_nlocs of %s failed\n", name ); 1378 mas_perror(); 1379 quit(11); 1380 } 1381 if (nlocs != *nlocs_p ) 1382 { 1383 (void)fprintf(stderr,"unexpected number of instances for %s" 1384 " - expected %u got %u\n", 1385 name, nlocs, *nlocs_p ); 1386 quit(11); 1387 1388 } 1389 /* get the resource list for the metric */ 1390 if (!(resource_p = mas_get_met_resources( md, id ))) 1391 { 1392 (void)fprintf(stderr,"mas_get_met_resources of %s failed\n", name ); 1393 mas_perror(); 1394 quit(11); 1395 } 1396 if (*resource_p != res_id ) 1397 { 1398 (void)fprintf(stderr,"unexpected resource id for %s\n" 1399 "- expected %u got %u\n", 1400 name, res_id, *resource_p); 1401 quit(11); 1402 } 1403 /* get the size of the resource */ 1404 if (!(ressz_p = mas_get_met_objsz( md, (metid_t)(*resource_p) ))) 1405 { 1406 (void)fprintf(stderr,"mas_get_met_objsz of resource failed\n"); 1407 mas_perror(); 1408 quit(11); 1409 } 1410 if (*ressz_p != ressz ) 1411 { 1412 (void)fprintf(stderr,"unexpected resource size for %s\n" 1413 "- expected %u got %u\n", 1414 name, ressz, *ressz_p ); 1415 quit(11); 1416 } 1417 /* 1418 * get the address of the resource 1419 */ 1420 if (!(resval_p = (uint32 *)mas_get_met( md, *resource_p, 0 ))) 1421 { 1422 (void)fprintf(stderr,"mas_get_met of resource failed\n"); 1423 mas_perror(); 1424 quit(11); 1425 } 1426 if (ressz == sizeof( short ) ) 1427 { 1428 if( (uint32)(*(short *)resval_p) != nlocs ) 1429 { 1430 (void)fprintf(stderr,"unexpected resource value for %s\n" 1431 "- expected %u got %u\n", 1432 name, nlocs, (uint32)(*(short *)resval_p) ); 1433 quit(11); 1434 } 1435 } 1436 else 1437 { /* assume size of uint32 */ 1438 if (*resval_p != nlocs ) 1439 { 1440 (void)fprintf(stderr,"unexpected resource value for %s\n" 1441 "- expected %u got %u\n", 1442 name, nlocs, *resval_p ); 1443 quit(11); 1444 } 1445 } 1446 return; 1447 } 1448 1449