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: 2.x with thread eliding 37 * 38 * DESCRIPTION: 39 * This is the machine-dependent module for Linux 2.x that elides threads 40 * from the output. 41 * 42 * CFLAGS: -DHAVE_GETOPT -DHAVE_STRERROR -DORDER 43 * 44 * TERMCAP: -lcurses 45 * 46 * AUTHOR: Richard Henderson <rth@tamu.edu> 47 * Order support added by Alexey Klimkin <kad@klon.tme.mcst.ru> 48 * Ported to 2.4 by William LeFebvre 49 * Thread eliding by William LeFebvre 50 */ 51 52#include "config.h" 53 54#include <sys/types.h> 55#include <stdio.h> 56#include <fcntl.h> 57#include <unistd.h> 58#include <stdlib.h> 59#include <errno.h> 60#include <dirent.h> 61#include <string.h> 62#include <math.h> 63#include <ctype.h> 64#include <sys/time.h> 65#include <sys/stat.h> 66#include <sys/vfs.h> 67 68#include <sys/param.h> /* for HZ */ 69#include <asm/page.h> /* for PAGE_SHIFT */ 70 71#if 0 72#include <linux/proc_fs.h> /* for PROC_SUPER_MAGIC */ 73#else 74#define PROC_SUPER_MAGIC 0x9fa0 75#endif 76 77#include "top.h" 78#include "machine.h" 79#include "utils.h" 80 81#define PROCFS "/proc" 82extern char *myname; 83 84/*=PROCESS INFORMATION==================================================*/ 85 86struct top_proc 87{ 88 pid_t pid; 89 pid_t ppid; 90 uid_t uid; 91 char *name; 92 int pri, nice; 93 unsigned long size, rss; /* in k */ 94 int state; 95 unsigned long time; 96 unsigned long start_time; 97 unsigned long otime; 98 unsigned long start_code; 99 unsigned long end_code; 100 unsigned long start_stack; 101 unsigned int threads; 102 double pcpu, wcpu; 103 struct top_proc *next; 104}; 105 106 107/*=STATE IDENT STRINGS==================================================*/ 108 109#define NPROCSTATES 7 110static char *state_abbrev[NPROCSTATES+1] = 111{ 112 "", "run", "sleep", "disk", "zomb", "stop", "swap", 113 NULL 114}; 115 116static char *procstatenames[NPROCSTATES+1] = 117{ 118 "", " running, ", " sleeping, ", " uninterruptable, ", 119 " zombie, ", " stopped, ", " swapping, ", 120 NULL 121}; 122 123#define NCPUSTATES 4 124static char *cpustatenames[NCPUSTATES+1] = 125{ 126 "user", "nice", "system", "idle", 127 NULL 128}; 129 130#define MEMUSED 0 131#define MEMFREE 1 132#define MEMSHARED 2 133#define MEMBUFFERS 3 134#define MEMCACHED 4 135#define NMEMSTATS 5 136static char *memorynames[NMEMSTATS+1] = 137{ 138 "K used, ", "K free, ", "K shared, ", "K buffers, ", "K cached", 139 NULL 140}; 141 142#define SWAPUSED 0 143#define SWAPFREE 1 144#define SWAPCACHED 2 145#define NSWAPSTATS 3 146static char *swapnames[NSWAPSTATS+1] = 147{ 148 "K used, ", "K free, ", "K cached", 149 NULL 150}; 151 152static char fmt_header[] = 153" PID X THR PRI NICE SIZE RES STATE TIME CPU COMMAND"; 154 155/* these are names given to allowed sorting orders -- first is default */ 156char *ordernames[] = 157{"cpu", "size", "res", "time", "command", NULL}; 158 159/* forward definitions for comparison functions */ 160int compare_cpu(); 161int compare_size(); 162int compare_res(); 163int compare_time(); 164int compare_cmd(); 165 166int (*proc_compares[])() = { 167 compare_cpu, 168 compare_size, 169 compare_res, 170 compare_time, 171 compare_cmd, 172 NULL }; 173 174/*=SYSTEM STATE INFO====================================================*/ 175 176/* these are for calculating cpu state percentages */ 177 178static long cp_time[NCPUSTATES]; 179static long cp_old[NCPUSTATES]; 180static long cp_diff[NCPUSTATES]; 181 182/* for calculating the exponential average */ 183 184static struct timeval lasttime; 185 186/* these are for keeping track of processes */ 187 188#define HASH_SIZE (1003) 189#define INITIAL_ACTIVE_SIZE (256) 190#define PROCBLOCK_SIZE (32) 191static struct top_proc *ptable[HASH_SIZE]; 192static struct top_proc **pactive; 193static struct top_proc **nextactive; 194static unsigned int activesize = 0; 195static time_t boottime = -1; 196 197/* these are for passing data back to the machine independant portion */ 198 199static int cpu_states[NCPUSTATES]; 200static int process_states[NPROCSTATES]; 201static long memory_stats[NMEMSTATS]; 202static long swap_stats[NSWAPSTATS]; 203 204/* usefull macros */ 205#define bytetok(x) (((x) + 512) >> 10) 206#define pagetok(x) ((x) << (PAGE_SHIFT - 10)) 207#define HASH(x) (((x) * 1686629713U) % HASH_SIZE) 208 209/*======================================================================*/ 210 211static inline char * 212skip_ws(const char *p) 213{ 214 while (isspace(*p)) p++; 215 return (char *)p; 216} 217 218static inline char * 219skip_token(const char *p) 220{ 221 while (isspace(*p)) p++; 222 while (*p && !isspace(*p)) p++; 223 return (char *)p; 224} 225 226static void 227xfrm_cmdline(char *p, int len) 228{ 229 while (--len > 0) 230 { 231 if (*p == '\0') 232 { 233 *p = ' '; 234 } 235 p++; 236 } 237} 238 239static void 240update_procname(struct top_proc *proc, char *cmd) 241 242{ 243 printable(cmd); 244 245 if (proc->name == NULL) 246 { 247 proc->name = strdup(cmd); 248 } 249 else if (strcmp(proc->name, cmd) != 0) 250 { 251 free(proc->name); 252 proc->name = strdup(cmd); 253 } 254} 255 256 257 258 259/* 260 * Process structures are allocated and freed as needed. Here we 261 * keep big pools of them, adding more pool as needed. When a 262 * top_proc structure is freed, it is added to a freelist and reused. 263 */ 264 265static struct top_proc *freelist = NULL; 266static struct top_proc *procblock = NULL; 267static struct top_proc *procmax = NULL; 268 269static struct top_proc * 270new_proc() 271{ 272 struct top_proc *p; 273 274 if (freelist) 275 { 276 p = freelist; 277 freelist = freelist->next; 278 } 279 else if (procblock) 280 { 281 p = procblock; 282 if (++procblock >= procmax) 283 { 284 procblock = NULL; 285 } 286 } 287 else 288 { 289 p = procblock = (struct top_proc *)calloc(PROCBLOCK_SIZE, 290 sizeof(struct top_proc)); 291 procmax = procblock++ + PROCBLOCK_SIZE; 292 } 293 294 /* initialization */ 295 if (p->name != NULL) 296 { 297 free(p->name); 298 p->name = NULL; 299 } 300 301 return p; 302} 303 304static void 305free_proc(struct top_proc *proc) 306{ 307 proc->next = freelist; 308 freelist = proc; 309} 310 311 312int 313machine_init(struct statics *statics) 314 315{ 316 /* make sure the proc filesystem is mounted */ 317 { 318 struct statfs sb; 319 if (statfs(PROCFS, &sb) < 0 || sb.f_type != PROC_SUPER_MAGIC) 320 { 321 fprintf(stderr, "%s: proc filesystem not mounted on " PROCFS "\n", 322 myname); 323 return -1; 324 } 325 } 326 327 /* chdir to the proc filesystem to make things easier */ 328 chdir(PROCFS); 329 330 /* get a boottime */ 331 { 332 int fd; 333 char buff[64]; 334 char *p; 335 unsigned long uptime; 336 struct timeval tv; 337 338 if ((fd = open("uptime", 0)) != -1) 339 { 340 if (read(fd, buff, sizeof(buff)) > 0) 341 { 342 uptime = strtoul(buff, &p, 10); 343 gettimeofday(&tv, 0); 344 boottime = tv.tv_sec - uptime; 345 } 346 close(fd); 347 } 348 } 349 350 /* fill in the statics information */ 351 statics->procstate_names = procstatenames; 352 statics->cpustate_names = cpustatenames; 353 statics->memory_names = memorynames; 354 statics->swap_names = swapnames; 355 statics->order_names = ordernames; 356 statics->boottime = boottime; 357 statics->flags.fullcmds = 1; 358 statics->flags.warmup = 1; 359 360 /* allocate needed space */ 361 pactive = (struct top_proc **)malloc(sizeof(struct top_proc *) * INITIAL_ACTIVE_SIZE); 362 activesize = INITIAL_ACTIVE_SIZE; 363 364 /* make sure the hash table is empty */ 365 memset(ptable, 0, HASH_SIZE * sizeof(struct top_proc *)); 366 367 /* all done! */ 368 return 0; 369} 370 371 372void 373get_system_info(struct system_info *info) 374 375{ 376 char buffer[4096+1]; 377 int fd, len; 378 char *p; 379 380 /* get load averages */ 381 382 if ((fd = open("loadavg", O_RDONLY)) != -1) 383 { 384 if ((len = read(fd, buffer, sizeof(buffer)-1)) > 0) 385 { 386 buffer[len] = '\0'; 387 388 info->load_avg[0] = strtod(buffer, &p); 389 info->load_avg[1] = strtod(p, &p); 390 info->load_avg[2] = strtod(p, &p); 391 p = skip_token(p); /* skip running/tasks */ 392 p = skip_ws(p); 393 if (*p) 394 { 395 info->last_pid = atoi(p); 396 } 397 else 398 { 399 info->last_pid = -1; 400 } 401 } 402 close(fd); 403 } 404 405 /* get the cpu time info */ 406 if ((fd = open("stat", O_RDONLY)) != -1) 407 { 408 if ((len = read(fd, buffer, sizeof(buffer)-1)) > 0) 409 { 410 buffer[len] = '\0'; 411 p = skip_token(buffer); /* "cpu" */ 412 cp_time[0] = strtoul(p, &p, 0); 413 cp_time[1] = strtoul(p, &p, 0); 414 cp_time[2] = strtoul(p, &p, 0); 415 cp_time[3] = strtoul(p, &p, 0); 416 417 /* convert cp_time counts to percentages */ 418 percentages(4, cpu_states, cp_time, cp_old, cp_diff); 419 } 420 close(fd); 421 } 422 423 /* get system wide memory usage */ 424 if ((fd = open("meminfo", O_RDONLY)) != -1) 425 { 426 char *p; 427 int mem = 0; 428 int swap = 0; 429 unsigned long memtotal = 0; 430 unsigned long memfree = 0; 431 unsigned long swaptotal = 0; 432 433 if ((len = read(fd, buffer, sizeof(buffer)-1)) > 0) 434 { 435 buffer[len] = '\0'; 436 p = buffer-1; 437 438 /* iterate thru the lines */ 439 while (p != NULL) 440 { 441 p++; 442 if (p[0] == ' ' || p[0] == '\t') 443 { 444 /* skip */ 445 } 446 else if (strncmp(p, "Mem:", 4) == 0) 447 { 448 p = skip_token(p); /* "Mem:" */ 449 p = skip_token(p); /* total memory */ 450 memory_stats[MEMUSED] = strtoul(p, &p, 10); 451 memory_stats[MEMFREE] = strtoul(p, &p, 10); 452 memory_stats[MEMSHARED] = strtoul(p, &p, 10); 453 memory_stats[MEMBUFFERS] = strtoul(p, &p, 10); 454 memory_stats[MEMCACHED] = strtoul(p, &p, 10); 455 memory_stats[MEMUSED] = bytetok(memory_stats[MEMUSED]); 456 memory_stats[MEMFREE] = bytetok(memory_stats[MEMFREE]); 457 memory_stats[MEMSHARED] = bytetok(memory_stats[MEMSHARED]); 458 memory_stats[MEMBUFFERS] = bytetok(memory_stats[MEMBUFFERS]); 459 memory_stats[MEMCACHED] = bytetok(memory_stats[MEMCACHED]); 460 mem = 1; 461 } 462 else if (strncmp(p, "Swap:", 5) == 0) 463 { 464 p = skip_token(p); /* "Swap:" */ 465 p = skip_token(p); /* total swap */ 466 swap_stats[SWAPUSED] = strtoul(p, &p, 10); 467 swap_stats[SWAPFREE] = strtoul(p, &p, 10); 468 swap_stats[SWAPUSED] = bytetok(swap_stats[SWAPUSED]); 469 swap_stats[SWAPFREE] = bytetok(swap_stats[SWAPFREE]); 470 swap = 1; 471 } 472 else if (!mem && strncmp(p, "MemTotal:", 9) == 0) 473 { 474 p = skip_token(p); 475 memtotal = strtoul(p, &p, 10); 476 } 477 else if (!mem && memtotal > 0 && strncmp(p, "MemFree:", 8) == 0) 478 { 479 p = skip_token(p); 480 memfree = strtoul(p, &p, 10); 481 memory_stats[MEMUSED] = memtotal - memfree; 482 memory_stats[MEMFREE] = memfree; 483 } 484 else if (!mem && strncmp(p, "MemShared:", 10) == 0) 485 { 486 p = skip_token(p); 487 memory_stats[MEMSHARED] = strtoul(p, &p, 10); 488 } 489 else if (!mem && strncmp(p, "Buffers:", 8) == 0) 490 { 491 p = skip_token(p); 492 memory_stats[MEMBUFFERS] = strtoul(p, &p, 10); 493 } 494 else if (!mem && strncmp(p, "Cached:", 7) == 0) 495 { 496 p = skip_token(p); 497 memory_stats[MEMCACHED] = strtoul(p, &p, 10); 498 } 499 else if (!swap && strncmp(p, "SwapTotal:", 10) == 0) 500 { 501 p = skip_token(p); 502 swaptotal = strtoul(p, &p, 10); 503 } 504 else if (!swap && swaptotal > 0 && strncmp(p, "SwapFree:", 9) == 0) 505 { 506 p = skip_token(p); 507 memfree = strtoul(p, &p, 10); 508 swap_stats[SWAPUSED] = swaptotal - memfree; 509 swap_stats[SWAPFREE] = memfree; 510 } 511 else if (!mem && strncmp(p, "SwapCached:", 11) == 0) 512 { 513 p = skip_token(p); 514 swap_stats[SWAPCACHED] = strtoul(p, &p, 10); 515 } 516 517 /* move to the next line */ 518 p = strchr(p, '\n'); 519 } 520 } 521 close(fd); 522 } 523 524 /* set arrays and strings */ 525 info->cpustates = cpu_states; 526 info->memory = memory_stats; 527 info->swap = swap_stats; 528} 529 530 531static void 532read_one_proc_stat(pid_t pid, struct top_proc *proc, struct process_select *sel) 533{ 534 char buffer[4096], *p, *q; 535 int fd, len; 536 int fullcmd; 537 538 /* if anything goes wrong, we return with proc->state == 0 */ 539 proc->state = 0; 540 541 /* full cmd handling */ 542 fullcmd = sel->fullcmd; 543 if (fullcmd) 544 { 545 sprintf(buffer, "%d/cmdline", pid); 546 if ((fd = open(buffer, O_RDONLY)) != -1) 547 { 548 /* read command line data */ 549 /* (theres no sense in reading more than we can fit) */ 550 if ((len = read(fd, buffer, MAX_COLS)) > 1) 551 { 552 buffer[len] = '\0'; 553 xfrm_cmdline(buffer, len); 554 update_procname(proc, buffer); 555 } 556 else 557 { 558 fullcmd = 0; 559 } 560 close(fd); 561 } 562 else 563 { 564 fullcmd = 0; 565 } 566 } 567 568 /* grab the proc stat info in one go */ 569 sprintf(buffer, "%d/stat", pid); 570 571 fd = open(buffer, O_RDONLY); 572 len = read(fd, buffer, sizeof(buffer)-1); 573 close(fd); 574 575 buffer[len] = '\0'; 576 577 proc->uid = (uid_t)proc_owner((int)pid); 578 579 /* parse out the status */ 580 581 /* skip pid and locate command, which is in parentheses */ 582 if ((p = strchr(buffer, '(')) == NULL) 583 { 584 return; 585 } 586 if ((q = strrchr(++p, ')')) == NULL) 587 { 588 return; 589 } 590 591 /* set the procname */ 592 *q = '\0'; 593 if (!fullcmd) 594 { 595 update_procname(proc, p); 596 } 597 598 /* scan the rest of the line */ 599 p = q+1; 600 p = skip_ws(p); 601 switch (*p++) /* state */ 602 { 603 case 'R': proc->state = 1; break; 604 case 'S': proc->state = 2; break; 605 case 'D': proc->state = 3; break; 606 case 'Z': proc->state = 4; break; 607 case 'T': proc->state = 5; break; 608 case 'W': proc->state = 6; break; 609 case '\0': return; 610 } 611 612 proc->ppid = strtoul(p, &p, 10); /* ppid */ 613 p = skip_token(p); /* skip pgrp */ 614 p = skip_token(p); /* skip session */ 615 p = skip_token(p); /* skip tty */ 616 p = skip_token(p); /* skip tty pgrp */ 617 p = skip_token(p); /* skip flags */ 618 p = skip_token(p); /* skip min flt */ 619 p = skip_token(p); /* skip cmin flt */ 620 p = skip_token(p); /* skip maj flt */ 621 p = skip_token(p); /* skip cmaj flt */ 622 623 proc->time = strtoul(p, &p, 10); /* utime */ 624 proc->time += strtoul(p, &p, 10); /* stime */ 625 626 p = skip_token(p); /* skip cutime */ 627 p = skip_token(p); /* skip cstime */ 628 629 proc->pri = strtol(p, &p, 10); /* priority */ 630 proc->nice = strtol(p, &p, 10); /* nice */ 631 632 p = skip_token(p); /* skip timeout */ 633 p = skip_token(p); /* skip it_real_val */ 634 proc->start_time = strtoul(p, &p, 10); /* start_time */ 635 636 proc->size = bytetok(strtoul(p, &p, 10)); /* vsize */ 637 proc->rss = pagetok(strtoul(p, &p, 10)); /* rss */ 638 639 p = skip_token(p); /* skip rlim */ 640 proc->start_code = strtoul(p, &p, 10); /* start_code */ 641 proc->end_code = strtoul(p, &p, 10); /* end_code */ 642 proc->start_stack = strtoul(p, &p, 10); /* start_stack */ 643 644 /* for the record, here are the rest of the fields */ 645#if 0 646 p = skip_token(p); /* skip sp */ 647 p = skip_token(p); /* skip pc */ 648 p = skip_token(p); /* skip signal */ 649 p = skip_token(p); /* skip sigblocked */ 650 p = skip_token(p); /* skip sigignore */ 651 p = skip_token(p); /* skip sigcatch */ 652 p = skip_token(p); /* skip wchan */ 653#endif 654} 655 656 657caddr_t 658get_process_info(struct system_info *si, 659 struct process_select *sel, 660 int compare_index) 661{ 662 struct timeval thistime; 663 double timediff, alpha, beta; 664 struct top_proc *proc; 665 pid_t pid; 666 unsigned long now; 667 unsigned long elapsed; 668 int i; 669 670 /* calculate the time difference since our last check */ 671 gettimeofday(&thistime, 0); 672 if (lasttime.tv_sec) 673 { 674 timediff = ((thistime.tv_sec - lasttime.tv_sec) + 675 (thistime.tv_usec - lasttime.tv_usec) * 1e-6); 676 } 677 else 678 { 679 timediff = 0; 680 } 681 lasttime = thistime; 682 683 /* round current time to a second */ 684 now = (unsigned long)thistime.tv_sec; 685 if (thistime.tv_usec >= 500000) 686 { 687 now++; 688 } 689 690 /* calculate constants for the exponental average */ 691 if (timediff > 0.0 && timediff < 30.0) 692 { 693 alpha = 0.5 * (timediff / 30.0); 694 beta = 1.0 - alpha; 695 } 696 else 697 alpha = beta = 0.5; 698 timediff *= HZ; /* convert to ticks */ 699 700 /* mark all hash table entries as not seen */ 701 for (i = 0; i < HASH_SIZE; ++i) 702 { 703 for (proc = ptable[i]; proc; proc = proc->next) 704 { 705 proc->state = 0; 706 } 707 } 708 709 /* read the process information */ 710 { 711 DIR *dir = opendir("."); 712 struct dirent *ent; 713 int total_procs = 0; 714 struct top_proc **active; 715 716 int show_idle = sel->idle; 717 int show_uid = sel->uid != -1; 718 719 memset(process_states, 0, sizeof(process_states)); 720 721 while ((ent = readdir(dir)) != NULL) 722 { 723 struct top_proc *pp; 724 725 if (!isdigit(ent->d_name[0])) 726 continue; 727 728 pid = atoi(ent->d_name); 729 730 /* look up hash table entry */ 731 proc = pp = ptable[HASH(pid)]; 732 while (proc && proc->pid != pid) 733 { 734 proc = proc->next; 735 } 736 737 /* if we came up empty, create a new entry */ 738 if (proc == NULL) 739 { 740 proc = new_proc(); 741 proc->pid = pid; 742 proc->next = pp; 743 ptable[HASH(pid)] = proc; 744 proc->time = proc->otime = 0; 745 } 746 747 read_one_proc_stat(pid, proc, sel); 748 proc->threads = 1; 749 750 if (proc->state == 0) 751 continue; 752 753 total_procs++; 754 process_states[proc->state]++; 755 756 /* calculate cpu percentage */ 757 if (timediff > 0.0) 758 { 759 if ((proc->pcpu = (proc->time - proc->otime) / timediff) < 0.0001) 760 { 761 proc->pcpu = 0; 762 } 763 } 764 else if ((elapsed = (now - boottime)*HZ - proc->start_time) > 0) 765 { 766 if ((proc->pcpu = (double)proc->time / (double)elapsed) < 0.0001) 767 { 768 proc->pcpu; 769 } 770 } 771 else 772 { 773 proc->pcpu = 0.0; 774 } 775 776 /* remember time for next time */ 777 proc->otime = proc->time; 778 } 779 closedir(dir); 780 781 /* make sure we have enough slots for the active procs */ 782 if (activesize < total_procs) 783 { 784 pactive = (struct top_proc **)realloc(pactive, 785 sizeof(struct top_proc *) * total_procs); 786 activesize = total_procs; 787 } 788 789 /* set up the active procs and flush dead entries */ 790 /* also coalesce threads */ 791 active = pactive; 792 for (i = 0; i < HASH_SIZE; i++) 793 { 794 struct top_proc *last; 795 struct top_proc *ptmp; 796 struct top_proc *parent; 797 798 last = NULL; 799 proc = ptable[i]; 800 while (proc != NULL) 801 { 802 if (proc->state == 0) 803 { 804 ptmp = proc; 805 if (last) 806 { 807 proc = last->next = proc->next; 808 } 809 else 810 { 811 proc = ptable[i] = proc->next; 812 } 813 free_proc(ptmp); 814 } 815 else 816 { 817 /* look up hash table entry for parent */ 818 parent = proc; 819 do { 820 pid = parent->ppid; 821 parent = ptable[HASH(pid)]; 822 while (parent && parent->pid != pid) 823 { 824 parent = parent->next; 825 } 826 } while (parent && parent->state == 0); 827 828 /* does this look like a thread of its parent? */ 829 if (parent && proc->size == parent->size && 830 proc->rss == parent->rss && 831 proc->start_code == parent->start_code && 832 proc->end_code == parent->end_code && 833 proc->start_stack == parent->start_stack) 834 { 835 /* yes it does: roll up the cumulative numbers */ 836 parent->threads += proc->threads; 837 parent->time += proc->time; 838 parent->pcpu += proc->pcpu; 839 840 /* mark this process as dead (undisplayable) */ 841 proc->state = 0; 842 } 843 else if ((show_idle || proc->state == 1 || proc->pcpu) && 844 (!show_uid || proc->uid == sel->uid)) 845 { 846 *active++ = proc; 847 last = proc; 848 } 849 proc = proc->next; 850 } 851 } 852 } 853 854 si->p_active = active - pactive; 855 si->p_total = total_procs; 856 si->procstates = process_states; 857 } 858 859 /* if requested, sort the "active" procs */ 860 if (si->p_active) 861 qsort(pactive, si->p_active, sizeof(struct top_proc *), 862 proc_compares[compare_index]); 863 864 /* don't even pretend that the return value thing here isn't bogus */ 865 nextactive = pactive; 866 return (caddr_t)0; 867} 868 869 870char * 871format_header(char *uname_field) 872 873{ 874 int uname_len = strlen(uname_field); 875 if (uname_len > 8) 876 uname_len = 8; 877 878 memcpy(strchr(fmt_header, 'X'), uname_field, uname_len); 879 880 return fmt_header; 881} 882 883 884char * 885format_next_process(caddr_t handle, char *(*get_userid)(int)) 886 887{ 888 static char fmt[MAX_COLS]; /* static area where result is built */ 889 struct top_proc *p = *nextactive++; 890 891 snprintf(fmt, sizeof(fmt), 892 "%5d %-8.8s %3d %3d %4d %5s %5s %-5s %6s %5.2f%% %s", 893 p->pid, 894 (*get_userid)(p->uid), 895 p->threads, 896 p->pri < -99 ? -99 : p->pri, 897 p->nice, 898 format_k(p->size), 899 format_k(p->rss), 900 state_abbrev[p->state], 901 format_time(p->time / HZ), 902 p->pcpu * 100.0, 903 p->name); 904 905 /* return the result */ 906 return (fmt); 907} 908 909/* comparison routines for qsort */ 910 911/* 912 * There are currently four possible comparison routines. main selects 913 * one of these by indexing in to the array proc_compares. 914 * 915 * Possible keys are defined as macros below. Currently these keys are 916 * defined: percent cpu, cpu ticks, process state, resident set size, 917 * total virtual memory usage. The process states are ordered as follows 918 * (from least to most important): WAIT, zombie, sleep, stop, start, run. 919 * The array declaration below maps a process state index into a number 920 * that reflects this ordering. 921 */ 922 923/* First, the possible comparison keys. These are defined in such a way 924 that they can be merely listed in the source code to define the actual 925 desired ordering. 926 */ 927 928#define ORDERKEY_PCTCPU if (dresult = p2->pcpu - p1->pcpu,\ 929 (result = dresult > 0.0 ? 1 : dresult < 0.0 ? -1 : 0) == 0) 930#define ORDERKEY_CPTICKS if ((result = (long)p2->time - (long)p1->time) == 0) 931#define ORDERKEY_STATE if ((result = (sort_state[p2->state] - \ 932 sort_state[p1->state])) == 0) 933#define ORDERKEY_PRIO if ((result = p2->pri - p1->pri) == 0) 934#define ORDERKEY_RSSIZE if ((result = p2->rss - p1->rss) == 0) 935#define ORDERKEY_MEM if ((result = p2->size - p1->size) == 0) 936#define ORDERKEY_NAME if ((result = strcmp(p1->name, p2->name)) == 0) 937 938/* Now the array that maps process state to a weight */ 939 940unsigned char sort_state[] = 941{ 942 0, /* empty */ 943 6, /* run */ 944 3, /* sleep */ 945 5, /* disk wait */ 946 1, /* zombie */ 947 2, /* stop */ 948 4 /* swap */ 949}; 950 951 952/* compare_cpu - the comparison function for sorting by cpu percentage */ 953 954int 955compare_cpu ( 956 struct top_proc **pp1, 957 struct top_proc **pp2) 958 { 959 register struct top_proc *p1; 960 register struct top_proc *p2; 961 register long result; 962 double dresult; 963 964 /* remove one level of indirection */ 965 p1 = *pp1; 966 p2 = *pp2; 967 968 ORDERKEY_PCTCPU 969 ORDERKEY_CPTICKS 970 ORDERKEY_STATE 971 ORDERKEY_PRIO 972 ORDERKEY_RSSIZE 973 ORDERKEY_MEM 974 ; 975 976 return result == 0 ? 0 : result < 0 ? -1 : 1; 977 } 978 979/* compare_size - the comparison function for sorting by total memory usage */ 980 981int 982compare_size ( 983 struct top_proc **pp1, 984 struct top_proc **pp2) 985 { 986 register struct top_proc *p1; 987 register struct top_proc *p2; 988 register long result; 989 double dresult; 990 991 /* remove one level of indirection */ 992 p1 = *pp1; 993 p2 = *pp2; 994 995 ORDERKEY_MEM 996 ORDERKEY_RSSIZE 997 ORDERKEY_PCTCPU 998 ORDERKEY_CPTICKS 999 ORDERKEY_STATE 1000 ORDERKEY_PRIO 1001 ; 1002 1003 return result == 0 ? 0 : result < 0 ? -1 : 1; 1004 } 1005 1006/* compare_res - the comparison function for sorting by resident set size */ 1007 1008int 1009compare_res ( 1010 struct top_proc **pp1, 1011 struct top_proc **pp2) 1012 { 1013 register struct top_proc *p1; 1014 register struct top_proc *p2; 1015 register long result; 1016 double dresult; 1017 1018 /* remove one level of indirection */ 1019 p1 = *pp1; 1020 p2 = *pp2; 1021 1022 ORDERKEY_RSSIZE 1023 ORDERKEY_MEM 1024 ORDERKEY_PCTCPU 1025 ORDERKEY_CPTICKS 1026 ORDERKEY_STATE 1027 ORDERKEY_PRIO 1028 ; 1029 1030 return result == 0 ? 0 : result < 0 ? -1 : 1; 1031 } 1032 1033/* compare_time - the comparison function for sorting by total cpu time */ 1034 1035int 1036compare_time ( 1037 struct top_proc **pp1, 1038 struct top_proc **pp2) 1039 { 1040 register struct top_proc *p1; 1041 register struct top_proc *p2; 1042 register long result; 1043 double dresult; 1044 1045 /* remove one level of indirection */ 1046 p1 = *pp1; 1047 p2 = *pp2; 1048 1049 ORDERKEY_CPTICKS 1050 ORDERKEY_PCTCPU 1051 ORDERKEY_STATE 1052 ORDERKEY_PRIO 1053 ORDERKEY_MEM 1054 ORDERKEY_RSSIZE 1055 ; 1056 1057 return result == 0 ? 0 : result < 0 ? -1 : 1; 1058 } 1059 1060/* compare_cmd - the comparison function for sorting by command name */ 1061 1062int 1063compare_cmd ( 1064 struct top_proc **pp1, 1065 struct top_proc **pp2) 1066 { 1067 register struct top_proc *p1; 1068 register struct top_proc *p2; 1069 register long result; 1070 double dresult; 1071 1072 /* remove one level of indirection */ 1073 p1 = *pp1; 1074 p2 = *pp2; 1075 1076 ORDERKEY_NAME 1077 ORDERKEY_PCTCPU 1078 ORDERKEY_CPTICKS 1079 ORDERKEY_STATE 1080 ORDERKEY_PRIO 1081 ORDERKEY_RSSIZE 1082 ORDERKEY_MEM 1083 ; 1084 1085 return result == 0 ? 0 : result < 0 ? -1 : 1; 1086 } 1087 1088 1089/* 1090 * proc_owner(pid) - returns the uid that owns process "pid", or -1 if 1091 * the process does not exist. 1092 * It is EXTREMLY IMPORTANT that this function work correctly. 1093 * If top runs setuid root (as in SVR4), then this function 1094 * is the only thing that stands in the way of a serious 1095 * security problem. It validates requests for the "kill" 1096 * and "renice" commands. 1097 */ 1098 1099int 1100proc_owner(int pid) 1101 1102{ 1103 struct stat sb; 1104 char buffer[32]; 1105 sprintf(buffer, "%d", pid); 1106 1107 if (stat(buffer, &sb) < 0) 1108 return -1; 1109 else 1110 return (int)sb.st_uid; 1111} 1112