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: OSF/1, Digital Unix 4.0, Compaq Tru64 5.0 37 * 38 * DESCRIPTION: 39 * This is the machine-dependent module for DEC OSF/1 and its descendents 40 * It is known to work on OSF/1 1.2, 1.3, 2.0-T3, 3.0, Digital Unix V4.0, 41 * Digital Unix 5.0, and Tru64 5.0. 42 * WARNING: if you use optimization with the standard "cc" compiler that 43 * . comes with V3.0 the resulting executable may core dump. If 44 * . this happens, recompile without optimization. 45 * 46 * LIBS: -lmld -lmach 47 * 48 * CFLAGS: -DHAVE_GETOPT -DORDER 49 * 50 * AUTHOR: Anthony Baxter, <anthony@aaii.oz.au> 51 * Derived originally from m_ultrix, by David S. Comay <dsc@seismo.css.gov>, 52 * although by now there is hardly any of the code from m_ultrix left. 53 * Helped a lot by having the source for syd(1), by Claus Kalle, and 54 * from several people at DEC who helped with providing information on 55 * some of the less-documented bits of the kernel interface. 56 * 57 * Modified: 31-Oct-94, Pat Welch, tpw@physics.orst.edu 58 * changed _mpid to pidtab for compatibility with OSF/1 version 3.0 59 * 60 * Modified: 13-Dec-94, William LeFebvre, lefebvre@dis.anl.gov 61 * removed used of pidtab (that was bogus) and changed things to 62 * automatically detect the absence of _mpid in the nlist and 63 * recover gracefully---this appears to be the only difference 64 * with 3.0. 65 * 66 * Modified: 3-Mar-00, Rainer Orth <ro@TechFak.Uni-Bielefeld.DE> 67 * added support for sort ordering. 68 */ 69 /* 70 * Theory of operation: 71 * 72 * Use Mach calls to build up a structure that contains all the sorts 73 * of stuff normally found in a struct proc in a BSD system. Then 74 * everything else uses this structure. This has major performance wins, 75 * and also should work for future versions of the O/S. 76 */ 77 78 #include "config.h" 79 80 #include <sys/types.h> 81 #include <sys/signal.h> 82 #include <sys/param.h> 83 84 #include <string.h> 85 #include <sys/user.h> 86 #include <stdio.h> 87 #include <nlist.h> 88 #include <math.h> 89 #include <sys/dir.h> 90 #include <sys/user.h> 91 #include <sys/proc.h> 92 #include <sys/dk.h> 93 #include <sys/vm.h> 94 #include <sys/file.h> 95 #include <sys/time.h> 96 /* #include <machine/pte.h> */ 97 /* forward declarations, needed by <net/if.h> included from <sys/table.h> */ 98 struct rtentry; 99 struct mbuf; 100 #include <sys/table.h> 101 #include <mach.h> 102 #include <mach/mach_types.h> 103 #include <mach/vm_statistics.h> 104 #include <sys/syscall.h> /* for SYS_setpriority, in setpriority(), below */ 105 106 107 #include "top.h" 108 #include "machine.h" 109 #include "utils.h" 110 111 extern int errno, sys_nerr; 112 extern char *sys_errlist[]; 113 #define strerror(e) (((e) >= 0 && (e) < sys_nerr) ? sys_errlist[(e)] : "Unknown error") 114 115 #define VMUNIX "/vmunix" 116 #define KMEM "/dev/kmem" 117 #define MEM "/dev/mem" 118 119 /* get_process_info passes back a handle. This is what it looks like: */ 120 121 struct handle 122 { 123 struct osf1_top_proc **next_proc; /* points to next valid proc pointer */ 124 int remaining; /* number of pointers remaining */ 125 }; 126 127 /* declarations for load_avg */ 128 #include "loadavg.h" 129 130 /* definitions for indices in the nlist array */ 131 #define X_MPID 0 132 133 static struct nlist nlst[] = { 134 { "_mpid" }, /* 0 */ 135 { 0 } 136 }; 137 138 /* Some versions of OSF/1 don't support reporting of the last PID. 139 This flag indicates whether or not we are reporting the last PID. */ 140 static int do_last_pid = 1; 141 142 /* 143 * These definitions control the format of the per-process area 144 */ 145 146 static char header[] = 147 " PID X PRI NICE SIZE RES STATE TIME CPU COMMAND"; 148 /* 01234567 -- field to fill in starts at header+7 */ 149 #define UNAME_START 7 150 151 #define Proc_format \ 152 "%6d %-8.8s %3d %4d %5s %5s %-5s %-6s %5.2f%% %s" 153 154 155 /* process state names for the "STATE" column of the display */ 156 /* the extra nulls in the string "run" are for adding a slash and 157 * the processor number when needed. Although OSF/1 doesnt support 158 * multiple processors yet, (and this module _certainly_ doesnt 159 * support it, either, we may as well plan for the future. :-) 160 */ 161 162 char *state_abbrev[] = 163 { 164 "", "run\0\0\0", "WAIT", "sleep", "sleep", "stop", "halt", "???", "zomb" 165 }; 166 167 168 static int kmem, mem; 169 170 /* values that we stash away in _init and use in later routines */ 171 172 static double logcpu; 173 174 /* these are retrieved from the kernel in _init */ 175 176 static unsigned long proc; 177 static int nproc; 178 static load_avg ccpu; 179 180 typedef long mtime_t; 181 182 /* these are offsets obtained via nlist and used in the get_ functions */ 183 184 static unsigned long mpid_offset; 185 186 /* these are for detailing the process states */ 187 188 int process_states[9]; 189 char *procstatenames[] = { 190 "", " running, ", " waiting, ", " sleeping, ", " idle, ", 191 " stopped, ", " halted, ", "", " zombie", 192 NULL 193 }; 194 195 /* these are for detailing the cpu states */ 196 197 int cpu_states[5]; 198 char *cpustatenames[] = { 199 "user", "nice", "system", "wio", "idle", NULL 200 }; 201 202 long old_cpu_ticks[5]; 203 204 /* these are for detailing the memory statistics */ 205 206 long memory_stats[5]; 207 char *memorynames[] = { 208 "K active, ", "K inactive, ", "K total, ", "K free", NULL 209 }; 210 211 long swap_stats[3]; 212 char *swapnames[] = { 213 "K in use, ", "K total", NULL 214 }; 215 216 /* these are names given to allowed sorting orders -- first is default */ 217 char *ordernames[] = { 218 "cpu", "size", "res", "time", NULL 219 }; 220 221 /* forward definitions for comparison functions */ 222 int compare_cpu(); 223 int compare_size(); 224 int compare_res(); 225 int compare_time(); 226 227 int (*proc_compares[])() = { 228 compare_cpu, 229 compare_size, 230 compare_res, 231 compare_time, 232 NULL 233 }; 234 235 /* these are for getting the memory statistics */ 236 237 static int pageshift; /* log base 2 of the pagesize */ 238 239 /* define pagetok in terms of pageshift */ 240 241 #define pagetok(size) ((size) << pageshift) 242 243 /* take a process, make it a mach task, and grab all the info out */ 244 void do_threads_calculations(); 245 246 /* 247 * Because I dont feel like repeatedly grunging through the kernel with 248 * Mach calls, and I also dont want the horrid performance hit this 249 * would give, I read the stuff I need out, and put in into my own 250 * structure, for later use. 251 */ 252 253 struct osf1_top_proc { 254 size_t p_mach_virt_size; 255 char p_mach_state; 256 int p_flag; 257 fixpt_t p_mach_pct_cpu; /* aka p_pctcpu */ 258 int used_ticks; 259 size_t process_size; 260 pid_t p_pid; 261 uid_t p_ruid; 262 char p_pri; 263 char p_nice; 264 size_t p_rssize; 265 char u_comm[PI_COMLEN + 1]; 266 } ; 267 268 /* these are for keeping track of the proc array */ 269 270 static int bytes; 271 static int pref_len; 272 static struct osf1_top_proc *pbase; 273 static struct osf1_top_proc **pref; 274 275 /* useful externals */ 276 extern int errno; 277 extern char *sys_errlist[]; 278 279 long percentages(); 280 281 machine_init(statics) 282 struct statics *statics; 283 { 284 register int i = 0; 285 register int pagesize; 286 struct tbl_sysinfo sibuf; 287 288 if ((kmem = open(KMEM, O_RDONLY)) == -1) { 289 perror(KMEM); 290 return(-1); 291 } 292 if ((mem = open(MEM, O_RDONLY)) == -1) { 293 perror(MEM); 294 return(-1); 295 } 296 297 /* get the list of symbols we want to access in the kernel */ 298 if (nlist(VMUNIX, nlst) == -1) 299 { 300 perror("TOP(nlist)"); 301 return (-1); 302 } 303 304 if (nlst[X_MPID].n_type == 0) 305 { 306 /* this kernel has no _mpid, so go without */ 307 do_last_pid = 0; 308 } 309 else 310 { 311 /* stash away mpid pointer for later use */ 312 mpid_offset = nlst[X_MPID].n_value; 313 } 314 315 /* get the symbol values out of kmem */ 316 nproc = table(TBL_PROCINFO, 0, (struct tbl_procinfo *)NULL, INT_MAX, 0); 317 318 /* allocate space for proc structure array and array of pointers */ 319 bytes = nproc * sizeof(struct osf1_top_proc); 320 pbase = (struct osf1_top_proc *)malloc(bytes); 321 pref = (struct osf1_top_proc **)malloc(nproc * 322 sizeof(struct osf1_top_proc *)); 323 324 /* Just in case ... */ 325 if (pbase == (struct osf1_top_proc *)NULL || 326 pref == (struct osf1_top_proc **)NULL) 327 { 328 fprintf(stderr, "top: cannot allocate sufficient memory\n"); 329 return(-1); 330 } 331 332 /* get the page size with "getpagesize" and calculate pageshift from it */ 333 pagesize = getpagesize(); 334 pageshift = 0; 335 while (pagesize > 1) 336 { 337 pageshift++; 338 pagesize >>= 1; 339 } 340 341 /* we only need the amount of log(2)1024 for our conversion */ 342 pageshift -= LOG1024; 343 344 /* fill in the statics information */ 345 statics->procstate_names = procstatenames; 346 statics->cpustate_names = cpustatenames; 347 statics->memory_names = memorynames; 348 statics->order_names = ordernames; 349 statics->swap_names = swapnames; 350 351 /* initialise this, for calculating cpu time */ 352 if (table(TBL_SYSINFO,0,&sibuf,1,sizeof(struct tbl_sysinfo))<0) { 353 perror("TBL_SYSINFO"); 354 return(-1); 355 } 356 old_cpu_ticks[0] = sibuf.si_user; 357 old_cpu_ticks[1] = sibuf.si_nice; 358 old_cpu_ticks[2] = sibuf.si_sys; 359 old_cpu_ticks[3] = sibuf.wait; 360 old_cpu_ticks[4] = sibuf.si_idle; 361 362 /* all done! */ 363 return(0); 364 } 365 366 char *format_header(uname_field) 367 register char *uname_field; 368 { 369 register char *ptr; 370 371 ptr = header + UNAME_START; 372 while (*uname_field != '\0') 373 { 374 *ptr++ = *uname_field++; 375 } 376 377 return(header); 378 } 379 380 void get_system_info(si) 381 struct system_info *si; 382 { 383 struct tbl_loadavg labuf; 384 struct tbl_sysinfo sibuf; 385 struct tbl_swapinfo swbuf; 386 vm_statistics_data_t vmstats; 387 int swap_pages=0,swap_free=0,i; 388 long new_ticks[5],diff_ticks[5]; 389 long delta_ticks; 390 391 if (do_last_pid) 392 { 393 /* last pid assigned */ 394 (void) getkval(mpid_offset, &(si->last_pid), sizeof(si->last_pid), 395 "_mpid"); 396 } 397 else 398 { 399 si->last_pid = -1; 400 } 401 402 /* get load averages */ 403 if (table(TBL_LOADAVG,0,&labuf,1,sizeof(struct tbl_loadavg))<0) { 404 perror("TBL_LOADAVG"); 405 return; 406 } 407 if (labuf.tl_lscale) /* scaled */ 408 for(i=0;i<3;i++) 409 si->load_avg[i] = ((double)labuf.tl_avenrun.l[i] / 410 (double)labuf.tl_lscale ); 411 else /* not scaled */ 412 for(i=0;i<3;i++) 413 si->load_avg[i] = labuf.tl_avenrun.d[i]; 414 415 /* array of cpu state counters */ 416 if (table(TBL_SYSINFO,0,&sibuf,1,sizeof(struct tbl_sysinfo))<0) { 417 perror("TBL_SYSINFO"); 418 return; 419 } 420 new_ticks[0] = sibuf.si_user ; new_ticks[1] = sibuf.si_nice; 421 new_ticks[2] = sibuf.si_sys ; new_ticks[3] = sibuf.wait; 422 new_ticks[4] = sibuf.si_idle; 423 delta_ticks=0; 424 for(i=0;i<5;i++) { 425 diff_ticks[i] = new_ticks[i] - old_cpu_ticks[i]; 426 delta_ticks += diff_ticks[i]; 427 old_cpu_ticks[i] = new_ticks[i]; 428 } 429 si->cpustates = cpu_states; 430 if(delta_ticks) 431 for(i=0;i<5;i++) 432 si->cpustates[i] = (int)( ( (double)diff_ticks[i] / 433 (double)delta_ticks ) * 1000 ); 434 435 /* memory information */ 436 /* this is possibly bogus - we work out total # pages by */ 437 /* adding up the free, active, inactive, wired down, and */ 438 /* zero filled. Anyone who knows a better way, TELL ME! */ 439 /* Change: dont use zero filled. */ 440 (void) vm_statistics(task_self(),&vmstats); 441 442 /* thanks DEC for the table() command. No thanks at all for */ 443 /* omitting the man page for it from OSF/1 1.2, and failing */ 444 /* to document SWAPINFO in the 1.3 man page. Lets hear it for */ 445 /* include files. */ 446 i=0; 447 while(table(TBL_SWAPINFO,i,&swbuf,1,sizeof(struct tbl_swapinfo))>0) { 448 swap_pages += swbuf.size; 449 swap_free += swbuf.free; 450 i++; 451 } 452 memory_stats[0] = pagetok(vmstats.active_count); 453 memory_stats[1] = pagetok(vmstats.inactive_count); 454 memory_stats[2] = pagetok((vmstats.free_count + vmstats.active_count + 455 vmstats.inactive_count + vmstats.wire_count)); 456 memory_stats[3] = pagetok(vmstats.free_count); 457 swap_stats[0] = pagetok(swap_pages - swap_free); 458 swap_stats[1] = pagetok(swap_pages); 459 si->memory = memory_stats; 460 si->swap = swap_stats; 461 } 462 463 static struct handle handle; 464 465 caddr_t get_process_info(si, sel, compare_index) 466 struct system_info *si; 467 struct process_select *sel; 468 int compare_index; 469 { 470 register int i; 471 register int total_procs; 472 register int active_procs; 473 register struct osf1_top_proc **prefp; 474 register struct osf1_top_proc *pp; 475 struct tbl_procinfo p_i[8]; 476 int j,k,r; 477 478 /* these are copied out of sel for speed */ 479 int show_idle; 480 int show_uid; 481 int show_command; 482 483 /* get a pointer to the states summary array */ 484 si->procstates = process_states; 485 486 /* set up flags which define what we are going to select */ 487 show_idle = sel->idle; 488 show_uid = sel->uid != -1; 489 show_command = sel->command != NULL; 490 491 /* count up process states and get pointers to interesting procs */ 492 total_procs = 0; 493 active_procs = 0; 494 memset((char *)process_states, 0, sizeof(process_states)); 495 prefp = pref; 496 pp=pbase; 497 for (j=0; j<nproc; j += 8) 498 { 499 r = table(TBL_PROCINFO, j, (struct tbl_procinfo *)p_i, 8, 500 sizeof(struct tbl_procinfo)); 501 for (k=0; k < r; k++ , pp++) 502 { 503 if(p_i[k].pi_pid == 0) 504 { 505 pp->p_pid = 0; 506 } 507 else 508 { 509 pp->p_pid = p_i[k].pi_pid; 510 pp->p_ruid = p_i[k].pi_ruid; 511 pp->p_flag = p_i[k].pi_flag; 512 pp->p_nice = getpriority(PRIO_PROCESS,p_i[k].pi_pid); 513 /* Load useful values into the proc structure */ 514 do_threads_calculations(pp); 515 /* 516 * Place pointers to each valid proc structure in pref[]. 517 * Process slots that are actually in use have a non-zero 518 * status field. 519 */ 520 #ifdef DEBUG 521 /* 522 * Emit debug info about all processes before selection. 523 */ 524 fprintf(stderr, "pid = %d ruid = %d comm = %s p_mach_state = %d p_stat = %d p_flag = 0x%x\n", 525 pp->p_pid, pp->p_ruid, p_i[k].pi_comm, 526 pp->p_mach_state, p_i[k].pi_status, pp->p_flag); 527 #endif 528 if (pp->p_mach_state != 0) 529 { 530 total_procs++; 531 process_states[pp->p_mach_state]++; 532 if ((pp->p_mach_state != 8) && 533 (show_idle || (pp->p_mach_pct_cpu != 0) || 534 (pp->p_mach_state == 1)) && 535 (!show_uid || pp->p_ruid == (uid_t)sel->uid)) { 536 *prefp++ = pp; 537 active_procs++; 538 } 539 } 540 } 541 } 542 } 543 544 /* if requested, sort the "interesting" processes */ 545 if (proc_compares[compare_index] != NULL) 546 { 547 qsort((char *)pref, active_procs, sizeof(struct osf1_top_proc *), 548 proc_compares[compare_index]); 549 } 550 551 /* remember active and total counts */ 552 si->p_total = total_procs; 553 si->p_active = pref_len = active_procs; 554 555 /* pass back a handle */ 556 handle.next_proc = pref; 557 handle.remaining = active_procs; 558 return((caddr_t)&handle); 559 } 560 561 char fmt[MAX_COLS]; /* static area where result is built */ 562 563 char *format_next_process(handle, get_userid) 564 caddr_t handle; 565 char *(*get_userid)(); 566 { 567 register struct osf1_top_proc *pp; 568 register long cputime; 569 register double pct; 570 struct user u; 571 struct handle *hp; 572 573 /* find and remember the next proc structure */ 574 hp = (struct handle *)handle; 575 pp = *(hp->next_proc++); 576 hp->remaining--; 577 578 /* get the process's user struct and set cputime */ 579 580 if (table(TBL_UAREA,pp->p_pid,&u,1,sizeof(struct user))<0) { 581 /* whoops, it must have died between the read of the proc area 582 * and now. Oh well, lets just dump some meaningless thing out 583 * to keep the rest of the program happy 584 */ 585 sprintf(fmt, 586 Proc_format, 587 pp->p_pid, 588 (*get_userid)(pp->p_ruid), 589 0, 590 0, 591 "", 592 "", 593 "dead", 594 "", 595 0.0, 596 "<dead>"); 597 return(fmt); 598 } 599 600 /* set u_comm for system processes */ 601 if (u.u_comm[0] == '\0') 602 { 603 if (pp->p_pid == 0) 604 { 605 (void) strcpy(u.u_comm, "[idle]"); 606 } 607 else if (pp->p_pid == 2) 608 { 609 (void) strcpy(u.u_comm, "[execpt.hndlr]"); 610 } 611 } 612 613 /* Check if process is in core */ 614 if (!(pp->p_flag & SLOAD)) { 615 /* 616 * Print swapped processes as <pname> 617 */ 618 char buf[sizeof(u.u_comm)]; 619 (void) strncpy(buf, u.u_comm, sizeof(u.u_comm)); 620 u.u_comm[0] = '<'; 621 (void) strncpy(&u.u_comm[1], buf, sizeof(u.u_comm) - 2); 622 u.u_comm[sizeof(u.u_comm) - 2] = '\0'; 623 (void) strncat(u.u_comm, ">", sizeof(u.u_comm) - 1); 624 u.u_comm[sizeof(u.u_comm) - 1] = '\0'; 625 } 626 627 cputime = u.u_ru.ru_utime.tv_sec + u.u_ru.ru_stime.tv_sec; 628 629 /* calculate the base for cpu percentages */ 630 pct = pctdouble(pp->p_mach_pct_cpu); 631 632 /* format this entry */ 633 sprintf(fmt, 634 Proc_format, 635 pp->p_pid, 636 (*get_userid)(pp->p_ruid), 637 pp->p_pri, 638 pp->p_nice, 639 format_k(pp->p_mach_virt_size/1024), 640 format_k(pp->p_rssize/1000), 641 state_abbrev[pp->p_mach_state], 642 format_time(cputime), 643 100.0 * ((double)pp->p_mach_pct_cpu / 10000.0), 644 printable(u.u_comm)); 645 646 /* return the result */ 647 return(fmt); 648 } 649 650 /* 651 * getkval(offset, ptr, size, refstr) - get a value out of the kernel. 652 * "offset" is the byte offset into the kernel for the desired value, 653 * "ptr" points to a buffer into which the value is retrieved, 654 * "size" is the size of the buffer (and the object to retrieve), 655 * "refstr" is a reference string used when printing error meessages, 656 * if "refstr" starts with a '!', then a failure on read will not 657 * be fatal (this may seem like a silly way to do things, but I 658 * really didn't want the overhead of another argument). 659 * 660 */ 661 662 getkval(offset, ptr, size, refstr) 663 664 unsigned long offset; 665 int *ptr; 666 int size; 667 char *refstr; 668 669 { 670 if (lseek(kmem, (long)offset, L_SET) == -1) { 671 if (*refstr == '!') 672 refstr++; 673 (void) fprintf(stderr, "%s: lseek to %s: %s\n", KMEM, 674 refstr, strerror(errno)); 675 quit(23); 676 } 677 if (read(kmem, (char *) ptr, size) == -1) { 678 if (*refstr == '!') 679 return(0); 680 else { 681 (void) fprintf(stderr, "%s: reading %s: %s\n", KMEM, 682 refstr, strerror(errno)); 683 quit(23); 684 } 685 } 686 return(1); 687 } 688 689 /* comparison routines for qsort */ 690 691 /* 692 * There are currently four possible comparison routines. main selects 693 * one of these by indexing in to the array proc_compares. 694 * 695 * Possible keys are defined as macros below. Currently these keys are 696 * defined: percent cpu, cpu ticks, process state, resident set size, 697 * total virtual memory usage. The process states are ordered as follows 698 * (from least to most important): WAIT, zomb, ???, halt, idle, sleep, 699 * stop, run. The array declaration below maps a process state index into 700 * a number that reflects this ordering. 701 */ 702 703 /* First, the possible comparison keys. These are defined in such a way 704 that they can be merely listed in the source code to define the actual 705 desired ordering. 706 */ 707 708 #define ORDERKEY_PCTCPU if (lresult = p2->p_mach_pct_cpu - p1->p_mach_pct_cpu,\ 709 (result = lresult > 0 ? 1 : lresult < 0 ? -1 : 0) == 0) 710 #define ORDERKEY_CPTICKS if ((result = p2->used_ticks - p1->used_ticks) == 0) 711 #define ORDERKEY_STATE if ((result = sorted_state[p2->p_mach_state] - \ 712 sorted_state[p1->p_mach_state]) == 0) 713 #define ORDERKEY_PRIO if ((result = p2->p_pri - p1->p_pri) == 0) 714 #define ORDERKEY_RSSIZE if ((result = p2->p_rssize - p1->p_rssize) == 0) 715 #define ORDERKEY_MEM if ((result = p2->p_mach_virt_size - p1->p_mach_virt_size) == 0) 716 717 /* Now the array that maps process state to a weight */ 718 719 static unsigned char sorted_state[] = 720 { 721 0, /*""*/ 722 8, /*"run"*/ 723 1, /*"WAIT"*/ 724 6, /*"sleep"*/ 725 5, /*"idle"*/ 726 7, /*"stop"*/ 727 4, /*"halt"*/ 728 3, /*"???"*/ 729 2, /*"zomb"*/ 730 }; 731 732 /* compare_cpu - the comparison function for sorting by cpu percentage */ 733 734 compare_cpu(pp1, pp2) 735 736 struct osf1_top_proc **pp1; 737 struct osf1_top_proc **pp2; 738 739 { 740 register struct osf1_top_proc *p1; 741 register struct osf1_top_proc *p2; 742 register long result; 743 register pctcpu lresult; 744 745 /* remove one level of indirection */ 746 p1 = *pp1; 747 p2 = *pp2; 748 749 ORDERKEY_PCTCPU 750 ORDERKEY_CPTICKS 751 ORDERKEY_STATE 752 ORDERKEY_PRIO 753 ORDERKEY_RSSIZE 754 ORDERKEY_MEM 755 ; 756 757 return(result); 758 } 759 760 /* compare_size - the comparison function for sorting by total memory usage */ 761 762 compare_size(pp1, pp2) 763 764 struct osf1_top_proc **pp1; 765 struct osf1_top_proc **pp2; 766 767 { 768 register struct osf1_top_proc *p1; 769 register struct osf1_top_proc *p2; 770 register long result; 771 register pctcpu lresult; 772 773 /* remove one level of indirection */ 774 p1 = *pp1; 775 p2 = *pp2; 776 777 ORDERKEY_MEM 778 ORDERKEY_RSSIZE 779 ORDERKEY_PCTCPU 780 ORDERKEY_CPTICKS 781 ORDERKEY_STATE 782 ORDERKEY_PRIO 783 ; 784 785 return(result); 786 } 787 788 /* compare_res - the comparison function for sorting by resident set size */ 789 790 compare_res(pp1, pp2) 791 792 struct osf1_top_proc **pp1; 793 struct osf1_top_proc **pp2; 794 795 { 796 register struct osf1_top_proc *p1; 797 register struct osf1_top_proc *p2; 798 register long result; 799 register pctcpu lresult; 800 801 /* remove one level of indirection */ 802 p1 = *pp1; 803 p2 = *pp2; 804 805 ORDERKEY_RSSIZE 806 ORDERKEY_MEM 807 ORDERKEY_PCTCPU 808 ORDERKEY_CPTICKS 809 ORDERKEY_STATE 810 ORDERKEY_PRIO 811 ; 812 813 return(result); 814 } 815 816 /* compare_time - the comparison function for sorting by total cpu time */ 817 818 compare_time(pp1, pp2) 819 820 struct osf1_top_proc **pp1; 821 struct osf1_top_proc **pp2; 822 823 { 824 register struct osf1_top_proc *p1; 825 register struct osf1_top_proc *p2; 826 register long result; 827 register pctcpu lresult; 828 829 /* remove one level of indirection */ 830 p1 = *pp1; 831 p2 = *pp2; 832 833 ORDERKEY_CPTICKS 834 ORDERKEY_PCTCPU 835 ORDERKEY_STATE 836 ORDERKEY_PRIO 837 ORDERKEY_RSSIZE 838 ORDERKEY_MEM 839 ; 840 841 return(result); 842 } 843 844 /* 845 * proc_owner(pid) - returns the uid that owns process "pid", or -1 if 846 * the process does not exist. 847 * It is EXTREMLY IMPORTANT that this function work correctly. 848 * If top runs setuid root (as in SVR4), then this function 849 * is the only thing that stands in the way of a serious 850 * security problem. It validates requests for the "kill" 851 * and "renice" commands. 852 */ 853 854 int proc_owner(pid) 855 856 int pid; 857 858 { 859 register int cnt; 860 register struct osf1_top_proc **prefp; 861 register struct osf1_top_proc *pp; 862 863 prefp = pref; 864 cnt = pref_len; 865 while (--cnt >= 0) 866 { 867 if ((pp = *prefp++)->p_pid == (pid_t)pid) 868 { 869 return((int)pp->p_ruid); 870 } 871 } 872 return(-1); 873 } 874 875 876 /* 877 * We use the Mach interface, as well as the table(UAREA,,,) call to 878 * get some more information, then put it into unused fields in our 879 * copy of the proc structure, to make it faster and easier to get at 880 * later. 881 */ 882 void do_threads_calculations(thisproc) 883 struct osf1_top_proc *thisproc; 884 { 885 int j; 886 task_t thistask; 887 task_basic_info_data_t taskinfo; 888 unsigned int taskinfo_l; 889 thread_array_t threadarr; 890 unsigned int threadarr_l; 891 thread_basic_info_t threadinfo; 892 thread_basic_info_data_t threadinfodata; 893 unsigned int threadinfo_l; 894 int task_tot_cpu=0; /* total cpu usage of threads in a task */ 895 struct user u; 896 897 thisproc->p_pri=0; 898 thisproc->p_rssize=0; 899 thisproc->p_mach_virt_size=0; 900 thisproc->p_mach_state=0; 901 thisproc->p_mach_pct_cpu=0; 902 903 if(task_by_unix_pid(task_self(), thisproc->p_pid, &thistask) 904 != KERN_SUCCESS){ 905 thisproc->p_mach_state=8; /* (zombie) */ 906 } else { 907 taskinfo_l=TASK_BASIC_INFO_COUNT; 908 if(task_info(thistask, TASK_BASIC_INFO, (task_info_t) &taskinfo, 909 &taskinfo_l) 910 != KERN_SUCCESS) { 911 thisproc->p_mach_state=8; /* (zombie) */ 912 } else { 913 int minim_state=99,mcurp=1000,mbasp=1000,mslpt=999; 914 915 thisproc->p_rssize=taskinfo.resident_size; 916 thisproc->p_mach_virt_size=taskinfo.virtual_size; 917 918 if (task_threads(thistask, &threadarr, &threadarr_l) != KERN_SUCCESS) 919 return; 920 threadinfo= &threadinfodata; 921 for(j=0; j < threadarr_l; j++) { 922 threadinfo_l=THREAD_BASIC_INFO_COUNT; 923 if(thread_info(threadarr[j],THREAD_BASIC_INFO, 924 (thread_info_t) threadinfo, &threadinfo_l) == KERN_SUCCESS) { 925 926 task_tot_cpu += threadinfo->cpu_usage; 927 if(minim_state>threadinfo->run_state) 928 minim_state=threadinfo->run_state; 929 if(mcurp>threadinfo->cur_priority) 930 mcurp=threadinfo->cur_priority; 931 if(mbasp>threadinfo->base_priority) 932 mbasp=threadinfo->base_priority; 933 if(mslpt>threadinfo->sleep_time) 934 mslpt=threadinfo->sleep_time; 935 } 936 } 937 switch (minim_state) { 938 case TH_STATE_RUNNING: 939 thisproc->p_mach_state=1; break; 940 case TH_STATE_UNINTERRUPTIBLE: 941 thisproc->p_mach_state=2; break; 942 case TH_STATE_WAITING: 943 thisproc->p_mach_state=(threadinfo->sleep_time > 20) ? 4 : 3; break; 944 case TH_STATE_STOPPED: 945 thisproc->p_mach_state=5; break; 946 case TH_STATE_HALTED: 947 thisproc->p_mach_state=6; break; 948 default: 949 thisproc->p_mach_state=7; break; 950 } 951 952 thisproc->p_pri=mcurp; 953 thisproc->p_mach_pct_cpu=(fixpt_t)(task_tot_cpu*10); 954 vm_deallocate(task_self(),(vm_address_t)threadarr,threadarr_l); 955 } 956 } 957 if (table(TBL_UAREA,thisproc->p_pid,&u,1,sizeof(struct user))>=0) { 958 thisproc->used_ticks=(u.u_ru.ru_utime.tv_sec + u.u_ru.ru_stime.tv_sec); 959 thisproc->process_size=u.u_tsize + u.u_dsize + u.u_ssize; 960 } 961 } 962 963 /* The reason for this function is that the system call will let 964 * someone lower their own processes priority (because top is setuid :-( 965 * Yes, using syscall() is a hack, if you can come up with something 966 * better, then I'd be thrilled to hear it. I'm not holding my breath, 967 * though. 968 * Anthony. 969 */ 970 int setpriority(int dummy, int procnum, int niceval) 971 { 972 973 int uid, curprio; 974 975 uid=getuid(); 976 if ( (curprio=getpriority(PRIO_PROCESS,procnum) ) == -1) 977 { 978 return(-1); /* errno goes back to renice_process() */ 979 } 980 /* check for not-root - if so, dont allow users to decrease priority */ 981 else if ( uid && (niceval<curprio) ) 982 { 983 errno=EACCES; 984 return(-1); 985 } 986 return(syscall(SYS_setpriority,PRIO_PROCESS,procnum,niceval)); 987 } 988 989