source: trunk/third/top/machine/m_linux.c @ 16185

Revision 16185, 17.6 KB checked in by ghudson, 23 years ago (diff)
This commit was generated by cvs2svn to compensate for changes in r16184, which included commits to RCS files with non-trunk default branches.
Line 
1/*
2 * top - a top users display for Unix
3 *
4 * SYNOPSIS:  Linux 1.2.x, 1.3.x 2.0.x, using the /proc filesystem
5 *
6 * DESCRIPTION:
7 * This is the machine-dependent module for Linux 1.2.x, 1.3.x or 2.0.x.
8 *
9 * LIBS:
10 *
11 * CFLAGS: -DHAVE_GETOPT -DHAVE_STRERROR -DORDER
12 *
13 * TERMCAP: -lcurses
14 *
15 * AUTHOR: Richard Henderson <rth@tamu.edu>
16 * Order support added by Alexey Klimkin <kad@klon.tme.mcst.ru>
17 */
18
19#include "top.h"
20#include "machine.h"
21#include "utils.h"
22
23#include <sys/types.h>
24#include <stdio.h>
25#include <fcntl.h>
26#include <unistd.h>
27#include <stdlib.h>
28#include <errno.h>
29#include <dirent.h>
30#include <string.h>
31#include <math.h>
32#include <ctype.h>
33#include <sys/time.h>
34#include <sys/stat.h>
35#include <sys/vfs.h>
36
37#include <sys/param.h>          /* for HZ */
38#include <asm/page.h>           /* for PAGE_SHIFT */
39#include <linux/tasks.h>        /* for NR_TASKS */
40
41#if 0
42#include <linux/proc_fs.h>      /* for PROC_SUPER_MAGIC */
43#else
44#define PROC_SUPER_MAGIC 0x9fa0
45#endif
46
47#define PROCFS "/proc"
48extern char *myname;
49extern uid_t proc_owner(pid_t pid);
50
51/*=PROCESS INFORMATION==================================================*/
52
53struct top_proc
54{
55    pid_t pid;
56    uid_t uid;
57    char name[64];
58    int pri, nice;
59    unsigned long size, rss;    /* in k */
60    int state;
61    unsigned long time;
62    double pcpu, wcpu;
63};
64   
65
66/*=STATE IDENT STRINGS==================================================*/
67
68#define NPROCSTATES 7
69static char *state_abbrev[NPROCSTATES+1] =
70{
71    "", "run", "sleep", "disk", "zomb", "stop", "swap",
72    NULL
73};
74
75static char *procstatenames[NPROCSTATES+1] =
76{
77    "", " running, ", " sleeping, ", " uninterruptable, ",
78    " zombie, ", " stopped, ", " swapping, ",
79    NULL
80};
81
82#define NCPUSTATES 4
83static char *cpustatenames[NCPUSTATES+1] =
84{
85    "user", "nice", "system", "idle",
86    NULL
87};
88
89#define NMEMSTATS 6
90static char *memorynames[NMEMSTATS+1] =
91{
92    "K used, ", "K free, ", "K shd, ", "K buf  Swap: ",
93    "K used, ", "K free",
94    NULL
95};
96
97static char fmt_header[] =
98"  PID X        PRI NICE  SIZE   RES STATE   TIME   WCPU    CPU COMMAND";
99
100#ifdef ORDER
101/* these are names given to allowed sorting orders -- first is default */
102char *ordernames[] =
103{"cpu", "size", "res", "time", NULL};
104
105/* forward definitions for comparison functions */
106int compare_cpu();
107int compare_size();
108int compare_res();
109int compare_time();
110
111int (*proc_compares[])() = {
112    compare_cpu,
113    compare_size,
114    compare_res,
115    compare_time,
116    NULL };
117#endif
118       
119/*=SYSTEM STATE INFO====================================================*/
120
121/* these are for calculating cpu state percentages */
122
123static long cp_time[NCPUSTATES];
124static long cp_old[NCPUSTATES];
125static long cp_diff[NCPUSTATES];
126
127/* for calculating the exponential average */
128
129static struct timeval lasttime;
130
131/* these are for keeping track of processes */
132
133#define HASH_SIZE       (NR_TASKS * 3 / 2)
134static struct top_proc ptable[HASH_SIZE];
135static struct top_proc *pactive[NR_TASKS];
136static struct top_proc **nextactive;
137
138/* these are for passing data back to the machine independant portion */
139
140static int cpu_states[NCPUSTATES];
141static int process_states[NPROCSTATES];
142static int memory_stats[NMEMSTATS];
143
144/* usefull macros */
145#define bytetok(x)      (((x) + 512) >> 10)
146#define pagetok(x)      ((x) << (PAGE_SHIFT - 10))
147#define HASH(x)         (((x) * 1686629713U) % HASH_SIZE)
148
149/*======================================================================*/
150
151static inline char *
152skip_ws(const char *p)
153{
154    while (isspace(*p)) p++;
155    return (char *)p;
156}
157   
158static inline char *
159skip_token(const char *p)
160{
161    while (isspace(*p)) p++;
162    while (*p && !isspace(*p)) p++;
163    return (char *)p;
164}
165
166 
167int
168machine_init(statics)
169    struct statics *statics;
170{
171    /* make sure the proc filesystem is mounted */
172    {
173        struct statfs sb;
174        if (statfs(PROCFS, &sb) < 0 || sb.f_type != PROC_SUPER_MAGIC)
175        {
176            fprintf(stderr, "%s: proc filesystem not mounted on " PROCFS "\n",
177                    myname);
178            return -1;
179        }
180    }
181
182    /* chdir to the proc filesystem to make things easier */
183    chdir(PROCFS);
184
185    /* initialize the process hash table */
186    {
187        int i;
188        for (i = 0; i < HASH_SIZE; ++i)
189            ptable[i].pid = -1;
190    }
191
192    /* fill in the statics information */
193    statics->procstate_names = procstatenames;
194    statics->cpustate_names = cpustatenames;
195    statics->memory_names = memorynames;
196#ifdef ORDER
197    statics->order_names = ordernames;
198#endif
199
200    /* all done! */
201    return 0;
202}
203
204
205void
206get_system_info(info)
207    struct system_info *info;
208{
209    char buffer[4096+1];
210    int fd, len;
211    char *p;
212
213    /* get load averages */
214    {
215        fd = open("loadavg", O_RDONLY);
216        len = read(fd, buffer, sizeof(buffer)-1);
217        close(fd);
218        buffer[len] = '\0';
219
220        info->load_avg[0] = strtod(buffer, &p);
221        info->load_avg[1] = strtod(p, &p);
222        info->load_avg[2] = strtod(p, &p);
223        p = skip_token(p);                      /* skip running/tasks */
224        p = skip_ws(p);
225        if (*p)
226            info->last_pid = atoi(p);
227        else
228            info->last_pid = -1;
229    }
230
231    /* get the cpu time info */
232    {
233        fd = open("stat", O_RDONLY);
234        len = read(fd, buffer, sizeof(buffer)-1);
235        close(fd);
236        buffer[len] = '\0';
237
238        p = skip_token(buffer);                 /* "cpu" */
239        cp_time[0] = strtoul(p, &p, 0);
240        cp_time[1] = strtoul(p, &p, 0);
241        cp_time[2] = strtoul(p, &p, 0);
242        cp_time[3] = strtoul(p, &p, 0);
243
244        /* convert cp_time counts to percentages */
245        percentages(4, cpu_states, cp_time, cp_old, cp_diff);
246    }
247
248    /* get system wide memory usage */
249    {
250        char *p;
251
252        fd = open("meminfo", O_RDONLY);
253        len = read(fd, buffer, sizeof(buffer)-1);
254        close(fd);
255        buffer[len] = '\0';
256
257        /* be prepared for extra columns to appear be seeking
258           to ends of lines */
259
260        p = strchr(buffer, '\n');
261        p = skip_token(p);                      /* "Mem:" */
262        p = skip_token(p);                      /* total memory */
263        memory_stats[0] = strtoul(p, &p, 10);
264        memory_stats[1] = strtoul(p, &p, 10);
265        memory_stats[2] = strtoul(p, &p, 10);
266        memory_stats[3] = strtoul(p, &p, 10);
267
268        p = strchr(p, '\n');
269        p = skip_token(p);                      /* "Swap:" */
270        p = skip_token(p);                      /* total swap */
271        memory_stats[4] = strtoul(p, &p, 10);
272        memory_stats[5] = strtoul(p, &p, 10);
273
274        memory_stats[0] = bytetok(memory_stats[0]);
275        memory_stats[1] = bytetok(memory_stats[1]);
276        memory_stats[2] = bytetok(memory_stats[2]);
277        memory_stats[3] = bytetok(memory_stats[3]);
278        memory_stats[4] = bytetok(memory_stats[4]);
279        memory_stats[5] = bytetok(memory_stats[5]);
280    }
281
282    /* set arrays and strings */
283    info->cpustates = cpu_states;
284    info->memory = memory_stats;
285}
286
287
288static void
289read_one_proc_stat(pid_t pid, struct top_proc *proc)
290{
291    char buffer[4096], *p;
292
293    /* grab the proc stat info in one go */
294    {
295        int fd, len;
296
297        sprintf(buffer, "%d/stat", pid);
298
299        fd = open(buffer, O_RDONLY);
300        len = read(fd, buffer, sizeof(buffer)-1);
301        close(fd);
302
303        buffer[len] = '\0';
304    }
305
306    proc->uid = proc_owner(pid);
307
308    /* parse out the status */
309   
310    p = buffer;
311    p = strchr(p, '(')+1;                       /* skip pid */
312    {
313        char *q = strrchr(p, ')');
314        int len = q-p;
315        if (len >= sizeof(proc->name))
316            len = sizeof(proc->name)-1;
317        memcpy(proc->name, p, len);
318        proc->name[len] = 0;
319        p = q+1;
320    }
321
322    p = skip_ws(p);
323    switch (*p++)
324    {
325      case 'R': proc->state = 1; break;
326      case 'S': proc->state = 2; break;
327      case 'D': proc->state = 3; break;
328      case 'Z': proc->state = 4; break;
329      case 'T': proc->state = 5; break;
330      case 'W': proc->state = 6; break;
331    }
332   
333    p = skip_token(p);                          /* skip ppid */
334    p = skip_token(p);                          /* skip pgrp */
335    p = skip_token(p);                          /* skip session */
336    p = skip_token(p);                          /* skip tty */
337    p = skip_token(p);                          /* skip tty pgrp */
338    p = skip_token(p);                          /* skip flags */
339    p = skip_token(p);                          /* skip min flt */
340    p = skip_token(p);                          /* skip cmin flt */
341    p = skip_token(p);                          /* skip maj flt */
342    p = skip_token(p);                          /* skip cmaj flt */
343   
344    proc->time = strtoul(p, &p, 10);            /* utime */
345    proc->time += strtoul(p, &p, 10);           /* stime */
346
347    p = skip_token(p);                          /* skip cutime */
348    p = skip_token(p);                          /* skip cstime */
349
350    proc->pri = strtol(p, &p, 10);              /* priority */
351    proc->nice = strtol(p, &p, 10);             /* nice */
352
353    p = skip_token(p);                          /* skip timeout */
354    p = skip_token(p);                          /* skip it_real_val */
355    p = skip_token(p);                          /* skip start_time */
356
357    proc->size = bytetok(strtoul(p, &p, 10));   /* vsize */
358    proc->rss = pagetok(strtoul(p, &p, 10));    /* rss */
359
360#if 0
361    /* for the record, here are the rest of the fields */
362    p = skip_token(p);                          /* skip rlim */
363    p = skip_token(p);                          /* skip start_code */
364    p = skip_token(p);                          /* skip end_code */
365    p = skip_token(p);                          /* skip start_stack */
366    p = skip_token(p);                          /* skip sp */
367    p = skip_token(p);                          /* skip pc */
368    p = skip_token(p);                          /* skip signal */
369    p = skip_token(p);                          /* skip sigblocked */
370    p = skip_token(p);                          /* skip sigignore */
371    p = skip_token(p);                          /* skip sigcatch */
372    p = skip_token(p);                          /* skip wchan */
373#endif
374}
375
376
377caddr_t
378get_process_info(struct system_info *si,
379                 struct process_select *sel,
380                 int (*compare)())
381{
382    struct timeval thistime;
383    double timediff, alpha, beta;
384
385    /* calculate the time difference since our last check */
386    gettimeofday(&thistime, 0);
387    if (lasttime.tv_sec)
388    {
389        timediff = ((thistime.tv_sec - lasttime.tv_sec) +
390                    (thistime.tv_usec - lasttime.tv_usec) * 1e-6);
391    }
392    else
393        timediff = 1e9;
394    lasttime = thistime;
395
396    /* calculate constants for the exponental average */
397    if (timediff < 30.0)
398    {
399        alpha = 0.5 * (timediff / 30.0);
400        beta = 1.0 - alpha;
401    }
402    else
403        alpha = beta = 0.5;
404    timediff *= HZ;  /* convert to ticks */
405
406    /* mark all hash table entries as not seen */
407    {
408        int i;
409        for (i = 0; i < HASH_SIZE; ++i)
410            ptable[i].state = 0;
411    }
412
413    /* read the process information */
414    {
415        DIR *dir = opendir(".");
416        struct dirent *ent;
417        int total_procs = 0;
418        struct top_proc **active = pactive;
419
420        int show_idle = sel->idle;
421        int show_uid = sel->uid != -1;
422
423        memset(process_states, 0, sizeof(process_states));
424
425        while ((ent = readdir(dir)) != NULL)
426        {
427            struct top_proc *proc;
428            pid_t pid;
429            unsigned long otime;
430
431            if (!isdigit(ent->d_name[0]))
432                continue;
433
434            pid = atoi(ent->d_name);
435
436            /* look up hash table entry */
437            proc = &ptable[HASH(pid)];
438            while (proc->pid != pid && proc->pid != -1)
439            {
440                if (++proc == ptable+HASH_SIZE)
441                    proc = ptable;
442            }
443
444            otime = proc->time;
445
446            read_one_proc_stat(pid, proc);
447
448            if (proc->state == 0)
449                continue;
450
451            total_procs++;
452            process_states[proc->state]++;
453
454            if (proc->pid == -1)
455            {
456                proc->pid = pid;
457                proc->wcpu = proc->pcpu = proc->time / timediff;
458            }
459            else
460            {
461                proc->pcpu = (proc->time - otime) / timediff;
462                proc->wcpu = proc->pcpu * alpha + proc->wcpu * beta;
463            }
464
465            if ((show_idle || proc->state == 1 || proc->pcpu) &&
466                (!show_uid || proc->uid == sel->uid))
467            {
468                *active++ = proc;
469            }
470        }
471        closedir(dir);
472
473        si->p_active = active - pactive;
474        si->p_total = total_procs;
475        si->procstates = process_states;
476    }
477
478    /* flush old hash table entries */
479    {
480        int i;
481        for (i = 0; i < HASH_SIZE; ++i)
482            if (ptable[i].state == 0)
483                ptable[i].pid = -1;
484    }
485
486    /* if requested, sort the "active" procs */
487    if (compare && si->p_active)
488        qsort(pactive, si->p_active, sizeof(struct top_proc *), compare);
489
490    /* don't even pretend that the return value thing here isn't bogus */
491    nextactive = pactive;
492    return (caddr_t)0;
493}
494
495
496char *
497format_header(uname_field)
498    char *uname_field;
499{
500    int uname_len = strlen(uname_field);
501    if (uname_len > 8)
502        uname_len = 8;
503
504    memcpy(strchr(fmt_header, 'X'), uname_field, uname_len);
505
506    return fmt_header;
507}
508
509
510char *
511format_next_process(handle, get_userid)
512     caddr_t handle;
513     char *(*get_userid)();
514{
515    static char fmt[128];       /* static area where result is built */
516    struct top_proc *p = *nextactive++;
517
518    sprintf(fmt,
519            "%5d %-8.8s %3d %4d %5s %5s %-5s %6s %5.2f%% %5.2f%% %.14s",
520            p->pid,
521            (*get_userid)(p->uid),
522            p->pri,
523            p->nice,
524            format_k(p->size),
525            format_k(p->rss),
526            state_abbrev[p->state],
527            format_time(p->time / HZ),
528            p->wcpu * 100.0,
529            p->pcpu * 100.0,
530            p->name);
531
532    /* return the result */
533    return (fmt);
534}
535
536#ifdef ORDER
537/* comparison routines for qsort */
538
539/*
540 * There are currently four possible comparison routines.  main selects
541 * one of these by indexing in to the array proc_compares.
542 *
543 * Possible keys are defined as macros below.  Currently these keys are
544 * defined:  percent cpu, cpu ticks, process state, resident set size,
545 * total virtual memory usage.  The process states are ordered as follows
546 * (from least to most important):  WAIT, zombie, sleep, stop, start, run.
547 * The array declaration below maps a process state index into a number
548 * that reflects this ordering.
549 */
550
551/* First, the possible comparison keys.  These are defined in such a way
552   that they can be merely listed in the source code to define the actual
553   desired ordering.
554 */
555
556#define ORDERKEY_PCTCPU  if (dresult = p2->pcpu - p1->pcpu,\
557                                                         (result = dresult > 0.0 ? 1 : dresult < 0.0 ? -1 : 0) == 0)
558#define ORDERKEY_CPTICKS if ((result = p2->time - p1->time) == 0)
559#define ORDERKEY_STATE   if ((result = (sort_state[p2->state] - \
560                                                                                sort_state[p1->state])) == 0)
561#define ORDERKEY_PRIO    if ((result = p2->pri - p1->pri) == 0)
562#define ORDERKEY_RSSIZE  if ((result = p2->rss - p1->rss) == 0)
563#define ORDERKEY_MEM     if ((result = p2->size - p1->size) == 0)
564
565/* Now the array that maps process state to a weight */
566
567unsigned char sort_state[] =
568{
569        0,      /* empty */
570        6,      /* run */
571        3,      /* sleep */
572        5,      /* disk wait */
573        1,      /* zombie */
574        2,      /* stop */
575        4       /* swap */
576};
577
578
579/* compare_cpu - the comparison function for sorting by cpu percentage */
580
581int
582compare_cpu (
583               struct top_proc **pp1,
584               struct top_proc **pp2)
585  {
586    register struct top_proc *p1;
587    register struct top_proc *p2;
588    register long result;
589    double dresult;
590
591    /* remove one level of indirection */
592    p1 = *pp1;
593    p2 = *pp2;
594
595    ORDERKEY_PCTCPU
596    ORDERKEY_CPTICKS
597    ORDERKEY_STATE
598    ORDERKEY_PRIO
599    ORDERKEY_RSSIZE
600    ORDERKEY_MEM
601    ;
602
603    return result == 0 ? 0 : result < 0 ? -1 : 1;
604  }
605
606/* compare_size - the comparison function for sorting by total memory usage */
607
608int
609compare_size (
610               struct top_proc **pp1,
611               struct top_proc **pp2)
612  {
613    register struct top_proc *p1;
614    register struct top_proc *p2;
615    register long result;
616    double dresult;
617
618    /* remove one level of indirection */
619    p1 = *pp1;
620    p2 = *pp2;
621
622    ORDERKEY_MEM
623    ORDERKEY_RSSIZE
624    ORDERKEY_PCTCPU
625    ORDERKEY_CPTICKS
626    ORDERKEY_STATE
627    ORDERKEY_PRIO
628    ;
629
630    return result == 0 ? 0 : result < 0 ? -1 : 1;
631  }
632
633/* compare_res - the comparison function for sorting by resident set size */
634
635int
636compare_res (
637               struct top_proc **pp1,
638               struct top_proc **pp2)
639  {
640    register struct top_proc *p1;
641    register struct top_proc *p2;
642    register long result;
643    double dresult;
644
645    /* remove one level of indirection */
646    p1 = *pp1;
647    p2 = *pp2;
648
649    ORDERKEY_RSSIZE
650    ORDERKEY_MEM
651    ORDERKEY_PCTCPU
652    ORDERKEY_CPTICKS
653    ORDERKEY_STATE
654    ORDERKEY_PRIO
655    ;
656
657    return result == 0 ? 0 : result < 0 ? -1 : 1;
658  }
659
660/* compare_time - the comparison function for sorting by total cpu time */
661
662int
663compare_time (
664               struct top_proc **pp1,
665               struct top_proc **pp2)
666  {
667    register struct top_proc *p1;
668    register struct top_proc *p2;
669    register long result;
670    double dresult;
671
672    /* remove one level of indirection */
673    p1 = *pp1;
674    p2 = *pp2;
675
676    ORDERKEY_CPTICKS
677    ORDERKEY_PCTCPU
678    ORDERKEY_STATE
679    ORDERKEY_PRIO
680    ORDERKEY_MEM
681    ORDERKEY_RSSIZE
682    ;
683
684    return result == 0 ? 0 : result < 0 ? -1 : 1;
685  }
686
687#else /* ORDER */
688/*
689 *  proc_compare - comparison function for "qsort"
690 *      Compares the resource consumption of two processes using five
691 *      distinct keys.  The keys (in descending order of importance) are:
692 *      percent cpu, cpu ticks, state, resident set size, total virtual
693 *      memory usage.  The process states are ordered as follows (from least
694 *      to most important):  WAIT, zombie, sleep, stop, start, run.  The
695 *      array declaration below maps a process state index into a number
696 *      that reflects this ordering.
697 */
698
699
700int
701proc_compare (pp1, pp2)
702    struct top_proc **pp1, **pp2;
703{
704    static unsigned char sort_state[] =
705    {
706        0,      /* empty */
707        6,      /* run */
708        3,      /* sleep */
709        5,      /* disk wait */
710        1,      /* zombie */
711        2,      /* stop */
712        4       /* swap */
713    };
714
715    struct top_proc *p1, *p2;
716    int result;
717    double dresult;
718
719    /* remove one level of indirection */
720    p1 = *pp1;
721    p2 = *pp2;
722
723    /* compare percent cpu (pctcpu) */
724    dresult = p2->pcpu - p1->pcpu;
725    if (dresult != 0.0)
726        return dresult > 0.0 ? 1 : -1;
727
728    /* use cputicks to break the tie */
729    if ((result = p2->time - p1->time) == 0)
730    {
731        /* use process state to break the tie */
732        if ((result = (sort_state[p2->state] - sort_state[p1->state])) == 0)
733        {
734            /* use priority to break the tie */
735            if ((result = p2->pri - p1->pri) == 0)
736            {
737                /* use resident set size (rssize) to break the tie */
738                if ((result = p2->rss - p1->rss) == 0)
739                {
740                    /* use total memory to break the tie */
741                    result = (p2->size - p1->size);
742                }
743            }
744        }
745    }
746
747    return result == 0 ? 0 : result < 0 ? -1 : 1;
748}
749#endif /* ORDER */
750
751/*
752 * proc_owner(pid) - returns the uid that owns process "pid", or -1 if
753 *              the process does not exist.
754 *              It is EXTREMLY IMPORTANT that this function work correctly.
755 *              If top runs setuid root (as in SVR4), then this function
756 *              is the only thing that stands in the way of a serious
757 *              security problem.  It validates requests for the "kill"
758 *              and "renice" commands.
759 */
760
761uid_t
762proc_owner(pid)
763    pid_t pid;
764{
765    struct stat sb;
766    char buffer[32];
767    sprintf(buffer, "%d", pid);
768
769    if (stat(buffer, &sb) < 0)
770        return -1;
771    else
772        return sb.st_uid;
773}
Note: See TracBrowser for help on using the repository browser.