source: trunk/third/top/machine/m_sunos5.c @ 16189

Revision 16189, 36.2 KB checked in by ghudson, 23 years ago (diff)
Merge with top 3.5beta12.
Line 
1/*
2 * top - a top users display for Unix
3 *
4 * SYNOPSIS:  Any Sun running SunOS 5.x (Solaris 2.x)
5 *
6 * DESCRIPTION:
7 * This is the machine-dependent module for SunOS 5.x (Solaris 2).
8 * There is some support for MP architectures.
9 * This makes top work on the following systems:
10 *         SunOS 5.0 (not tested)
11 *         SunOS 5.1
12 *         SunOS 5.2
13 *         SunOS 5.3
14 *         SunOS 5.4
15 *         SunOS 5.5
16 *         SunOS 5.6
17 *         SunOS 5.7
18 *
19 *     Tested on a SPARCclassic with SunOS 5.1, using gcc-2.3.3, and
20 *     SPARCsystem 600 with SunOS 5.2, using Sun C
21 *
22 * LIBS: -lelf -lkvm -lkstat
23 *
24 * CFLAGS: -DHAVE_GETOPT -DORDER -DHAVE_STRERROR
25 *
26 *
27 * AUTHORS:      Torsten Kasch          <torsten@techfak.uni-bielefeld.de>
28 *               Robert Boucher         <boucher@sofkin.ca>
29 * CONTRIBUTORS: Marc Cohen             <marc@aai.com>
30 *               Charles Hedrick        <hedrick@geneva.rutgers.edu>
31 *               William L. Jones       <jones@chpc>
32 *               Petri Kutvonen         <kutvonen@cs.helsinki.fi>
33 *               Casper Dik             <casper.dik@sun.com>
34 *               Tim Pugh               <tpugh@oce.orst.edu>
35 */
36
37#define _KMEMUSER
38
39#if (OSREV == 551)
40#undef OSREV
41#define OSREV 55
42#endif
43
44#define USE_NEW_PROC
45#if defined(USE_NEW_PROC) && OSREV >= 56
46#define _STRUCTURED_PROC 1
47#define prpsinfo psinfo
48#include <sys/procfs.h>
49#define pr_fill pr_nlwp
50/* These require an ANSI C compiler "Reisser cpp" doesn't like this */
51#define pr_state pr_lwp.pr_state
52#define pr_oldpri pr_lwp.pr_oldpri
53#define pr_nice pr_lwp.pr_nice
54#define pr_pri pr_lwp.pr_pri
55#define pr_onpro pr_lwp.pr_onpro
56#define ZOMBIE(p)       ((p)->pr_nlwp == 0)
57#define SIZE_K(p)       ((p)->pr_size)
58#define RSS_K(p)        ((p)->pr_rssize)
59#else
60#undef USE_NEW_PROC
61#define ZOMBIE(p)       ((p)->pr_zomb)
62#define SIZE_K(p)       ((p)->pr_bysize/1024)
63#define RSS_K(p)        ((p)->pr_byrssize/1024)
64#define pr_onpro        pr_filler[5]
65#endif
66
67#include "top.h"
68#include "machine.h"
69#include "utils.h"
70#include <stdio.h>
71#include <fcntl.h>
72#include <unistd.h>
73#include <stdlib.h>
74#include <errno.h>
75#include <dirent.h>
76#include <nlist.h>
77#include <string.h>
78#include <kvm.h>
79#include <sys/types.h>
80#include <sys/param.h>
81#include <sys/signal.h>
82#include <sys/fault.h>
83#include <sys/sysinfo.h>
84#include <sys/sysmacros.h>
85#include <sys/syscall.h>
86#include <sys/user.h>
87#include <sys/proc.h>
88#include <sys/procfs.h>
89#include <sys/vm.h>
90#include <sys/var.h>
91#include <sys/cpuvar.h>
92#include <sys/file.h>
93#include <sys/time.h>
94#include <sys/priocntl.h>
95#include <sys/tspriocntl.h>
96#include <sys/processor.h>
97#include <sys/swap.h>
98#include <vm/anon.h>
99#include <math.h>
100
101#if OSREV >= 53
102#define USE_KSTAT
103#endif
104#ifdef USE_KSTAT
105#include <kstat.h>
106/*
107 * Some kstats are fixed at 32 bits, these will be specified as ui32; some
108 * are "natural" size (32 bit on 32 bit Solaris, 64 on 64 bit Solaris
109 * we'll make those unsigned long)
110 * Older Solaris doesn't define KSTAT_DATA_UINT32, those are always 32 bit.
111 */
112# ifndef KSTAT_DATA_UINT32
113#  define ui32 ul
114# endif
115#endif
116
117#ifdef SC_AINFO
118#undef USE_ANONINFO                             /* Use swapctl() instead */
119#endif
120
121#define UNIX "/dev/ksyms"
122#define KMEM "/dev/kmem"
123#define PROCFS "/proc"
124#define CPUSTATES     5
125#ifndef PRIO_MIN
126#define PRIO_MIN        -20
127#endif
128#ifndef PRIO_MAX
129#define PRIO_MAX        20
130#endif
131
132#ifndef FSCALE
133#define FSHIFT  8               /* bits to right of fixed binary point */
134#define FSCALE  (1<<FSHIFT)
135#endif /* FSCALE */
136
137#define loaddouble(la) ((double)(la) / FSCALE)
138#define dbl_align(x)    (((unsigned long)(x)+(sizeof(double)-1)) & \
139                                                ~(sizeof(double)-1))
140#if (OSREV >= 54)
141    /*
142     * snarfed from <sys/procfs.h>:
143     * The following percent numbers are 16-bit binary
144     * fractions [0 .. 1] with the binary point to the
145     * right of the high-order bit (one == 0x8000)
146     */
147#define percent_cpu(pp) (((double)pp->pr_pctcpu)/0x8000*100)
148#define weighted_cpu(pp) (*(double *)dbl_align(pp->pr_filler))
149#else
150#define percent_cpu(pp) (*(double *)dbl_align(&pp->pr_filler[0]))
151#define weighted_cpu(pp) (*(double *)dbl_align(&pp->pr_filler[2]))
152#endif
153
154/* definitions for indices in the nlist array */
155#define X_V                      0
156#define X_MPID                   1
157#define X_ANONINFO               2
158#define X_MAXMEM                 3
159#define X_SWAPFS_MINFREE         4
160#define X_FREEMEM                5
161#define X_AVAILRMEM              6
162#define X_AVENRUN                7
163#define X_CPU                    8
164#define X_NPROC                  9
165#define X_NCPUS                 10
166
167static struct nlist nlst[] =
168{
169  {"v"},                        /* 0 */ /* replaced by dynamic allocation */
170  {"mpid"},                     /* 1 */
171#if OSREV >= 56
172  /* this structure really has some extra fields, but the first three match */
173  {"k_anoninfo"},               /* 2 */
174#else
175  {"anoninfo"},                 /* 2 */
176#endif
177  {"maxmem"},                   /* 3 */ /* use sysconf */
178  {"swapfs_minfree"},           /* 4 */ /* used only w/ USE_ANONINFO */
179  {"freemem"},                  /* 5 */ /* available from kstat >= 2.5 */
180  {"availrmem"},                /* 6 */ /* available from kstat >= 2.5 */
181  {"avenrun"},                  /* 7 */ /* available from kstat */
182  {"cpu"},                      /* 8 */ /* available from kstat */
183  {"nproc"},                    /* 9 */ /* available from kstat */
184  {"ncpus"},                    /* 10 */ /* available from kstat */
185  {0}
186};
187
188static unsigned long avenrun_offset;
189static unsigned long mpid_offset;
190#ifdef USE_KSTAT
191#define NO_NPROC
192static kstat_ctl_t *kc = NULL;
193static kstat_t **cpu_ks;
194static cpu_stat_t *cpu_stat;
195#else
196static unsigned long *cpu_offset;
197#endif
198static unsigned long nproc_offset;
199static unsigned long freemem_offset;
200static unsigned long maxmem_offset;
201static unsigned long availrmem_offset;
202static unsigned long swapfs_minfree_offset;
203static unsigned long anoninfo_offset;
204static void reallocproc(int n);
205static int maxprocs;
206
207/* get_process_info passes back a handle.  This is what it looks like: */
208struct handle
209  {
210    struct prpsinfo **next_proc;/* points to next valid proc pointer */
211    int remaining;              /* number of pointers remaining */
212  };
213
214/*
215 * Structure for keeping track of CPU times from last time around
216 * the program.  We keep these things in a hash table, which is
217 * recreated at every cycle.
218 */
219struct oldproc
220  {
221    pid_t oldpid;
222    double oldtime;
223    double oldpct;
224  };
225int oldprocs;                   /* size of table */
226#define HASH(x) ((x << 1) % oldprocs)
227
228/*
229 * GCC assumes that all doubles are aligned.  Unfortunately it
230 * doesn't round up the structure size to be a multiple of 8.
231 * Thus we'll get a coredump when going through array.  The
232 * following is a size rounded up to 8.
233 */
234#define PRPSINFOSIZE dbl_align(sizeof(struct prpsinfo))
235
236/*
237 *  These definitions control the format of the per-process area
238 */
239#if OSREV >= 58
240static char header[] =
241"   PID X        THR PRI NICE  SIZE   RES STATE    TIME    CPU COMMAND";
242/* 0123456   -- field to fill in starts at header+6 */
243#define UNAME_START 7
244
245#define Proc_format \
246        "%6d %-8.8s %3d %3d %4d %5s %5s %-6s %6s %5.2f%% %s"
247#else
248static char header[] =
249"  PID X        THR PRI NICE  SIZE   RES STATE    TIME    CPU COMMAND";
250/* 0123456   -- field to fill in starts at header+6 */
251#define UNAME_START 6
252
253#define Proc_format \
254        "%5d %-8.8s %3d %3d %4d %5s %5s %-6s %6s %5.2f%% %s"
255#endif
256
257/* process state names for the "STATE" column of the display */
258char *state_abbrev[] =
259{"", "sleep", "run", "zombie", "stop", "start", "cpu", "swap"};
260
261int process_states[8];
262char *procstatenames[] =
263{
264  "", " sleeping, ", " running, ", " zombie, ", " stopped, ",
265  " starting, ", " on cpu, ", " swapped, ",
266  NULL
267};
268
269int cpu_states[CPUSTATES];
270char *cpustatenames[] =
271{"idle", "user", "kernel", "iowait", "swap", NULL};
272#define CPUSTATE_IOWAIT 3
273#define CPUSTATE_SWAP   4
274
275
276/* these are for detailing the memory statistics */
277int memory_stats[5];
278char *memorynames[] =
279{"K real, ", "K active, ", "K free, ", "K swap in use, ", "K swap free", NULL};
280
281/* these are names given to allowed sorting orders -- first is default */
282char *ordernames[] =
283{"cpu", "size", "res", "time", NULL};
284
285/* forward definitions for comparison functions */
286int compare_cpu();
287int compare_size();
288int compare_res();
289int compare_time();
290
291int (*proc_compares[])() = {
292    compare_cpu,
293    compare_size,
294    compare_res,
295    compare_time,
296    NULL };
297
298kvm_t *kd;
299static DIR *procdir;
300static int nproc;
301static int ncpus;
302
303/* these are for keeping track of the proc array */
304static int bytes;
305static struct prpsinfo *pbase;
306static struct prpsinfo **pref;
307static struct oldproc *oldbase;
308
309/* pagetok function is really a pointer to an appropriate function */
310static int pageshift;
311static int (*p_pagetok) ();
312#define pagetok(size) ((*p_pagetok)(size))
313
314/* useful externals */
315extern char *myname;
316extern int check_nlist ();
317extern int gettimeofday ();
318extern int getkval ();
319extern void perror ();
320extern void getptable ();
321extern void quit ();
322extern int nlist ();
323
324int pagetok_none(int size)
325
326{
327    return(size);
328}
329
330int pagetok_left(int size)
331
332{
333    return(size << pageshift);
334}
335
336int pagetok_right(int size)
337
338{
339    return(size >> pageshift);
340}
341
342int
343machine_init (struct statics *statics)
344{
345    static struct var v;
346    struct oldproc *op, *endbase;
347    int i;
348    char *p;
349#ifndef USE_KSTAT
350    int offset;
351#endif
352
353    /* There's a buffer overflow bug in curses that can be exploited when
354       we run as root.  By making sure that TERMINFO is set to something
355       this bug is avoided.  This code thanks to Casper */
356    if ((p = getenv("TERMINFO")) == NULL || *p == '\0')
357    {
358        putenv("TERMINFO=/usr/share/lib/terminfo/");
359    }
360
361    /* perform the kvm_open - suppress error here */
362    kd = kvm_open (NULL, NULL, NULL, O_RDONLY, NULL);
363
364    /*
365     * turn off super group/user privs - but beware; we might
366     * want the privs back later and we still have a fd to
367     * /dev/kmem open so we can't use setgid()/setuid() as that
368     * would allow a debugger to attach to this process. CD
369     */
370    setegid(getgid());
371    seteuid(getuid()); /* super user not needed for NEW_PROC */
372
373    /* fill in the statics information */
374    statics->procstate_names = procstatenames;
375    statics->cpustate_names = cpustatenames;
376    statics->memory_names = memorynames;
377    statics->order_names = ordernames;
378
379    /*
380     * test kvm_open return value - print error message and exit
381     * when we need to read kernel memory (older releases)
382     */
383    if (kd == NULL)
384      {
385#ifndef USE_KSTAT
386        /* Print the failure message here */
387        (void) kvm_open (NULL, NULL, NULL, O_RDONLY, "top");
388        perror("kvm_open");
389        return (-1);
390#endif
391      }
392    if (kd)
393      {
394      if (kvm_nlist (kd, nlst) < 0)
395        {
396          perror ("kvm_nlist");
397          return (-1);
398        }
399      if (check_nlist (nlst) != 0)
400        return (-1);
401      }
402
403#ifndef NO_NPROC
404    /* NPROC Tuning parameter for max number of processes */
405    (void) getkval (nlst[X_V].n_value, &v, sizeof (struct var), nlst[X_V].n_name);
406    nproc = v.v_proc;
407
408    reallocproc(nproc);
409#endif
410
411    /* stash away certain offsets for later use */
412    mpid_offset = nlst[X_MPID].n_value;
413    nproc_offset = nlst[X_NPROC].n_value;
414    avenrun_offset = nlst[X_AVENRUN].n_value;
415    anoninfo_offset = nlst[X_ANONINFO].n_value;
416    freemem_offset = nlst[X_FREEMEM].n_value;
417    maxmem_offset = nlst[X_MAXMEM].n_value;
418    availrmem_offset = nlst[X_AVAILRMEM].n_value;
419    swapfs_minfree_offset = nlst[X_SWAPFS_MINFREE].n_value;
420
421
422#ifndef USE_KSTAT
423    (void) getkval (nlst[X_NCPUS].n_value, (int *) (&ncpus),
424                    sizeof (ncpus), "ncpus");
425
426    cpu_offset = (unsigned long *) malloc (ncpus * sizeof (unsigned long));
427    for (i = offset = 0; i < ncpus; offset += sizeof(unsigned long)) {
428        (void) getkval (nlst[X_CPU].n_value + offset,
429                        &cpu_offset[i], sizeof (unsigned long),
430                        nlst[X_CPU].n_name );
431        if (cpu_offset[i] != 0)
432            i++;
433    }
434#endif
435
436    /* calculate pageshift value */
437    i = sysconf(_SC_PAGESIZE);
438    pageshift = 0;
439    while ((i >>= 1) > 0)
440    {
441        pageshift++;
442    }
443
444    /* calculate an amount to shift to K values */
445    /* remember that log base 2 of 1024 is 10 (i.e.: 2^10 = 1024) */
446    pageshift -= 10;
447
448    /* now determine which pageshift function is appropriate for the
449       result (have to because x << y is undefined for y < 0) */
450    if (pageshift > 0)
451    {
452        /* this is the most likely */
453        p_pagetok = pagetok_left;
454    }
455    else if (pageshift == 0)
456    {
457        p_pagetok = pagetok_none;
458    }
459    else
460    {
461        p_pagetok = pagetok_right;
462        pageshift = -pageshift;
463    }
464
465    if (!(procdir = opendir (PROCFS)))
466      {
467        (void) fprintf (stderr, "Unable to open %s\n", PROCFS);
468        return (-1);
469      }
470
471    if (chdir (PROCFS))
472      {                         /* handy for later on when we're reading it */
473        (void) fprintf (stderr, "Unable to chdir to %s\n", PROCFS);
474        return (-1);
475      }
476
477    /* all done! */
478    return (0);
479  }
480
481char *
482format_header (register char *uname_field)
483{
484  register char *ptr;
485
486  ptr = header + UNAME_START;
487  while (*uname_field != '\0')
488    *ptr++ = *uname_field++;
489
490  return (header);
491}
492
493#ifdef USE_KSTAT
494
495#define UPDKCID(nk,ok) \
496if (nk == -1) { \
497  perror("kstat_read "); \
498  quit(1); \
499} \
500if (nk != ok)\
501  goto kcid_changed;
502
503int kupdate(int avenrun[3])
504{
505    kstat_t *ks;
506    kid_t nkcid;
507    int i;
508    int changed = 0;
509    static int ncpu = 0;
510    static kid_t kcid = 0;
511    kstat_named_t *kn;
512
513
514    /*
515     * 0. kstat_open
516     */
517
518    if (!kc)
519    {
520        kc = kstat_open();
521        if (!kc)
522        {
523            perror("kstat_open ");
524            quit(1);
525        }
526        changed = 1;
527        kcid = kc->kc_chain_id;
528    }
529
530    /* keep doing it until no more changes */
531  kcid_changed:
532
533    /*
534     * 1.  kstat_chain_update
535     */
536    nkcid = kstat_chain_update(kc);
537    if (nkcid)
538    {
539        /* UPDKCID will abort if nkcid is -1, so no need to check */
540        changed = 1;
541        kcid = nkcid;
542    }
543    UPDKCID(nkcid,0);
544
545    ks = kstat_lookup(kc, "unix", 0, "system_misc");
546    if (kstat_read(kc, ks, 0) == -1) {
547        perror("kstat_read");
548        quit(1);
549    }
550
551    /* load average */
552    kn = kstat_data_lookup(ks, "avenrun_1min");
553    if (kn)
554        avenrun[0] = kn->value.ui32;
555    kn = kstat_data_lookup(ks, "avenrun_5min");
556    if (kn)
557        avenrun[1] = kn->value.ui32;
558    kn = kstat_data_lookup(ks, "avenrun_15min");
559    if (kn)
560        avenrun[2] = kn->value.ui32;
561
562    /* nproc */
563    kn = kstat_data_lookup(ks, "nproc");
564    if (kn) {
565        nproc = kn->value.ui32;
566#ifdef NO_NPROC
567        if (nproc > maxprocs)
568            reallocproc(2 * nproc);
569#endif
570    }
571
572    if (changed) {
573
574        /*
575         * 2. get data addresses
576         */
577
578        ncpu = 0;
579
580        kn = kstat_data_lookup(ks, "ncpus");
581        if (kn && kn->value.ui32 > ncpus) {
582            ncpus = kn->value.ui32;
583            cpu_ks = (kstat_t **) realloc (cpu_ks, ncpus * sizeof (kstat_t *));
584            cpu_stat = (cpu_stat_t *) realloc (cpu_stat,
585                        ncpus * sizeof (cpu_stat_t));
586        }
587
588        for (ks = kc->kc_chain; ks;
589             ks = ks->ks_next)
590        {
591            if (strncmp(ks->ks_name, "cpu_stat", 8) == 0)
592            {
593                nkcid = kstat_read(kc, ks, NULL);
594                /* if kcid changed, pointer might be invalid */
595                UPDKCID(nkcid, kcid);
596
597                cpu_ks[ncpu] = ks;
598                ncpu++;
599                if (ncpu > ncpus)
600                {
601                    fprintf(stderr, "kstat finds too many cpus: should be %d\n",
602                            ncpus);
603                    quit(1);
604                }
605            }
606        }
607        /* note that ncpu could be less than ncpus, but that's okay */
608        changed = 0;
609    }
610
611    /*
612     * 3. get data
613     */
614
615    for (i = 0; i < ncpu; i++)
616    {
617        nkcid = kstat_read(kc, cpu_ks[i], &cpu_stat[i]);
618        /* if kcid changed, pointer might be invalid */
619        UPDKCID(nkcid, kcid);
620    }
621
622    /* return the number of cpus found */
623    return(ncpu);
624}
625
626#endif /* USE_KSTAT */
627
628void
629get_system_info (struct system_info *si)
630{
631  int avenrun[3];
632  static long freemem;
633  static long maxmem;
634  static long availrmem;
635  static long swapfs_minfree;
636  static int swap_total;
637  static int swap_free;
638  struct anoninfo anoninfo;
639  static long cp_time[CPUSTATES];
640  static long cp_old[CPUSTATES];
641  static long cp_diff[CPUSTATES];
642  register int j, i;
643#ifdef USE_KSTAT
644  kstat_t *ks;
645  kstat_named_t *kn;
646  int cpus_found;
647#else
648  struct cpu cpu;
649#endif
650
651  /* get the cp_time array */
652  for (j = 0; j < CPUSTATES; j++)
653    cp_time[j] = 0L;
654
655#ifdef USE_KSTAT
656  /* use kstat to update all processor information */
657  cpus_found = kupdate(avenrun);
658  for (i = 0; i < cpus_found; i++)
659    {
660      /* sum counters up to, but not including, wait state counter */
661      for (j = 0; j < CPU_WAIT; j++)
662        cp_time[j] += (long) cpu_stat[i].cpu_sysinfo.cpu[j];
663
664      /* add in wait state breakdown counters */
665      cp_time[CPUSTATE_IOWAIT] += (long) cpu_stat[i].cpu_sysinfo.wait[W_IO] +
666                                  (long) cpu_stat[i].cpu_sysinfo.wait[W_PIO];
667      cp_time[CPUSTATE_SWAP] += (long) cpu_stat[i].cpu_sysinfo.wait[W_SWAP];
668    }
669    /* avenrun */
670
671#if OSREV >= 55
672    /*
673     * The system_pages kstat is extremely expensive on systems with large
674     * amounts of physical memory.  But since we only need freemem/availrmem
675     * and those are readily avaiable in the kernel cheaply, we read the
676     * kernel if kvm_open succeeded.
677     */
678    if (kd == NULL) {
679        ks = kstat_lookup(kc, "unix", 0, "system_pages");
680        if (kstat_read(kc, ks, 0) == -1) {
681            perror("kstat_read");
682            quit(1);
683        }
684#ifdef USE_ANONINFO
685        kn = kstat_data_lookup(ks, "availrmem");
686        if (kn)
687            availrmem = kn->value.ul;
688#endif
689        kn = kstat_data_lookup(ks, "freemem");
690        if (kn)
691            freemem = kn->value.ul;
692    }
693#endif /* OSREV >= 55 */
694
695#else /* !USE_KSTAT */
696
697  for (i = 0; i < ncpus; i++)
698    if (cpu_offset[i] != 0)
699    {
700      /* get struct cpu for this processor */
701      (void) getkval (cpu_offset[i], &cpu, sizeof (struct cpu), "cpu");
702
703      /* sum counters up to, but not including, wait state counter */
704      for (j = 0; j < CPU_WAIT; j++)
705        cp_time[j] += (long) cpu.cpu_stat.cpu_sysinfo.cpu[j];
706
707      /* add in wait state breakdown counters */
708      cp_time[CPUSTATE_IOWAIT] += (long) cpu.cpu_stat.cpu_sysinfo.wait[W_IO] +
709                                  (long) cpu.cpu_stat.cpu_sysinfo.wait[W_PIO];
710      cp_time[CPUSTATE_SWAP] += (long) cpu.cpu_stat.cpu_sysinfo.wait[W_SWAP];
711    }
712
713  /* get load average array */
714  (void) getkval (avenrun_offset, (int *) avenrun, sizeof (avenrun), "avenrun");
715
716
717#endif /* USE_KSTAT */
718
719  /* convert cp_time counts to percentages */
720  (void) percentages (CPUSTATES, cpu_states, cp_time, cp_old, cp_diff);
721
722   /* get mpid -- process id of last process */
723  if (kd)
724    (void) getkval(mpid_offset, &(si->last_pid), sizeof (si->last_pid), "mpid");
725  else
726    si->last_pid = -1;
727
728  /* convert load averages to doubles */
729  for (i = 0; i < 3; i++)
730    si->load_avg[i] = loaddouble (avenrun[i]);
731
732  /* get system wide main memory usage structure */
733#if 1
734  maxmem = sysconf(_SC_PHYS_PAGES);
735#else
736  (void) getkval (maxmem_offset, (int *) (&maxmem), sizeof (maxmem), "maxmem");
737#endif
738
739#if defined(USE_KSTAT) && OSREV >= 55
740   if (kd != NULL)              /* always get freemem from kvm if we can*/
741#endif
742      (void) getkval (freemem_offset, (int *) (&freemem),
743                    sizeof (freemem), "freemem");
744
745  memory_stats[0] = pagetok (maxmem);
746  memory_stats[1] = 0;
747  memory_stats[2] = pagetok (freemem);
748
749#ifdef USE_ANONINFO
750  (void) getkval (anoninfo_offset, (int *) (&anoninfo), sizeof (anoninfo), "anoninfo");
751#if defined(USE_KSTAT) && OSREV >= 55
752   if (kd != NULL)              /* always get availrmem from kvm if we can*/
753#endif
754      (void) getkval (availrmem_offset, (int *) (&availrmem),
755                    sizeof (availrmem), "availrmem");
756
757  (void) getkval (swapfs_minfree_offset, (int *) (&swapfs_minfree), sizeof (swapfs_minfree), "swapfs_minfree");
758  memory_stats[3] = pagetok (anoninfo.ani_resv);
759  memory_stats[4] = pagetok (MAX ((int) (anoninfo.ani_max - anoninfo.ani_resv), 0) + availrmem - swapfs_minfree);
760#else
761  get_swapinfo(&swap_total, &swap_free);
762  memory_stats[3] = pagetok(swap_total - swap_free);
763  memory_stats[4] = pagetok(swap_free);
764#endif
765
766  /* set arrays and strings */
767  si->cpustates = cpu_states;
768  si->memory = memory_stats;
769}
770
771static struct handle handle;
772
773caddr_t
774get_process_info (
775                   struct system_info *si,
776                   struct process_select *sel,
777                   int (*compare) ())
778{
779  register int i;
780  register int total_procs;
781  register int active_procs;
782  register struct prpsinfo **prefp;
783  register struct prpsinfo *pp;
784
785  /* these are copied out of sel for speed */
786  int show_idle;
787  int show_system;
788  int show_uid;
789
790#ifndef USE_KSTAT
791  /* Get current number of processes */
792  /* Got this when calling system info if using kstat */
793  (void) getkval (nproc_offset, (int *) (&nproc), sizeof (nproc), "nproc");
794#endif
795
796  /* read all the proc structures */
797  getptable (pbase);
798
799  /* get a pointer to the states summary array */
800  si->procstates = process_states;
801
802  /* set up flags which define what we are going to select */
803  show_idle = sel->idle;
804  show_system = sel->system;
805  show_uid = sel->uid != -1;
806
807  /* count up process states and get pointers to interesting procs */
808  total_procs = 0;
809  active_procs = 0;
810  (void) memset (process_states, 0, sizeof (process_states));
811  prefp = pref;
812
813  for (pp = pbase, i = 0; i < nproc;
814       i++, pp = (struct prpsinfo *) ((char *) pp + PRPSINFOSIZE))
815    {
816      /*
817         *  Place pointers to each valid proc structure in pref[].
818         *  Process slots that are actually in use have a non-zero
819         *  status field.  Processes with SSYS set are system
820         *  processes---these get ignored unless show_sysprocs is set.
821         */
822      if (pp->pr_state != 0 &&
823          (show_system || ((pp->pr_flag & SSYS) == 0)))
824        {
825          total_procs++;
826          process_states[pp->pr_state]++;
827          if ((!ZOMBIE(pp)) &&
828              (show_idle || percent_cpu (pp) || (pp->pr_state == SRUN) || (pp->pr_state == SONPROC)) &&
829              (!show_uid || pp->pr_uid == (uid_t) sel->uid))
830            {
831              *prefp++ = pp;
832              active_procs++;
833            }
834        }
835    }
836
837  /* if requested, sort the "interesting" processes */
838  if (compare != NULL)
839    qsort ((char *) pref, active_procs, sizeof (struct prpsinfo *), compare);
840
841  /* remember active and total counts */
842  si->p_total = total_procs;
843  si->p_active = active_procs;
844
845  /* pass back a handle */
846  handle.next_proc = pref;
847  handle.remaining = active_procs;
848  return ((caddr_t) & handle);
849}
850
851char fmt[MAX_COLS];                     /* static area where result is built */
852
853char *
854format_next_process (
855                      caddr_t handle,
856                      char *(*get_userid) ())
857{
858  register struct prpsinfo *pp;
859  struct handle *hp;
860  register long cputime;
861  register double pctcpu;
862  char sb[10];
863
864  /* find and remember the next proc structure */
865  hp = (struct handle *) handle;
866  pp = *(hp->next_proc++);
867  hp->remaining--;
868
869  /* get the cpu usage and calculate the cpu percentages */
870  cputime = pp->pr_time.tv_sec;
871  pctcpu = percent_cpu (pp);
872
873  if (pp->pr_state == SONPROC && ncpus > 1)
874    sprintf(sb,"cpu/%-2d", pp->pr_onpro); /* XXX large #s may overflow colums */
875  else
876    *sb = '\0';
877
878  /* format this entry */
879  sprintf (fmt,
880           Proc_format,
881           pp->pr_pid,
882           (*get_userid) (pp->pr_uid),
883           (u_short)pp->pr_fill < 999 ? (u_short)pp->pr_fill : 999,
884           pp->pr_pri,
885           pp->pr_nice - NZERO,
886           format_k(SIZE_K(pp)),
887           format_k(RSS_K(pp)),
888           *sb ? sb : state_abbrev[pp->pr_state],
889           format_time(cputime),
890           pctcpu,
891           pp->pr_fname);
892
893  /* return the result */
894  return (fmt);
895}
896
897/*
898 * check_nlist(nlst) - checks the nlist to see if any symbols were not
899 *              found.  For every symbol that was not found, a one-line
900 *              message is printed to stderr.  The routine returns the
901 *              number of symbols NOT found.
902 */
903int
904check_nlist (register struct nlist *nlst)
905{
906  register int i;
907
908  /* check to see if we got ALL the symbols we requested */
909  /* this will write one line to stderr for every symbol not found */
910
911  i = 0;
912  while (nlst->n_name != NULL)
913    {
914      if (nlst->n_type == 0)
915        {
916          /* this one wasn't found */
917          fprintf (stderr, "kernel: no symbol named `%s'\n", nlst->n_name);
918          i = 1;
919        }
920      nlst++;
921    }
922  return (i);
923}
924
925
926/*
927 *  getkval(offset, ptr, size, refstr) - get a value out of the kernel.
928 *      "offset" is the byte offset into the kernel for the desired value,
929 *      "ptr" points to a buffer into which the value is retrieved,
930 *      "size" is the size of the buffer (and the object to retrieve),
931 *      "refstr" is a reference string used when printing error meessages,
932 *          if "refstr" starts with a '!', then a failure on read will not
933 *          be fatal (this may seem like a silly way to do things, but I
934 *          really didn't want the overhead of another argument).
935 *
936 */
937int
938getkval (unsigned long offset,
939         int *ptr,
940         int size,
941         char *refstr)
942{
943  if (kvm_read (kd, offset, (char *) ptr, size) != size)
944    {
945      if (*refstr == '!')
946        {
947          return (0);
948        }
949      else
950        {
951          fprintf (stderr, "top: kvm_read for %s: %s\n", refstr, strerror(errno));
952          quit (23);
953        }
954    }
955  return (1);
956
957}
958
959/* comparison routines for qsort */
960
961/*
962 * There are currently four possible comparison routines.  main selects
963 * one of these by indexing in to the array proc_compares.
964 *
965 * Possible keys are defined as macros below.  Currently these keys are
966 * defined:  percent cpu, cpu ticks, process state, resident set size,
967 * total virtual memory usage.  The process states are ordered as follows
968 * (from least to most important):  WAIT, zombie, sleep, stop, start, run.
969 * The array declaration below maps a process state index into a number
970 * that reflects this ordering.
971 */
972
973/* First, the possible comparison keys.  These are defined in such a way
974   that they can be merely listed in the source code to define the actual
975   desired ordering.
976 */
977
978#define ORDERKEY_PCTCPU  if (dresult = percent_cpu (p2) - percent_cpu (p1),\
979                             (result = dresult > 0.0 ? 1 : dresult < 0.0 ? -1 : 0) == 0)
980#define ORDERKEY_CPTICKS if ((result = p2->pr_time.tv_sec - p1->pr_time.tv_sec) == 0)
981#define ORDERKEY_STATE   if ((result = (long) (sorted_state[p2->pr_state] - \
982                               sorted_state[p1->pr_state])) == 0)
983#define ORDERKEY_PRIO    if ((result = p2->pr_oldpri - p1->pr_oldpri) == 0)
984#define ORDERKEY_RSSIZE  if ((result = p2->pr_rssize - p1->pr_rssize) == 0)
985#define ORDERKEY_MEM     if ((result = (p2->pr_size - p1->pr_size)) == 0)
986
987/* Now the array that maps process state to a weight */
988
989unsigned char sorted_state[] =
990{
991  0,                            /* not used             */
992  3,                            /* sleep                */
993  6,                            /* run                  */
994  2,                            /* zombie               */
995  4,                            /* stop                 */
996  5,                            /* start                */
997  7,                            /* run on a processor   */
998  1                             /* being swapped (WAIT) */
999};
1000
1001
1002/* compare_cpu - the comparison function for sorting by cpu percentage */
1003
1004int
1005compare_cpu (
1006               struct prpsinfo **pp1,
1007               struct prpsinfo **pp2)
1008  {
1009    register struct prpsinfo *p1;
1010    register struct prpsinfo *p2;
1011    register long result;
1012    double dresult;
1013
1014    /* remove one level of indirection */
1015    p1 = *pp1;
1016    p2 = *pp2;
1017
1018    ORDERKEY_PCTCPU
1019    ORDERKEY_CPTICKS
1020    ORDERKEY_STATE
1021    ORDERKEY_PRIO
1022    ORDERKEY_RSSIZE
1023    ORDERKEY_MEM
1024    ;
1025
1026    return (result);
1027  }
1028
1029/* compare_size - the comparison function for sorting by total memory usage */
1030
1031int
1032compare_size (
1033               struct prpsinfo **pp1,
1034               struct prpsinfo **pp2)
1035  {
1036    register struct prpsinfo *p1;
1037    register struct prpsinfo *p2;
1038    register long result;
1039    double dresult;
1040
1041    /* remove one level of indirection */
1042    p1 = *pp1;
1043    p2 = *pp2;
1044
1045    ORDERKEY_MEM
1046    ORDERKEY_RSSIZE
1047    ORDERKEY_PCTCPU
1048    ORDERKEY_CPTICKS
1049    ORDERKEY_STATE
1050    ORDERKEY_PRIO
1051    ;
1052
1053    return (result);
1054  }
1055
1056/* compare_res - the comparison function for sorting by resident set size */
1057
1058int
1059compare_res (
1060               struct prpsinfo **pp1,
1061               struct prpsinfo **pp2)
1062  {
1063    register struct prpsinfo *p1;
1064    register struct prpsinfo *p2;
1065    register long result;
1066    double dresult;
1067
1068    /* remove one level of indirection */
1069    p1 = *pp1;
1070    p2 = *pp2;
1071
1072    ORDERKEY_RSSIZE
1073    ORDERKEY_MEM
1074    ORDERKEY_PCTCPU
1075    ORDERKEY_CPTICKS
1076    ORDERKEY_STATE
1077    ORDERKEY_PRIO
1078    ;
1079
1080    return (result);
1081  }
1082
1083/* compare_time - the comparison function for sorting by total cpu time */
1084
1085int
1086compare_time (
1087               struct prpsinfo **pp1,
1088               struct prpsinfo **pp2)
1089  {
1090    register struct prpsinfo *p1;
1091    register struct prpsinfo *p2;
1092    register long result;
1093    double dresult;
1094
1095    /* remove one level of indirection */
1096    p1 = *pp1;
1097    p2 = *pp2;
1098
1099    ORDERKEY_CPTICKS
1100    ORDERKEY_PCTCPU
1101    ORDERKEY_STATE
1102    ORDERKEY_PRIO
1103    ORDERKEY_MEM
1104    ORDERKEY_RSSIZE
1105    ;
1106
1107    return (result);
1108  }
1109
1110/*
1111get process table
1112 V.4 only has a linked list of processes so we want to follow that
1113 linked list, get all the process structures, and put them in our own
1114 table
1115*/
1116void
1117getptable (struct prpsinfo *baseptr)
1118{
1119  struct prpsinfo *currproc;    /* pointer to current proc structure    */
1120#ifndef USE_NEW_PROC
1121  struct prstatus prstatus;     /* for additional information */
1122#endif
1123  int numprocs = 0;
1124  int i;
1125  struct dirent *direntp;
1126  struct oldproc *op;
1127  static struct timeval lasttime =
1128  {0, 0};
1129  struct timeval thistime;
1130  double timediff;
1131  double alpha, beta;
1132  struct oldproc *endbase;
1133
1134  gettimeofday (&thistime, NULL);
1135  /*
1136   * To avoid divides, we keep times in nanoseconds.  This is
1137   * scaled by 1e7 rather than 1e9 so that when we divide we
1138   * get percent.
1139   */
1140  if (lasttime.tv_sec)
1141    timediff = ((double) thistime.tv_sec * 1.0e7 +
1142                ((double) thistime.tv_usec * 10.0)) -
1143      ((double) lasttime.tv_sec * 1.0e7 +
1144       ((double) lasttime.tv_usec * 10.0));
1145  else
1146    timediff = 1.0e7;
1147
1148  /*
1149     * constants for exponential average.  avg = alpha * new + beta * avg
1150     * The goal is 50% decay in 30 sec.  However if the sample period
1151     * is greater than 30 sec, there's not a lot we can do.
1152     */
1153  if (timediff < 30.0e7)
1154    {
1155      alpha = 0.5 * (timediff / 30.0e7);
1156      beta = 1.0 - alpha;
1157    }
1158  else
1159    {
1160      alpha = 0.5;
1161      beta = 0.5;
1162    }
1163
1164  endbase = oldbase + oldprocs;
1165  currproc = baseptr;
1166
1167  /* before reading /proc files, turn on root privs */
1168  /* (we don't care if this fails since it will be caught later) */
1169#ifndef USE_NEW_PROC
1170  seteuid(0);
1171#endif
1172
1173  for (rewinddir (procdir); (direntp = readdir (procdir));)
1174    {
1175      int fd;
1176      char buf[30];
1177
1178      if (direntp->d_name[0] == '.')
1179        continue;
1180
1181#ifdef USE_NEW_PROC
1182      snprintf(buf, sizeof(buf), "%s/psinfo", direntp->d_name);
1183
1184      if ((fd = open (buf, O_RDONLY)) < 0)
1185        continue;
1186
1187      if (read(fd, currproc, sizeof(psinfo_t)) != sizeof(psinfo_t))
1188        {
1189          (void) close (fd);
1190          continue;
1191        }
1192       
1193#else
1194      if ((fd = open (direntp->d_name, O_RDONLY)) < 0)
1195        continue;
1196
1197      if (ioctl (fd, PIOCPSINFO, currproc) < 0)
1198        {
1199          (void) close (fd);
1200          continue;
1201        }
1202
1203      if (ioctl (fd, PIOCSTATUS, &prstatus) < 0)
1204      {
1205          /* not a show stopper -- just fill in the needed values */
1206          currproc->pr_fill = 0;
1207          currproc->pr_onpro = 0;
1208       } else {
1209          /* copy over the values we need from prstatus */
1210          currproc->pr_fill = (short)prstatus.pr_nlwp;
1211          currproc->pr_onpro = prstatus.pr_processor;
1212       }
1213#endif
1214
1215      /*
1216       * SVr4 doesn't keep track of CPU% in the kernel, so we have
1217       * to do our own.  See if we've heard of this process before.
1218       * If so, compute % based on CPU since last time.
1219       * NOTE:  Solaris 2.4 and higher do maintain CPU% in prpsinfo.
1220       */
1221      op = oldbase + HASH (currproc->pr_pid);
1222      while (1)
1223        {
1224          if (op->oldpid == -1) /* not there */
1225            break;
1226          if (op->oldpid == currproc->pr_pid)
1227            {                   /* found old data */
1228#if (OSREV < 54)
1229              percent_cpu (currproc) =
1230                ((currproc->pr_time.tv_sec * 1.0e9 +
1231                  currproc->pr_time.tv_nsec)
1232                 - op->oldtime) / timediff;
1233#endif
1234              weighted_cpu (currproc) =
1235                op->oldpct * beta + percent_cpu (currproc) * alpha;
1236
1237              break;
1238            }
1239          op++;                 /* try next entry in hash table */
1240          if (op == endbase)    /* table wrapped around */
1241            op = oldbase;
1242        }
1243
1244      /* Otherwise, it's new, so use all of its CPU time */
1245      if (op->oldpid == -1)
1246        {
1247#if (OSREV >= 54)
1248          weighted_cpu (currproc) =
1249            percent_cpu (currproc);
1250#else
1251          if (lasttime.tv_sec)
1252            {
1253              percent_cpu (currproc) =
1254                (currproc->pr_time.tv_sec * 1.0e9 +
1255                 currproc->pr_time.tv_nsec) / timediff;
1256              weighted_cpu (currproc) =
1257                percent_cpu (currproc);
1258            }
1259          else
1260            {                   /* first screen -- no difference is possible */
1261              percent_cpu (currproc) = 0.0;
1262              weighted_cpu (currproc) = 0.0;
1263            }
1264#endif
1265        }
1266
1267      numprocs++;
1268      currproc = (struct prpsinfo *) ((char *) currproc + PRPSINFOSIZE);
1269      (void) close (fd);
1270#ifdef NO_NPROC
1271      /* Atypical place for growth */
1272      if (numprocs >= maxprocs) {
1273            reallocproc(2 * numprocs);
1274            currproc = (struct prpsinfo *)
1275                    ((char *)baseptr + PRPSINFOSIZE * numprocs);
1276      }
1277#endif
1278    }
1279
1280#ifndef USE_NEW_PROC
1281  /* turn off root privs */
1282  seteuid(getuid());
1283#endif
1284
1285  if (nproc != numprocs)
1286    nproc = numprocs;
1287
1288  /*
1289   * Save current CPU time for next time around
1290   * For the moment recreate the hash table each time, as the code
1291   * is easier that way.
1292   */
1293  oldprocs = 2 * nproc;
1294  endbase = oldbase + oldprocs;
1295  for (op = oldbase; op < endbase; op++)
1296    op->oldpid = -1;
1297  for (i = 0, currproc = baseptr;
1298       i < nproc;
1299     i++, currproc = (struct prpsinfo *) ((char *) currproc + PRPSINFOSIZE))
1300    {
1301      /* find an empty spot */
1302      op = oldbase + HASH (currproc->pr_pid);
1303      while (1)
1304        {
1305          if (op->oldpid == -1)
1306            break;
1307          op++;
1308          if (op == endbase)
1309            op = oldbase;
1310        }
1311      op->oldpid = currproc->pr_pid;
1312      op->oldtime = (currproc->pr_time.tv_sec * 1.0e9 +
1313                     currproc->pr_time.tv_nsec);
1314      op->oldpct = weighted_cpu (currproc);
1315    }
1316  lasttime = thistime;
1317}
1318
1319/*
1320 * proc_owner(pid) - returns the uid that owns process "pid", or -1 if
1321 *              the process does not exist.
1322 *              It is EXTREMLY IMPORTANT that this function work correctly.
1323 *              If top runs setuid root (as in SVR4), then this function
1324 *              is the only thing that stands in the way of a serious
1325 *              security problem.  It validates requests for the "kill"
1326 *              and "renice" commands.
1327 */
1328uid_t
1329proc_owner (pid_t pid)
1330{
1331  register struct prpsinfo *p;
1332  int i;
1333  for (i = 0, p = pbase; i < nproc;
1334       i++, p = (struct prpsinfo *) ((char *) p + PRPSINFOSIZE)) {
1335    if (p->pr_pid == pid)
1336      return (p->pr_uid);
1337  }
1338  return (-1);
1339}
1340
1341#if OSREV < 55
1342int
1343setpriority (int dummy, int who, int niceval)
1344{
1345  int scale;
1346  int prio;
1347  pcinfo_t pcinfo;
1348  pcparms_t pcparms;
1349  tsparms_t *tsparms;
1350
1351  strcpy (pcinfo.pc_clname, "TS");
1352  if (priocntl (0, 0, PC_GETCID, (caddr_t) & pcinfo) == -1)
1353    return (-1);
1354
1355  prio = niceval;
1356  if (prio > PRIO_MAX)
1357    prio = PRIO_MAX;
1358  else if (prio < PRIO_MIN)
1359    prio = PRIO_MIN;
1360
1361  tsparms = (tsparms_t *) pcparms.pc_clparms;
1362  scale = ((tsinfo_t *) pcinfo.pc_clinfo)->ts_maxupri;
1363  tsparms->ts_uprilim = tsparms->ts_upri = -(scale * prio) / 20;
1364  pcparms.pc_cid = pcinfo.pc_cid;
1365
1366  if (priocntl (P_PID, who, PC_SETPARMS, (caddr_t) & pcparms) == -1)
1367    return (-1);
1368
1369  return (0);
1370}
1371#endif
1372
1373#ifndef USE_ANONINFO
1374get_swapinfo(int *total, int *fr)
1375
1376{
1377#ifdef SC_AINFO
1378    struct anoninfo anon;
1379
1380    if (swapctl(SC_AINFO, &anon) == -1) {
1381        *total = *fr = 0;
1382        return;
1383    }
1384    *total = anon.ani_max;
1385    *fr = anon.ani_max - anon.ani_resv;
1386#else
1387    register int cnt, i;
1388    register int t, f;
1389    struct swaptable *swt;
1390    struct swapent *ste;
1391    static char path[256];
1392
1393    /* get total number of swap entries */
1394    cnt = swapctl(SC_GETNSWP, 0);
1395
1396    /* allocate enough space to hold count + n swapents */
1397    swt = (struct swaptable *)malloc(sizeof(int) +
1398                                     cnt * sizeof(struct swapent));
1399    if (swt == NULL)
1400    {
1401        *total = 0;
1402        *fr = 0;
1403        return;
1404    }
1405    swt->swt_n = cnt;
1406
1407    /* fill in ste_path pointers: we don't care about the paths, so we point
1408       them all to the same buffer */
1409    ste = &(swt->swt_ent[0]);
1410    i = cnt;
1411    while (--i >= 0)
1412    {
1413        ste++->ste_path = path;
1414    }
1415
1416    /* grab all swap info */
1417    swapctl(SC_LIST, swt);
1418
1419    /* walk thru the structs and sum up the fields */
1420    t = f = 0;
1421    ste = &(swt->swt_ent[0]);
1422    i = cnt;
1423    while (--i >= 0)
1424    {
1425        /* dont count slots being deleted */
1426        if (!(ste->ste_flags & ST_INDEL) &&
1427            !(ste->ste_flags & ST_DOINGDEL))
1428        {
1429            t += ste->ste_pages;
1430            f += ste->ste_free;
1431        }
1432        ste++;
1433    }
1434
1435    /* fill in the results */
1436    *total = t;
1437    *fr = f;
1438    free(swt);
1439#endif /* SC_AINFO */
1440}
1441#endif /* USE_ANONINFO */
1442
1443/*
1444 * When we reach a proc limit, we need to realloc the stuff.
1445 */
1446static void reallocproc(int n)
1447{
1448    int bytes;
1449    struct oldproc *op, *endbase;
1450
1451    if (n < maxprocs)
1452        return;
1453
1454    maxprocs = n;
1455
1456    /* allocate space for proc structure array and array of pointers */
1457    bytes = maxprocs * PRPSINFOSIZE;
1458    pbase = (struct prpsinfo *) realloc(pbase, bytes);
1459    pref = (struct prpsinfo **) realloc(pref,
1460                        maxprocs * sizeof(struct prpsinfo *));
1461    oldbase = (struct oldproc *) realloc(oldbase,
1462                        2 * maxprocs * sizeof(struct oldproc));
1463
1464    /* Just in case ... */
1465    if (pbase == (struct prpsinfo *) NULL || pref == (struct prpsinfo **) NULL
1466        || oldbase == (struct oldproc *) NULL)
1467      {
1468        fprintf (stderr, "%s: can't allocate sufficient memory\n", myname);
1469        quit(1);
1470      }
1471
1472    /*
1473     * We're growing from 0 to some number, only then we need to
1474     * init the oldproc stuff
1475     */
1476    if (!oldprocs) {
1477        oldprocs = 2 * maxprocs;
1478
1479        endbase = oldbase + oldprocs;
1480        for (op = oldbase; op < endbase; op++)
1481          op->oldpid = -1;
1482    }
1483}
Note: See TracBrowser for help on using the repository browser.