Home | History | Annotate | Line # | Download | only in machine
      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 (at) tamu.edu>
     47  * Order support added by Alexey Klimkin <kad (at) 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"
     82 extern char *myname;
     83 
     84 /*=PROCESS INFORMATION==================================================*/
     85 
     86 struct 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
    110 static char *state_abbrev[NPROCSTATES+1] =
    111 {
    112     "", "run", "sleep", "disk", "zomb", "stop", "swap",
    113     NULL
    114 };
    115 
    116 static char *procstatenames[NPROCSTATES+1] =
    117 {
    118     "", " running, ", " sleeping, ", " uninterruptable, ",
    119     " zombie, ", " stopped, ", " swapping, ",
    120     NULL
    121 };
    122 
    123 #define NCPUSTATES 4
    124 static 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
    136 static 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
    146 static char *swapnames[NSWAPSTATS+1] =
    147 {
    148     "K used, ", "K free, ", "K cached",
    149     NULL
    150 };
    151 
    152 static 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 */
    156 char *ordernames[] =
    157 {"cpu", "size", "res", "time", "command", NULL};
    158 
    159 /* forward definitions for comparison functions */
    160 int compare_cpu();
    161 int compare_size();
    162 int compare_res();
    163 int compare_time();
    164 int compare_cmd();
    165 
    166 int (*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 
    178 static long cp_time[NCPUSTATES];
    179 static long cp_old[NCPUSTATES];
    180 static long cp_diff[NCPUSTATES];
    181 
    182 /* for calculating the exponential average */
    183 
    184 static 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)
    191 static struct top_proc *ptable[HASH_SIZE];
    192 static struct top_proc **pactive;
    193 static struct top_proc **nextactive;
    194 static unsigned int activesize = 0;
    195 static time_t boottime = -1;
    196 
    197 /* these are for passing data back to the machine independant portion */
    198 
    199 static int cpu_states[NCPUSTATES];
    200 static int process_states[NPROCSTATES];
    201 static long memory_stats[NMEMSTATS];
    202 static 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 
    211 static inline char *
    212 skip_ws(const char *p)
    213 {
    214     while (isspace(*p)) p++;
    215     return (char *)p;
    216 }
    217 
    218 static inline char *
    219 skip_token(const char *p)
    220 {
    221     while (isspace(*p)) p++;
    222     while (*p && !isspace(*p)) p++;
    223     return (char *)p;
    224 }
    225 
    226 static void
    227 xfrm_cmdline(char *p, int len)
    228 {
    229     while (--len > 0)
    230     {
    231 	if (*p == '\0')
    232 	{
    233 	    *p = ' ';
    234 	}
    235 	p++;
    236     }
    237 }
    238 
    239 static void
    240 update_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 
    265 static struct top_proc *freelist = NULL;
    266 static struct top_proc *procblock = NULL;
    267 static struct top_proc *procmax = NULL;
    268 
    269 static struct top_proc *
    270 new_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 
    304 static void
    305 free_proc(struct top_proc *proc)
    306 {
    307     proc->next = freelist;
    308     freelist = proc;
    309 }
    310 
    311 
    312 int
    313 machine_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 
    372 void
    373 get_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 
    531 static void
    532 read_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 
    657 caddr_t
    658 get_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 
    870 char *
    871 format_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 
    884 char *
    885 format_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 
    940 unsigned 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 
    954 int
    955 compare_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 
    981 int
    982 compare_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 
   1008 int
   1009 compare_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 
   1035 int
   1036 compare_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 
   1062 int
   1063 compare_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 
   1099 int
   1100 proc_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