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 FreeBSD 5.x, 6.x, 7.x, 8.x 37 * 38 * DESCRIPTION: 39 * Originally written for BSD4.4 system by Christos Zoulas. 40 * Ported to FreeBSD 2.x by Steven Wallace && Wolfram Schneider 41 * Order support hacked in from top-3.5beta6/machine/m_aix41.c 42 * by Monte Mitzelfelt 43 * Ported to FreeBSD 5.x and higher by William LeFebvre 44 * 45 * AUTHOR: Christos Zoulas <christos@ee.cornell.edu> 46 * Steven Wallace <swallace@freebsd.org> 47 * Wolfram Schneider <wosch@FreeBSD.org> 48 */ 49 50 51#include <sys/time.h> 52#include <sys/types.h> 53#include <sys/signal.h> 54#include <sys/param.h> 55 56#include "config.h" 57#include <stdio.h> 58#include <string.h> 59#include <nlist.h> 60#include <math.h> 61#include <kvm.h> 62#include <pwd.h> 63#include <sys/errno.h> 64#include <sys/sysctl.h> 65#include <sys/dkstat.h> 66#include <sys/file.h> 67#include <sys/time.h> 68#include <sys/proc.h> 69#include <sys/user.h> 70#include <sys/vmmeter.h> 71#include <sys/resource.h> 72#include <sys/rtprio.h> 73#ifdef HAVE_UNISTD_H 74#include <unistd.h> 75#endif 76 77/* Swap */ 78#include <stdlib.h> 79#include <sys/conf.h> 80 81#include <osreldate.h> /* for changes in kernel structures */ 82 83#include "top.h" 84#include "machine.h" 85#include "utils.h" 86#include "username.h" 87#include "hash.h" 88#include "display.h" 89 90extern char* printable __P((char *)); 91int swapmode __P((int *retavail, int *retfree)); 92static int smpmode; 93static int namelength; 94 95/* 96 * Versions prior to 5.x do not track threads in kinfo_proc, so we 97 * simply do not display any information about them. 98 * Versions 5.x, 6.x, and 7.x track threads but the data reported 99 * as runtime for each thread is actually per-process and is just 100 * duplicated across all threads. It would be very wrong to show 101 * this data individually for each thread. Therefore we will show 102 * a THR column (number of threads) but not provide any sort of 103 * per-thread display. We distinguish between these three ways of 104 * handling threads as follows: HAS_THREADS indicates that the 105 * system has and tracks kernel threads (a THR column will appear 106 * in the display). HAS_SHOWTHREADS indicates that the system 107 * reports correct per-thread information and we will provide a 108 * per-thread display (the 'H' and 't' command) upon request. 109 * HAS_SHOWTHREADS implies HAS_THREADS. 110 */ 111 112/* HAS_THREADS for anything 5.x and up */ 113#if OSMAJOR >= 5 114#define HAS_THREADS 115#endif 116 117/* HAS_SHOWTHREADS for anything 8.x and up */ 118#if OSMAJOR >=8 119#define HAS_SHOWTHREADS 120#endif 121 122/* get_process_info passes back a handle. This is what it looks like: */ 123 124struct handle 125{ 126 struct kinfo_proc **next_proc; /* points to next valid proc pointer */ 127 int remaining; /* number of pointers remaining */ 128}; 129 130/* declarations for load_avg */ 131#include "loadavg.h" 132 133/* 134 * Macros to access process information: 135 * In versions 4.x and earlier the kinfo_proc structure was a collection of 136 * substructures (kp_proc and kp_eproc). Starting with 5.0 kinfo_proc was 137 * redesigned and "flattene" so that most of the information was available 138 * in a single structure. We use macros to access the various types of 139 * information and define these macros according to the OS revision. The 140 * names PP, EP, and VP are due to the fact that information was originally 141 * contained in the different substructures. We retain these names in the 142 * code for backward compatibility. These macros use ANSI concatenation. 143 * PP: proc 144 * EP: extented proc 145 * VP: vm (virtual memory information) 146 * PRUID: Real uid 147 * RP: rusage 148 * PPCPU: where we store calculated cpu% data 149 * SPPTR: where we store pointer to extra calculated data 150 * SP: access to the extra calculated data pointed to by SPPTR 151 */ 152#if OSMAJOR <= 4 153#define PP(pp, field) ((pp)->kp_proc . p_##field) 154#define EP(pp, field) ((pp)->kp_eproc . e_##field) 155#define VP(pp, field) ((pp)->kp_eproc.e_vm . vm_##field) 156#define PRUID(pp) ((pp)->kp_eproc.e_pcred.p_ruid) 157#else 158#define PP(pp, field) ((pp)->ki_##field) 159#define EP(pp, field) ((pp)->ki_##field) 160#define VP(pp, field) ((pp)->ki_##field) 161#define PRUID(pp) ((pp)->ki_ruid) 162#define RP(pp, field) ((pp)->ki_rusage.ru_##field) 163#define PPCPU(pp) ((pp)->ki_sparelongs[0]) 164#define SPPTR(pp) ((pp)->ki_spareptrs[0]) 165#define SP(pp, field) (((struct save_proc *)((pp)->ki_spareptrs[0]))->sp_##field) 166#endif 167 168/* what we consider to be process size: */ 169#if OSMAJOR <= 4 170#define PROCSIZE(pp) (VP((pp), map.size) / 1024) 171#else 172#define PROCSIZE(pp) (((pp)->ki_size) / 1024) 173#endif 174 175/* calculate a per-second rate using milliseconds */ 176#define per_second(n, msec) (((n) * 1000) / (msec)) 177 178/* process state names for the "STATE" column of the display */ 179/* the extra nulls in the string "run" are for adding a slash and 180 the processor number when needed */ 181 182char *state_abbrev[] = 183{ 184 "?", "START", "RUN", "SLEEP", "STOP", "ZOMB", "WAIT", "LOCK" 185}; 186#define NUM_STATES 8 187 188/* kernel access */ 189static kvm_t *kd; 190 191/* these are for dealing with sysctl-based data */ 192#define MAXMIBLEN 8 193struct sysctl_mib { 194 char *name; 195 int mib[MAXMIBLEN]; 196 size_t miblen; 197}; 198static struct sysctl_mib mibs[] = { 199 { "vm.stats.sys.v_swtch" }, 200#define V_SWTCH 0 201 { "vm.stats.sys.v_trap" }, 202#define V_TRAP 1 203 { "vm.stats.sys.v_intr" }, 204#define V_INTR 2 205 { "vm.stats.sys.v_soft" }, 206#define V_SOFT 3 207 { "vm.stats.vm.v_forks" }, 208#define V_FORKS 4 209 { "vm.stats.vm.v_vforks" }, 210#define V_VFORKS 5 211 { "vm.stats.vm.v_rforks" }, 212#define V_RFORKS 6 213 { "vm.stats.vm.v_vm_faults" }, 214#define V_VM_FAULTS 7 215 { "vm.stats.vm.v_swapin" }, 216#define V_SWAPIN 8 217 { "vm.stats.vm.v_swapout" }, 218#define V_SWAPOUT 9 219 { "vm.stats.vm.v_tfree" }, 220#define V_TFREE 10 221 { "vm.stats.vm.v_vnodein" }, 222#define V_VNODEIN 11 223 { "vm.stats.vm.v_vnodeout" }, 224#define V_VNODEOUT 12 225 { "vm.stats.vm.v_active_count" }, 226#define V_ACTIVE_COUNT 13 227 { "vm.stats.vm.v_inactive_count" }, 228#define V_INACTIVE_COUNT 14 229 { "vm.stats.vm.v_wire_count" }, 230#define V_WIRE_COUNT 15 231 { "vm.stats.vm.v_cache_count" }, 232#define V_CACHE_COUNT 16 233 { "vm.stats.vm.v_free_count" }, 234#define V_FREE_COUNT 17 235 { "vm.stats.vm.v_swappgsin" }, 236#define V_SWAPPGSIN 18 237 { "vm.stats.vm.v_swappgsout" }, 238#define V_SWAPPGSOUT 19 239 { "vfs.bufspace" }, 240#define VFS_BUFSPACE 20 241 { "kern.cp_time" }, 242#define K_CP_TIME 21 243#ifdef HAS_SHOWTHREADS 244 { "kern.proc.all" }, 245#else 246 { "kern.proc.proc" }, 247#endif 248#define K_PROC 22 249 { NULL } 250}; 251 252 253/* these are for calculating cpu state percentages */ 254 255static long cp_time[CPUSTATES]; 256static long cp_old[CPUSTATES]; 257static long cp_diff[CPUSTATES]; 258 259/* these are for detailing the process states */ 260 261int process_states[8]; 262char *procstatenames[] = { 263 "", " starting, ", " running, ", " sleeping, ", " stopped, ", " zombie, ", 264 " waiting, ", " locked, ", 265 NULL 266}; 267 268/* these are for detailing the cpu states */ 269 270int cpu_states[CPUSTATES]; 271char *cpustatenames[] = { 272 "user", "nice", "system", "interrupt", "idle", NULL 273}; 274 275/* these are for detailing the kernel information */ 276 277int kernel_stats[9]; 278char *kernelnames[] = { 279 " ctxsw, ", " trap, ", " intr, ", " soft, ", " fork, ", 280 " flt, ", " pgin, ", " pgout, ", " fr", 281 NULL 282}; 283 284/* these are for detailing the memory statistics */ 285 286long memory_stats[7]; 287char *memorynames[] = { 288 "K Active, ", "K Inact, ", "K Wired, ", "K Cache, ", "K Buf, ", "K Free", 289 NULL 290}; 291 292long swap_stats[7]; 293char *swapnames[] = { 294/* 0 1 2 3 4 5 */ 295 "K Total, ", "K Used, ", "K Free, ", "% Inuse, ", "K In, ", "K Out", 296 NULL 297}; 298 299 300/* 301 * pbase points to the array that holds the kinfo_proc structures. pref 302 * (pronounced p-ref) points to an array of kinfo_proc pointers and is where 303 * we build up a list of processes we wish to display. Both pbase and pref are 304 * potentially resized on every call to get_process_info. psize is the number 305 * of procs for which we currently have space allocated. pref_len is the number 306 * of valid pointers in pref (this is used by proc_owner). We start psize off 307 * at -1 to ensure that space gets allocated on the first call to 308 * get_process_info. 309 */ 310 311static int psize = -1; 312static int pref_len; 313static struct kinfo_proc *pbase = NULL; 314static struct kinfo_proc **pref = NULL; 315 316/* this structure retains information from the proc array between samples */ 317struct save_proc { 318 pid_t sp_pid; 319 u_int64_t sp_runtime; 320 long sp_vcsw; 321 long sp_ivcsw; 322 long sp_inblock; 323 long sp_oublock; 324 long sp_majflt; 325 long sp_totalio; 326 long sp_old_nvcsw; 327 long sp_old_nivcsw; 328 long sp_old_inblock; 329 long sp_old_oublock; 330 long sp_old_majflt; 331}; 332hash_table *procs; 333 334struct proc_field { 335 char *name; 336 int width; 337 int rjust; 338 int min_screenwidth; 339 int (*format)(char *, int, struct kinfo_proc *); 340}; 341 342/* these are for getting the memory statistics */ 343 344static int pagesize; /* kept from getpagesize */ 345static int pageshift; /* log base 2 of the pagesize */ 346 347/* define pagetok in terms of pageshift */ 348 349#define pagetok(size) ((size) << pageshift) 350 351/* things that we track between updates */ 352static u_int ctxsws = 0; 353static u_int traps = 0; 354static u_int intrs = 0; 355static u_int softs = 0; 356static u_int64_t forks = 0; 357static u_int pfaults; 358static u_int pagein; 359static u_int pageout; 360static u_int tfreed; 361static int swappgsin = -1; 362static int swappgsout = -1; 363extern struct timeval timeout; 364static struct timeval lasttime = { 0, 0 }; 365static long elapsed_time; 366static long elapsed_msecs; 367 368/* things that we track during an update */ 369static long total_io; 370static int show_fullcmd; 371static struct handle handle; 372static int username_length; 373static int show_usernames; 374static int display_mode; 375static int *display_fields; 376#ifdef HAS_SHOWTHREADS 377static int show_threads = 0; 378#endif 379 380 381/* sorting orders. first is default */ 382char *ordernames[] = { 383 "cpu", "size", "res", "time", "pri", "io", "pid", NULL 384}; 385 386/* compare routines */ 387int proc_compare(), compare_size(), compare_res(), compare_time(), 388 compare_prio(), compare_io(), compare_pid(); 389 390int (*proc_compares[])() = { 391 proc_compare, 392 compare_size, 393 compare_res, 394 compare_time, 395 compare_prio, 396 compare_io, 397 compare_pid, 398 NULL 399}; 400 401/* swap related calculations */ 402 403static int mib_swapinfo[16]; 404static int *mib_swapinfo_idx; 405static int mib_swapinfo_size = 0; 406 407void 408swap_init() 409 410{ 411 size_t m; 412 413 m = sizeof(mib_swapinfo) / sizeof(mib_swapinfo[0]); 414 if (sysctlnametomib("vm.swap_info", mib_swapinfo, &m) != -1) 415 { 416 mib_swapinfo_size = m + 1; 417 mib_swapinfo_idx = &(mib_swapinfo[m]); 418 } 419} 420 421int 422swap_getdata(long long *retavail, long long *retfree) 423 424{ 425 int n; 426 size_t size; 427 long long total = 0; 428 long long used = 0; 429 struct xswdev xsw; 430 431 n = 0; 432 if (mib_swapinfo_size > 0) 433 { 434 *mib_swapinfo_idx = 0; 435 while (size = sizeof(xsw), 436 sysctl(mib_swapinfo, mib_swapinfo_size, &xsw, &size, NULL, 0) != -1) 437 { 438 dprintf("swap_getdata: swaparea %d: nblks %d, used %d\n", 439 n, xsw.xsw_nblks, xsw.xsw_used); 440 total += (long long)xsw.xsw_nblks; 441 used += (long long)xsw.xsw_used; 442 *mib_swapinfo_idx = ++n; 443 } 444 445 *retavail = pagetok(total); 446 *retfree = pagetok(total) - pagetok(used); 447 448 if (total > 0) 449 { 450 n = (int)((double)used * 100.0 / (double)total); 451 } 452 else 453 { 454 n = 0; 455 } 456 } 457 else 458 { 459 *retavail = 0; 460 *retfree = 0; 461 } 462 463 dprintf("swap_getdata: avail %lld, free %lld, %d%%\n", 464 *retavail, *retfree, n); 465 return(n); 466} 467 468/* 469 * getkval(offset, ptr, size) - get a value out of the kernel. 470 * "offset" is the byte offset into the kernel for the desired value, 471 * "ptr" points to a buffer into which the value is retrieved, 472 * "size" is the size of the buffer (and the object to retrieve). 473 * Return 0 on success, -1 on any kind of failure. 474 */ 475 476static int 477getkval(unsigned long offset, int *ptr, int size) 478 479{ 480 if (kd != NULL) 481 { 482 if (kvm_read(kd, offset, (char *) ptr, size) == size) 483 { 484 return(0); 485 } 486 } 487 return(-1); 488} 489 490int 491get_sysctl_mibs() 492 493{ 494 struct sysctl_mib *mp; 495 size_t len; 496 497 mp = mibs; 498 while (mp->name != NULL) 499 { 500 len = MAXMIBLEN; 501 if (sysctlnametomib(mp->name, mp->mib, &len) == -1) 502 { 503 message_error(" sysctlnametomib: %s", strerror(errno)); 504 return -1; 505 } 506 mp->miblen = len; 507 mp++; 508 } 509 return 0; 510} 511 512int 513get_sysctl(int idx, void *v, size_t l) 514 515{ 516 struct sysctl_mib *m; 517 size_t len; 518 519 m = &(mibs[idx]); 520 len = l; 521 if (sysctl(m->mib, m->miblen, v, &len, NULL, 0) == -1) 522 { 523 message_error(" sysctl: %s", strerror(errno)); 524 return -1; 525 } 526 return len; 527} 528 529size_t 530get_sysctlsize(int idx) 531 532{ 533 size_t len; 534 struct sysctl_mib *m; 535 536 m = &(mibs[idx]); 537 if (sysctl(m->mib, m->miblen, NULL, &len, NULL, 0) == -1) 538 { 539 message_error(" sysctl (size): %s", strerror(errno)); 540 len = 0; 541 } 542 return len; 543} 544 545int 546fmt_pid(char *buf, int sz, struct kinfo_proc *pp) 547 548{ 549 return snprintf(buf, sz, "%6d", PP(pp, pid)); 550} 551 552int 553fmt_username(char *buf, int sz, struct kinfo_proc *pp) 554 555{ 556 return snprintf(buf, sz, "%-*.*s", 557 username_length, username_length, username(PRUID(pp))); 558} 559 560int 561fmt_uid(char *buf, int sz, struct kinfo_proc *pp) 562 563{ 564 return snprintf(buf, sz, "%6d", PRUID(pp)); 565} 566 567int 568fmt_thr(char *buf, int sz, struct kinfo_proc *pp) 569 570{ 571 return snprintf(buf, sz, "%3d", PP(pp, numthreads)); 572} 573 574int 575fmt_pri(char *buf, int sz, struct kinfo_proc *pp) 576 577{ 578#if OSMAJOR <= 4 579 return snprintf(buf, sz, "%3d", PP(pp, priority)); 580#else 581 return snprintf(buf, sz, "%3d", PP(pp, pri.pri_level)); 582#endif 583} 584 585int 586fmt_nice(char *buf, int sz, struct kinfo_proc *pp) 587 588{ 589 return snprintf(buf, sz, "%4d", PP(pp, nice) - NZERO); 590} 591 592int 593fmt_size(char *buf, int sz, struct kinfo_proc *pp) 594 595{ 596 return snprintf(buf, sz, "%5s", format_k(PROCSIZE(pp))); 597} 598 599int 600fmt_res(char *buf, int sz, struct kinfo_proc *pp) 601 602{ 603 return snprintf(buf, sz, "%5s", format_k(pagetok(VP(pp, rssize)))); 604} 605 606int 607fmt_state(char *buf, int sz, struct kinfo_proc *pp) 608 609{ 610 int state; 611 char status[16]; 612 613 state = PP(pp, stat); 614 switch(state) 615 { 616 case SRUN: 617 if (smpmode && PP(pp, oncpu) != 0xff) 618 sprintf(status, "CPU%d", PP(pp, oncpu)); 619 else 620 strcpy(status, "RUN"); 621 break; 622 623 case SSLEEP: 624 if (EP(pp, wmesg) != NULL) { 625 sprintf(status, "%.6s", EP(pp, wmesg)); 626 break; 627 } 628 /* fall through */ 629 default: 630 if (state >= 0 && state < NUM_STATES) 631 sprintf(status, "%.6s", state_abbrev[(unsigned char) state]); 632 else 633 sprintf(status, "?%-5d", state); 634 break; 635 } 636 637 return snprintf(buf, sz, "%-6.6s", status); 638} 639 640int 641fmt_flags(char *buf, int sz, struct kinfo_proc *pp) 642 643{ 644 long flag; 645 char chrs[12]; 646 char *p; 647 648 flag = PP(pp, flag); 649 p = chrs; 650 if (PP(pp, nice) < NZERO) 651 *p++ = '<'; 652 else if (PP(pp, nice) > NZERO) 653 *p++ = 'N'; 654 if (flag & P_TRACED) 655 *p++ = 'X'; 656 if (flag & P_WEXIT && PP(pp, stat) != SZOMB) 657 *p++ = 'E'; 658 if (flag & P_PPWAIT) 659 *p++ = 'V'; 660 if (flag & P_SYSTEM || PP(pp, lock) > 0) 661 *p++ = 'L'; 662 if (PP(pp, kiflag) & KI_SLEADER) 663 *p++ = 's'; 664 if (flag & P_CONTROLT) 665 *p++ = '+'; 666 if (flag & P_JAILED) 667 *p++ = 'J'; 668 *p = '\0'; 669 670 return snprintf(buf, sz, "%-3.3s", chrs); 671} 672 673int 674fmt_c(char *buf, int sz, struct kinfo_proc *pp) 675 676{ 677 return snprintf(buf, sz, "%1x", PP(pp, lastcpu)); 678} 679 680int 681fmt_time(char *buf, int sz, struct kinfo_proc *pp) 682 683{ 684 return snprintf(buf, sz, "%6s", 685 format_time((PP(pp, runtime) + 500000) / 1000000)); 686} 687 688int 689fmt_cpu(char *buf, int sz, struct kinfo_proc *pp) 690 691{ 692 return snprintf(buf, sz, "%5.2f%%", (double)PPCPU(pp) / 100.0); 693} 694 695int 696fmt_command(char *buf, int sz, struct kinfo_proc *pp) 697 698{ 699 int inmem; 700 char cmd[MAX_COLS]; 701 char *bufp; 702 struct pargs pargs; 703 int len; 704 705#if OSMAJOR <= 4 706 inmem = (PP(pp, flag) & P_INMEM); 707#else 708 inmem = (PP(pp, sflag) & PS_INMEM); 709#endif 710 711 if (show_fullcmd && inmem) 712 { 713 /* get the pargs structure */ 714 if (getkval((unsigned long)PP(pp, args), (int *)&pargs, sizeof(pargs)) != -1) 715 { 716 /* determine workable length */ 717 if ((len = pargs.ar_length) >= MAX_COLS) 718 { 719 len = MAX_COLS - 1; 720 } 721 722 /* get the string from that */ 723 if (len > 0 && getkval((unsigned long)PP(pp, args) + 724 sizeof(pargs.ar_ref) + 725 sizeof(pargs.ar_length), 726 (int *)cmd, len) != -1) 727 { 728 /* successfull retrieval: now convert nulls in to spaces */ 729 bufp = cmd; 730 while (len-- > 0) 731 { 732 if (*bufp == '\0') 733 { 734 *bufp = ' '; 735 } 736 bufp++; 737 } 738 739 /* null terminate cmd */ 740 *--bufp = '\0'; 741 742 /* format cmd as our answer */ 743 return snprintf(buf, sz, "%s", cmd); 744 } 745 } 746 } 747 748 /* for anything else we just display comm */ 749 return snprintf(buf, sz, inmem ? "%s" : "<%s>", printable(PP(pp, comm))); 750} 751 752int 753fmt_vcsw(char *buf, int sz, struct kinfo_proc *pp) 754 755{ 756 return snprintf(buf, sz, "%6ld", per_second(SP(pp, vcsw), elapsed_msecs)); 757} 758 759int 760fmt_ivcsw(char *buf, int sz, struct kinfo_proc *pp) 761 762{ 763 return snprintf(buf, sz, "%6ld", per_second(SP(pp, ivcsw), elapsed_msecs)); 764} 765 766int 767fmt_read(char *buf, int sz, struct kinfo_proc *pp) 768 769{ 770 return snprintf(buf, sz, "%6ld", per_second(SP(pp, inblock), elapsed_msecs)); 771} 772 773int 774fmt_write(char *buf, int sz, struct kinfo_proc *pp) 775 776{ 777 return snprintf(buf, sz, "%6ld", per_second(SP(pp, oublock), elapsed_msecs)); 778} 779 780int 781fmt_fault(char *buf, int sz, struct kinfo_proc *pp) 782 783{ 784 return snprintf(buf, sz, "%6ld", per_second(SP(pp, majflt), elapsed_msecs)); 785} 786 787int 788fmt_iototal(char *buf, int sz, struct kinfo_proc *pp) 789 790{ 791 return snprintf(buf, sz, "%6ld", per_second(SP(pp, totalio), elapsed_msecs)); 792} 793 794int 795fmt_iopct(char *buf, int sz, struct kinfo_proc *pp) 796 797{ 798 return snprintf(buf, sz, "%6.2f", (SP(pp, totalio) * 100.) / total_io); 799} 800 801 802struct proc_field proc_field[] = { 803 { "PID", 6, 1, 0, fmt_pid }, 804 { "USERNAME", 8, 0, 0, fmt_username }, 805#define FIELD_USERNAME 1 806 { "UID", 6, 1, 0, fmt_uid }, 807#define FIELD_UID 2 808 { "THR", 3, 1, 0, fmt_thr }, 809 { "PRI", 3, 1, 0, fmt_pri }, 810 { "NICE", 4, 1, 0, fmt_nice }, 811 { "SIZE", 5, 1, 0, fmt_size }, 812 { "RES", 5, 1, 0, fmt_res }, 813 { "STATE", 6, 0, 0, fmt_state }, 814 { "FLG", 3, 0, 84, fmt_flags }, 815 { "C", 1, 0, 0, fmt_c }, 816 { "TIME", 6, 1, 0, fmt_time }, 817 { "CPU", 6, 1, 0, fmt_cpu }, 818 { "COMMAND", 7, 0, 0, fmt_command }, 819 { "VCSW", 6, 1, 0, fmt_vcsw }, 820 { "IVCSW", 6, 1, 0, fmt_ivcsw }, 821 { "READ", 6, 1, 0, fmt_read }, 822 { "WRITE", 6, 1, 0, fmt_write }, 823 { "FAULT", 6, 1, 0, fmt_fault }, 824 { "TOTAL", 6, 1, 0, fmt_iototal }, 825 { "PERCENT", 7, 1, 0, fmt_iopct }, 826 { NULL, 0, 0, 0, NULL } 827}; 828#define MAX_FIELDS 24 829 830static int mode0_display[MAX_FIELDS]; 831static int mode0thr_display[MAX_FIELDS]; 832static int mode1_display[MAX_FIELDS]; 833 834int 835field_index(char *col) 836 837{ 838 struct proc_field *fp; 839 int i = 0; 840 841 fp = proc_field; 842 while (fp->name != NULL) 843 { 844 if (strcmp(col, fp->name) == 0) 845 { 846 return i; 847 } 848 fp++; 849 i++; 850 } 851 852 return -1; 853} 854 855void 856field_subst(int *fp, int old, int new) 857 858{ 859 while (*fp != -1) 860 { 861 if (*fp == old) 862 { 863 *fp = new; 864 } 865 fp++; 866 } 867} 868 869int 870machine_init(struct statics *statics) 871 872{ 873 int i = 0; 874 size_t len; 875 int *ip; 876 877 struct timeval boottime; 878 879 len = sizeof(smpmode); 880 if ((sysctlbyname("machdep.smp_active", &smpmode, &len, NULL, 0) < 0 && 881 sysctlbyname("smp.smp_active", &smpmode, &len, NULL, 0) < 0) || 882 len != sizeof(smpmode)) 883 { 884 smpmode = 0; 885 } 886 smpmode = smpmode != 0; 887 888 /* kvm_open the active kernel: its okay if this fails */ 889 kd = kvm_open(NULL, NULL, NULL, O_RDONLY, NULL); 890 891 /* get boot time */ 892 len = sizeof(boottime); 893 if (sysctlbyname("kern.boottime", &boottime, &len, NULL, 0) == -1) 894 { 895 /* we have no boottime to report */ 896 boottime.tv_sec = -1; 897 } 898 899 pbase = NULL; 900 pref = NULL; 901 902 /* get the page size with "getpagesize" and calculate pageshift from it */ 903 i = pagesize = getpagesize(); 904 pageshift = 0; 905 while (i > 1) 906 { 907 pageshift++; 908 i >>= 1; 909 } 910 911 /* translate sysctl paths to mibs for faster access */ 912 get_sysctl_mibs(); 913 914 /* initialize swap stuff */ 915 swap_init(); 916 917 /* create the hash table that remembers proc data */ 918 procs = hash_create(2039); 919 920 /* we only need the amount of log(2)1024 for our conversion */ 921 pageshift -= LOG1024; 922 923 /* fill in the statics information */ 924 statics->procstate_names = procstatenames; 925 statics->cpustate_names = cpustatenames; 926 statics->memory_names = memorynames; 927 statics->kernel_names = kernelnames; 928 statics->boottime = boottime.tv_sec; 929 statics->swap_names = swapnames; 930 statics->order_names = ordernames; 931 statics->flags.warmup = 1; 932 statics->modemax = 2; 933#ifdef HAS_SHOWTHREADS 934 statics->flags.threads = 1; 935#endif 936 937 /* we need kvm descriptor in order to show full commands */ 938 statics->flags.fullcmds = kd != NULL; 939 940 /* set up the display indices for mode0 */ 941 ip = mode0_display; 942 *ip++ = field_index("PID"); 943 *ip++ = field_index("USERNAME"); 944#ifdef HAS_THREADS 945 *ip++ = field_index("THR"); 946#endif 947 *ip++ = field_index("PRI"); 948 *ip++ = field_index("NICE"); 949 *ip++ = field_index("SIZE"); 950 *ip++ = field_index("RES"); 951 *ip++ = field_index("STATE"); 952 *ip++ = field_index("FLG"); 953 if (smpmode) 954 *ip++ = field_index("C"); 955 *ip++ = field_index("TIME"); 956 *ip++ = field_index("CPU"); 957 *ip++ = field_index("COMMAND"); 958 *ip = -1; 959 960#ifdef HAS_SHOWTHREADS 961 /* set up the display indices for mode0 showing threads */ 962 ip = mode0thr_display; 963 *ip++ = field_index("PID"); 964 *ip++ = field_index("USERNAME"); 965 *ip++ = field_index("PRI"); 966 *ip++ = field_index("NICE"); 967 *ip++ = field_index("SIZE"); 968 *ip++ = field_index("RES"); 969 *ip++ = field_index("STATE"); 970 *ip++ = field_index("FLG"); 971 if (smpmode) 972 *ip++ = field_index("C"); 973 *ip++ = field_index("TIME"); 974 *ip++ = field_index("CPU"); 975 *ip++ = field_index("COMMAND"); 976 *ip = -1; 977#endif 978 979 /* set up the display indices for mode1 */ 980 ip = mode1_display; 981 *ip++ = field_index("PID"); 982 *ip++ = field_index("USERNAME"); 983 *ip++ = field_index("VCSW"); 984 *ip++ = field_index("IVCSW"); 985 *ip++ = field_index("READ"); 986 *ip++ = field_index("WRITE"); 987 *ip++ = field_index("FAULT"); 988 *ip++ = field_index("TOTAL"); 989 *ip++ = field_index("PERCENT"); 990 *ip++ = field_index("COMMAND"); 991 *ip = -1; 992 993 /* all done! */ 994 return(0); 995} 996 997char *format_header(char *uname_field) 998 999{ 1000 return ""; 1001} 1002 1003void 1004get_vm_sum(struct vmmeter *sum) 1005 1006{ 1007#define GET_VM_STAT(v, s) (void)get_sysctl(v, &(sum->s), sizeof(sum->s)) 1008 1009 GET_VM_STAT(V_SWTCH, v_swtch); 1010 GET_VM_STAT(V_TRAP, v_trap); 1011 GET_VM_STAT(V_INTR, v_intr); 1012 GET_VM_STAT(V_SOFT, v_soft); 1013 GET_VM_STAT(V_VFORKS, v_vforks); 1014 GET_VM_STAT(V_FORKS, v_forks); 1015 GET_VM_STAT(V_RFORKS, v_rforks); 1016 GET_VM_STAT(V_VM_FAULTS, v_vm_faults); 1017 GET_VM_STAT(V_SWAPIN, v_swapin); 1018 GET_VM_STAT(V_SWAPOUT, v_swapout); 1019 GET_VM_STAT(V_TFREE, v_tfree); 1020 GET_VM_STAT(V_VNODEIN, v_vnodein); 1021 GET_VM_STAT(V_VNODEOUT, v_vnodeout); 1022 GET_VM_STAT(V_ACTIVE_COUNT, v_active_count); 1023 GET_VM_STAT(V_INACTIVE_COUNT, v_inactive_count); 1024 GET_VM_STAT(V_WIRE_COUNT, v_wire_count); 1025 GET_VM_STAT(V_CACHE_COUNT, v_cache_count); 1026 GET_VM_STAT(V_FREE_COUNT, v_free_count); 1027 GET_VM_STAT(V_SWAPPGSIN, v_swappgsin); 1028 GET_VM_STAT(V_SWAPPGSOUT, v_swappgsout); 1029} 1030 1031void 1032get_system_info(struct system_info *si) 1033 1034{ 1035 long total; 1036 struct timeval thistime; 1037 struct timeval timediff; 1038 1039 /* timestamp and time difference */ 1040 gettimeofday(&thistime, 0); 1041 timersub(&thistime, &lasttime, &timediff); 1042 elapsed_time = timediff.tv_sec * 1000000 + timediff.tv_usec; 1043 elapsed_msecs = timediff.tv_sec * 1000 + timediff.tv_usec / 1000; 1044 1045 /* get the load averages */ 1046 if (getloadavg(si->load_avg, NUM_AVERAGES) == -1) 1047 { 1048 /* failed: fill in with zeroes */ 1049 (void) memset(si->load_avg, 0, sizeof(si->load_avg)); 1050 } 1051 1052 /* get the cp_time array */ 1053 (void)get_sysctl(K_CP_TIME, &cp_time, sizeof(cp_time)); 1054 1055 /* convert cp_time counts to percentages */ 1056 total = percentages(CPUSTATES, cpu_states, cp_time, cp_old, cp_diff); 1057 1058 /* sum memory & swap statistics */ 1059 { 1060 struct vmmeter sum; 1061 static unsigned int swap_delay = 0; 1062 static long long swapavail = 0; 1063 static long long swapfree = 0; 1064 static int bufspace = 0; 1065 1066 get_vm_sum(&sum); 1067 1068 /* get bufspace */ 1069 bufspace = 0; 1070 (void) get_sysctl(VFS_BUFSPACE, &bufspace, sizeof(bufspace)); 1071 1072 /* kernel stats */ 1073 dprintf("kernel: swtch %d, trap %d, intr %d, soft %d, vforks %d\n", 1074 sum.v_swtch, sum.v_trap, sum.v_intr, sum.v_soft, sum.v_vforks); 1075 kernel_stats[0] = per_second(sum.v_swtch - ctxsws, elapsed_msecs); 1076 kernel_stats[1] = per_second(sum.v_trap - traps, elapsed_msecs); 1077 kernel_stats[2] = per_second(sum.v_intr - intrs, elapsed_msecs); 1078 kernel_stats[3] = per_second(sum.v_soft - softs, elapsed_msecs); 1079 kernel_stats[4] = per_second(sum.v_vforks + sum.v_forks + 1080 sum.v_rforks - forks, elapsed_msecs); 1081 kernel_stats[5] = per_second(sum.v_vm_faults - pfaults, elapsed_msecs); 1082 kernel_stats[6] = per_second(sum.v_swapin + sum.v_vnodein - pagein, elapsed_msecs); 1083 kernel_stats[7] = per_second(sum.v_swapout + sum.v_vnodeout - pageout, elapsed_msecs); 1084 kernel_stats[8] = per_second(sum.v_tfree - tfreed, elapsed_msecs); 1085 ctxsws = sum.v_swtch; 1086 traps = sum.v_trap; 1087 intrs = sum.v_intr; 1088 softs = sum.v_soft; 1089 forks = (u_int64_t)sum.v_vforks + sum.v_forks + sum.v_rforks; 1090 pfaults = sum.v_vm_faults; 1091 pagein = sum.v_swapin + sum.v_vnodein; 1092 pageout = sum.v_swapout + sum.v_vnodeout; 1093 tfreed = sum.v_tfree; 1094 1095 /* convert memory stats to Kbytes */ 1096 memory_stats[0] = pagetok(sum.v_active_count); 1097 memory_stats[1] = pagetok(sum.v_inactive_count); 1098 memory_stats[2] = pagetok(sum.v_wire_count); 1099 memory_stats[3] = pagetok(sum.v_cache_count); 1100 memory_stats[4] = bufspace / 1024; 1101 memory_stats[5] = pagetok(sum.v_free_count); 1102 memory_stats[6] = -1; 1103 1104 /* first interval */ 1105 if (swappgsin < 0) 1106 { 1107 swap_stats[4] = 0; 1108 swap_stats[5] = 0; 1109 } 1110 1111 /* compute differences between old and new swap statistic */ 1112 else 1113 { 1114 swap_stats[4] = pagetok(sum.v_swappgsin - swappgsin); 1115 swap_stats[5] = pagetok(sum.v_swappgsout - swappgsout); 1116 } 1117 1118 swappgsin = sum.v_swappgsin; 1119 swappgsout = sum.v_swappgsout; 1120 1121 /* call CPU heavy swap_getdata() only for changes */ 1122 if (swap_stats[4] > 0 || swap_stats[5] > 0 || swap_delay == 0) 1123 { 1124 swap_stats[3] = swap_getdata(&swapavail, &swapfree); 1125 swap_stats[0] = swapavail; 1126 swap_stats[1] = swapavail - swapfree; 1127 swap_stats[2] = swapfree; 1128 } 1129 swap_delay = 1; 1130 swap_stats[6] = -1; 1131 } 1132 1133 /* set arrays and strings */ 1134 si->cpustates = cpu_states; 1135 si->kernel = kernel_stats; 1136 si->memory = memory_stats; 1137 si->swap = swap_stats; 1138 1139 si->last_pid = -1; 1140 1141 lasttime = thistime; 1142} 1143 1144caddr_t 1145get_process_info(struct system_info *si, 1146 struct process_select *sel, 1147 int compare_index) 1148 1149{ 1150 int i; 1151 int total_procs; 1152 int active_procs; 1153 struct kinfo_proc **prefp; 1154 struct kinfo_proc *pp; 1155 struct kinfo_proc *prev_pp = NULL; 1156 struct save_proc *savep; 1157 long proc_io; 1158 pid_t pid; 1159 size_t size; 1160 int nproc; 1161 1162 /* these are copied out of sel for speed */ 1163 int show_idle; 1164 int show_self; 1165 int show_system; 1166 int show_uid; 1167 char *show_command; 1168 1169 /* get proc table size and give it a boost */ 1170 nproc = (int)get_sysctlsize(K_PROC) / sizeof(struct kinfo_proc); 1171 nproc += nproc >> 4; 1172 size = nproc * sizeof(struct kinfo_proc); 1173 dprintf("get_process_info: nproc %d, psize %d, size %d\n", nproc, psize, size); 1174 1175 /* make sure we have enough space allocated */ 1176 if (nproc > psize) 1177 { 1178 /* reallocate both pbase and pref */ 1179 pbase = (struct kinfo_proc *)realloc(pbase, size); 1180 pref = (struct kinfo_proc **)realloc(pref, 1181 sizeof(struct kinfo_proc *) * nproc); 1182 psize = nproc; 1183 } 1184 1185 /* make sure we got the space we asked for */ 1186 if (pref == NULL || pbase == NULL) 1187 { 1188 /* abandon all hope */ 1189 message_error(" Out of memory!"); 1190 nproc = psize = 0; 1191 si->p_total = 0; 1192 si->p_active = 0; 1193 return NULL; 1194 } 1195 1196 /* get all process information (threads, too) */ 1197 if (size > 0) 1198 { 1199 nproc = get_sysctl(K_PROC, pbase, size); 1200 if (nproc == -1) 1201 { 1202 nproc = 0; 1203 } 1204 else 1205 { 1206 nproc /= sizeof(struct kinfo_proc); 1207 } 1208 } 1209 1210 /* get a pointer to the states summary array */ 1211 si->procstates = process_states; 1212 1213 /* set up flags which define what we are going to select */ 1214 show_idle = sel->idle; 1215 show_self = 0; 1216 show_system = sel->system; 1217 show_uid = sel->uid != -1; 1218 show_fullcmd = sel->fullcmd; 1219 show_command = sel->command; 1220 show_usernames = sel->usernames; 1221 display_mode = sel->mode; 1222#ifdef HAS_SHOWTHREADS 1223 show_threads = sel->threads; 1224#endif 1225 1226 /* count up process states and get pointers to interesting procs */ 1227 total_procs = 0; 1228 active_procs = 0; 1229 total_io = 0; 1230 memset((char *)process_states, 0, sizeof(process_states)); 1231 prefp = pref; 1232 for (pp = pbase, i = 0; i < nproc; pp++, i++) 1233 { 1234 /* 1235 * Place pointers to each valid proc structure in pref[]. 1236 * Process slots that are actually in use have a non-zero 1237 * status field. Processes with P_SYSTEM set are system 1238 * processes---these get ignored unless show_sysprocs is set. 1239 */ 1240 pid = PP(pp, pid); 1241 if (PP(pp, stat) != 0) 1242 { 1243#ifdef HAS_SHOWTHREADS 1244 int is_thread; 1245 lwpid_t tid; 1246 1247 /* get thread id */ 1248 tid = PP(pp, tid); 1249 1250 /* is this just a thread? */ 1251 is_thread = (prev_pp != NULL && PP(prev_pp, pid) == pid); 1252 1253 /* count this process and its state */ 1254 /* only count threads if we are showing them */ 1255 if (show_threads || !is_thread) 1256 { 1257 total_procs++; 1258 process_states[(unsigned char) PP(pp, stat)]++; 1259 } 1260 1261 /* grab old data from hash */ 1262 if ((savep = hash_lookup_lwpid(procs, tid)) != NULL) 1263 { 1264 /* verify that this is not a new or different thread */ 1265 /* (freebsd reuses thread ids fairly quickly) */ 1266 /* pids must match and time can't have gone backwards */ 1267 if (pid != savep->sp_pid || PP(pp, runtime) < savep->sp_runtime) 1268 { 1269 /* not the same thread -- reuse the save_proc structure */ 1270 memset(savep, 0, sizeof(struct save_proc)); 1271 savep->sp_pid = pid; 1272 } 1273 } 1274 else 1275 { 1276 /* havent seen this one before */ 1277 savep = (struct save_proc *)calloc(1, sizeof(struct save_proc)); 1278 savep->sp_pid = pid; 1279 hash_add_lwpid(procs, tid, savep); 1280 } 1281 1282#else /* !HAS_SHOWTHREADS */ 1283 total_procs++; 1284 process_states[(unsigned char) PP(pp, stat)]++; 1285 1286 /* grab old data from hash */ 1287 if ((savep = hash_lookup_pid(procs, pid)) == NULL) 1288 { 1289 /* havent seen this one before */ 1290 savep = (struct save_proc *)calloc(1, sizeof(struct save_proc)); 1291 savep->sp_pid = pid; 1292 hash_add_pid(procs, pid, savep); 1293 } 1294#endif 1295 1296 /* save the pointer to the sp struct */ 1297 SPPTR(pp) = (void *)savep; 1298 1299 /* calculate %cpu */ 1300 PPCPU(pp) = ((PP(pp, runtime) - savep->sp_runtime) * 10000) / 1301 elapsed_time; 1302 dprintf("%d (%d): runtime %lld, saved_pid %d, saved_runtime %lld, elapsed_time %d, ppcpu %d\n", 1303 pid, PP(pp, tid), PP(pp, runtime), savep->sp_pid, savep->sp_runtime, 1304 elapsed_time, PPCPU(pp)); 1305 1306 /* calculate io differences */ 1307 proc_io = 0; 1308 savep->sp_vcsw = (RP(pp, nvcsw) - savep->sp_old_nvcsw); 1309 savep->sp_ivcsw = (RP(pp, nivcsw) - savep->sp_old_nivcsw); 1310 proc_io += (savep->sp_inblock = (RP(pp, inblock) - savep->sp_old_inblock)); 1311 proc_io += (savep->sp_oublock = (RP(pp, oublock) - savep->sp_old_oublock)); 1312 proc_io += (savep->sp_majflt = (RP(pp, majflt) - savep->sp_old_majflt)); 1313 total_io += proc_io; 1314 savep->sp_totalio = proc_io; 1315 1316 /* save data for next time */ 1317 savep->sp_runtime = PP(pp, runtime); 1318 savep->sp_old_nvcsw = RP(pp, nvcsw); 1319 savep->sp_old_nivcsw = RP(pp, nivcsw); 1320 savep->sp_old_inblock = RP(pp, inblock); 1321 savep->sp_old_oublock = RP(pp, oublock); 1322 savep->sp_old_majflt = RP(pp, majflt); 1323 1324 /* is this one selected for viewing? */ 1325 if ((PP(pp, stat) != SZOMB) && 1326 (show_system || ((PP(pp, flag) & P_SYSTEM) == 0)) && 1327 (show_idle || (PP(pp, pctcpu) != 0) || 1328 (PP(pp, stat) == SRUN)) && 1329 (!show_uid || PRUID(pp) == (uid_t)sel->uid) && 1330 (show_command == NULL || 1331 strcasestr(PP(pp, comm), show_command) != NULL)) 1332 { 1333#ifdef HAS_SHOWTHREADS 1334 /* yes, but make sure it isn't just a thread */ 1335 if (show_threads || !is_thread) 1336 { 1337 /* we will be showing this thread */ 1338 *prefp++ = pp; 1339 active_procs++; 1340 } 1341 else 1342 { 1343 /* we will not be showing this thread, but we need to roll 1344 up its cpu usage in to its process */ 1345 PP(prev_pp, pctcpu) += PP(pp, pctcpu); 1346 } 1347#else /* !HAS_SHOWTHREADS */ 1348 /* we will be showing this process */ 1349 *prefp++ = pp; 1350 active_procs++; 1351#endif 1352 } 1353 prev_pp = pp; 1354 } 1355 } 1356 1357 dprintf("total_io: %d\n", total_io); 1358 if (total_io == 0) total_io = 1; 1359 1360 /* if requested, sort the "interesting" processes */ 1361 if (active_procs > 1) 1362 { 1363 qsort((char *)pref, active_procs, sizeof(struct kinfo_proc *), 1364 proc_compares[compare_index]); 1365 } 1366 1367 /* remember active and total counts */ 1368 si->p_total = total_procs; 1369 si->p_active = pref_len = active_procs; 1370 1371 /* pass back a handle */ 1372 handle.next_proc = pref; 1373 handle.remaining = active_procs; 1374 return((caddr_t)&handle); 1375} 1376 1377static char p_header[MAX_COLS]; 1378 1379char * 1380format_process_header(struct process_select *sel, caddr_t handle, int count) 1381 1382{ 1383 int cols; 1384 int n; 1385 int w; 1386 char *p; 1387 int *fi; 1388 struct kinfo_proc **kip; 1389 struct proc_field *fp; 1390 1391 /* check for null handle */ 1392 if (handle == NULL) 1393 { 1394 return(""); 1395 } 1396 1397 /* remember how many columns there are on the display */ 1398 cols = display_columns(); 1399 1400 /* mode & threads dictate format */ 1401 fi = display_fields = 1402 sel->mode == 0 ? 1403 (sel->threads == 0 ? mode0_display : mode0thr_display) : 1404 mode1_display; 1405 1406 /* set username field correctly */ 1407 if (!sel->usernames) 1408 { 1409 /* display uids */ 1410 field_subst(fi, FIELD_USERNAME, FIELD_UID); 1411 } 1412 else 1413 { 1414 /* display usernames */ 1415 field_subst(fi, FIELD_UID, FIELD_USERNAME); 1416 1417 /* we also need to determine the longest username for column width */ 1418 /* calculate namelength from first "count" processes */ 1419 kip = ((struct handle *)handle)->next_proc; 1420 n = ((struct handle *)handle)->remaining; 1421 if (n > count) 1422 n = count; 1423 namelength = 0; 1424 while (n-- > 0) 1425 { 1426 w = strlen(username(PRUID(*kip))); 1427 if (w > namelength) namelength = w; 1428 kip++; 1429 } 1430 dprintf("format_process_header: namelength %d\n", namelength); 1431 1432 /* place it in bounds */ 1433 if (namelength < 8) 1434 { 1435 namelength = 8; 1436 } 1437 1438 /* set the column width */ 1439 proc_field[FIELD_USERNAME].width = username_length = namelength; 1440 } 1441 1442 /* walk thru fields and construct header */ 1443 /* are we worried about overflow??? */ 1444 p = p_header; 1445 while (*fi != -1) 1446 { 1447 fp = &(proc_field[*fi++]); 1448 if (fp->min_screenwidth <= cols) 1449 { 1450 p += sprintf(p, fp->rjust ? "%*s" : "%-*s", fp->width, fp->name); 1451 *p++ = ' '; 1452 } 1453 } 1454 *--p = '\0'; 1455 1456 return p_header; 1457} 1458 1459static char fmt[MAX_COLS]; /* static area where result is built */ 1460 1461char * 1462format_next_process(caddr_t handle, char *(*get_userid)(int)) 1463 1464{ 1465 struct kinfo_proc *pp; 1466 struct handle *hp; 1467 struct proc_field *fp; 1468 int *fi; 1469 int i; 1470 int cols; 1471 char *p; 1472 int len; 1473 int x; 1474 1475 /* find and remember the next proc structure */ 1476 hp = (struct handle *)handle; 1477 pp = *(hp->next_proc++); 1478 hp->remaining--; 1479 1480 /* mode & threads dictate format */ 1481 fi = display_fields; 1482 1483 /* screen width is a consideration, too */ 1484 cols = display_columns(); 1485 1486 /* build output by field */ 1487 p = fmt; 1488 len = MAX_COLS; 1489 while ((i = *fi++) != -1) 1490 { 1491 fp = &(proc_field[i]); 1492 if (len > 0 && fp->min_screenwidth <= cols) 1493 { 1494 x = (*(fp->format))(p, len, pp); 1495 if (x >= len) 1496 { 1497 dprintf("format_next_process: formatter overflow: x %d, len %d, p %08x => %08x, fmt %08x - %08x\n", 1498 x, len, p, p + len, fmt, fmt + sizeof(fmt)); 1499 p += len; 1500 len = 0; 1501 } 1502 else 1503 { 1504 p += x; 1505 *p++ = ' '; 1506 len -= x + 1; 1507 } 1508 } 1509 } 1510 *--p = '\0'; 1511 1512 /* return the result */ 1513 return(fmt); 1514} 1515 1516/* comparison routines for qsort */ 1517 1518/* 1519 * proc_compare - comparison function for "qsort" 1520 * Compares the resource consumption of two processes using five 1521 * distinct keys. The keys (in descending order of importance) are: 1522 * percent cpu, cpu ticks, state, resident set size, total virtual 1523 * memory usage. The process states are ordered as follows (from least 1524 * to most important): WAIT, zombie, sleep, stop, start, run. The 1525 * array declaration below maps a process state index into a number 1526 * that reflects this ordering. 1527 */ 1528 1529static unsigned char sorted_state[] = 1530{ 1531 0, /* not used */ 1532 3, /* sleep */ 1533 1, /* ABANDONED (WAIT) */ 1534 6, /* run */ 1535 5, /* start */ 1536 2, /* zombie */ 1537 4 /* stop */ 1538}; 1539 1540 1541#define ORDERKEY_PCTCPU \ 1542 if (lresult = (long) PPCPU(p2) - (long) PPCPU(p1), \ 1543 (result = lresult > 0 ? 1 : lresult < 0 ? -1 : 0) == 0) 1544 1545#define ORDERKEY_CPTICKS \ 1546 if ((result = PP(p2, runtime) > PP(p1, runtime) ? 1 : \ 1547 PP(p2, runtime) < PP(p1, runtime) ? -1 : 0) == 0) 1548 1549#define ORDERKEY_STATE \ 1550 if ((result = sorted_state[(unsigned char) PP(p2, stat)] - \ 1551 sorted_state[(unsigned char) PP(p1, stat)]) == 0) 1552 1553#if OSMAJOR <= 4 1554#define ORDERKEY_PRIO \ 1555 if ((result = PP(p2, priority) - PP(p1, priority)) == 0) 1556#else 1557#define ORDERKEY_PRIO \ 1558 if ((result = PP(p2, pri.pri_level) - PP(p1, pri.pri_level)) == 0) 1559#endif 1560 1561#define ORDERKEY_RSSIZE \ 1562 if ((result = VP(p2, rssize) - VP(p1, rssize)) == 0) 1563 1564#define ORDERKEY_MEM \ 1565 if ( (result = PROCSIZE(p2) - PROCSIZE(p1)) == 0 ) 1566 1567#define ORDERKEY_IO \ 1568 if ( (result = SP(p2, totalio) - SP(p1, totalio)) == 0) 1569 1570#define ORDERKEY_PID \ 1571 if ( (result = PP(p1, pid) - PP(p2, pid)) == 0) 1572 1573/* compare_cpu - the comparison function for sorting by cpu percentage */ 1574 1575int 1576proc_compare(struct proc **pp1, struct proc **pp2) 1577 1578{ 1579 struct kinfo_proc *p1; 1580 struct kinfo_proc *p2; 1581 int result; 1582 pctcpu lresult; 1583 1584 /* remove one level of indirection */ 1585 p1 = *(struct kinfo_proc **) pp1; 1586 p2 = *(struct kinfo_proc **) pp2; 1587 1588 ORDERKEY_PCTCPU 1589 ORDERKEY_CPTICKS 1590 ORDERKEY_STATE 1591 ORDERKEY_PRIO 1592 ORDERKEY_RSSIZE 1593 ORDERKEY_MEM 1594 ; 1595 1596 return(result); 1597} 1598 1599/* compare_size - the comparison function for sorting by total memory usage */ 1600 1601int 1602compare_size(struct proc **pp1, struct proc **pp2) 1603 1604{ 1605 struct kinfo_proc *p1; 1606 struct kinfo_proc *p2; 1607 int result; 1608 pctcpu lresult; 1609 1610 /* remove one level of indirection */ 1611 p1 = *(struct kinfo_proc **) pp1; 1612 p2 = *(struct kinfo_proc **) pp2; 1613 1614 ORDERKEY_MEM 1615 ORDERKEY_RSSIZE 1616 ORDERKEY_PCTCPU 1617 ORDERKEY_CPTICKS 1618 ORDERKEY_STATE 1619 ORDERKEY_PRIO 1620 ; 1621 1622 return(result); 1623} 1624 1625/* compare_res - the comparison function for sorting by resident set size */ 1626 1627int 1628compare_res(struct proc **pp1, struct proc **pp2) 1629 1630{ 1631 struct kinfo_proc *p1; 1632 struct kinfo_proc *p2; 1633 int result; 1634 pctcpu lresult; 1635 1636 /* remove one level of indirection */ 1637 p1 = *(struct kinfo_proc **) pp1; 1638 p2 = *(struct kinfo_proc **) pp2; 1639 1640 ORDERKEY_RSSIZE 1641 ORDERKEY_MEM 1642 ORDERKEY_PCTCPU 1643 ORDERKEY_CPTICKS 1644 ORDERKEY_STATE 1645 ORDERKEY_PRIO 1646 ; 1647 1648 return(result); 1649} 1650 1651/* compare_time - the comparison function for sorting by total cpu time */ 1652 1653int 1654compare_time(struct proc **pp1, struct proc **pp2) 1655 1656{ 1657 struct kinfo_proc *p1; 1658 struct kinfo_proc *p2; 1659 int result; 1660 pctcpu lresult; 1661 1662 /* remove one level of indirection */ 1663 p1 = *(struct kinfo_proc **) pp1; 1664 p2 = *(struct kinfo_proc **) pp2; 1665 1666 ORDERKEY_CPTICKS 1667 ORDERKEY_PCTCPU 1668 ORDERKEY_STATE 1669 ORDERKEY_PRIO 1670 ORDERKEY_RSSIZE 1671 ORDERKEY_MEM 1672 ; 1673 1674 return(result); 1675 } 1676 1677/* compare_prio - the comparison function for sorting by priority */ 1678 1679int 1680compare_prio(struct proc **pp1, struct proc **pp2) 1681 1682{ 1683 struct kinfo_proc *p1; 1684 struct kinfo_proc *p2; 1685 int result; 1686 pctcpu lresult; 1687 1688 /* remove one level of indirection */ 1689 p1 = *(struct kinfo_proc **) pp1; 1690 p2 = *(struct kinfo_proc **) pp2; 1691 1692 ORDERKEY_PRIO 1693 ORDERKEY_CPTICKS 1694 ORDERKEY_PCTCPU 1695 ORDERKEY_STATE 1696 ORDERKEY_RSSIZE 1697 ORDERKEY_MEM 1698 ; 1699 1700 return(result); 1701} 1702 1703/* compare_io - the comparison function for sorting by io count */ 1704 1705int 1706compare_io(struct proc **pp1, struct proc **pp2) 1707 1708{ 1709 struct kinfo_proc *p1; 1710 struct kinfo_proc *p2; 1711 int result; 1712 pctcpu lresult; 1713 1714 /* remove one level of indirection */ 1715 p1 = *(struct kinfo_proc **) pp1; 1716 p2 = *(struct kinfo_proc **) pp2; 1717 1718 ORDERKEY_IO 1719 ORDERKEY_PCTCPU 1720 ORDERKEY_CPTICKS 1721 ORDERKEY_STATE 1722 ORDERKEY_PRIO 1723 ORDERKEY_RSSIZE 1724 ORDERKEY_MEM 1725 ; 1726 1727 return(result); 1728} 1729 1730/* compare_pid - the comparison function for sorting by process id */ 1731 1732int 1733compare_pid(struct proc **pp1, struct proc **pp2) 1734 1735{ 1736 struct kinfo_proc *p1; 1737 struct kinfo_proc *p2; 1738 int result; 1739 1740 /* remove one level of indirection */ 1741 p1 = *(struct kinfo_proc **) pp1; 1742 p2 = *(struct kinfo_proc **) pp2; 1743 1744 ORDERKEY_PID 1745 ; 1746 1747 return(result); 1748} 1749 1750/* 1751 * proc_owner(pid) - returns the uid that owns process "pid", or -1 if 1752 * the process does not exist. 1753 * It is EXTREMLY IMPORTANT that this function work correctly. 1754 * If top runs setuid root (as in SVR4), then this function 1755 * is the only thing that stands in the way of a serious 1756 * security problem. It validates requests for the "kill" 1757 * and "renice" commands. 1758 */ 1759 1760int 1761proc_owner(int pid) 1762 1763{ 1764 int cnt; 1765 struct kinfo_proc **prefp; 1766 struct kinfo_proc *pp; 1767 1768 prefp = pref; 1769 cnt = pref_len; 1770 while (--cnt >= 0) 1771 { 1772 pp = *prefp++; 1773 if (PP(pp, pid) == (pid_t)pid) 1774 { 1775 return((int)PRUID(pp)); 1776 } 1777 } 1778 return(-1); 1779} 1780 1781