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

Revision 16185, 21.4 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:  For a FreeBSD-2.0 (4.4BSD) system
5 *            Note process resident sizes could be wrong, but ps shows
6 *            zero for them too..
7 *
8 * DESCRIPTION:
9 * Originally written for BSD4.4 system by Christos Zoulas.
10 * Ported to FreeBSD 2.0 by Steven Wallace && Wolfram Schneider
11 *
12 * This is the machine-dependent module for FreeBSD 2.x
13 * Works for:
14 *      FreeBSD 2.0
15 *      FreeBSD 2.1.5
16 *      FreeBSD 2.2
17 *
18 * LIBS: -lkvm
19 *
20 * CFLAGS: -DHAVE_GETOPT
21 *
22 * AUTHOR:  Christos Zoulas <christos@ee.cornell.edu>
23 *          Steven Wallace  <swallace@freebsd.org>
24 *          Wolfram Schneider <wosch@cs.tu-berlin.de>
25 *
26 * $Id: m_freebsd20.c,v 1.1.1.2 2001-05-08 21:47:43 ghudson Exp $
27 */
28
29
30
31#define LASTPID      /**/  /* use last pid, compiler depended */
32/* #define LASTPID_FIXED /**/
33#define VM_REAL      /**/  /* use the same values as vmstat -s */
34#define USE_SWAP     /**/  /* use swap usage (pstat -s),
35                              need to much cpu time */
36/* #define DEBUG 1      /**/
37
38#include <sys/types.h>
39#include <sys/signal.h>
40#include <sys/param.h>
41
42#include "os.h"
43#include <stdio.h>
44#include <nlist.h>
45#include <math.h>
46#include <kvm.h>
47#include <sys/errno.h>
48#include <sys/sysctl.h>
49#include <sys/dir.h>
50#include <sys/dkstat.h>
51#include <sys/file.h>
52#include <sys/time.h>
53
54#include <sys/vmmeter.h>
55#include <sys/user.h>
56
57#ifdef USE_SWAP
58#include <stdlib.h>
59#include <sys/rlist.h>
60#include <sys/conf.h>
61#endif
62
63static int check_nlist __P((struct nlist *));
64static int getkval __P((unsigned long, int *, int, char *));
65extern char* printable __P((char *));
66
67#include "top.h"
68#include "machine.h"
69
70
71/* get_process_info passes back a handle.  This is what it looks like: */
72
73struct handle
74{
75    struct kinfo_proc **next_proc;      /* points to next valid proc pointer */
76    int remaining;              /* number of pointers remaining */
77};
78
79/* declarations for load_avg */
80#include "loadavg.h"
81
82#define PP(pp, field) ((pp)->kp_proc . field)
83#define EP(pp, field) ((pp)->kp_eproc . field)
84#define VP(pp, field) ((pp)->kp_eproc.e_vm . field)
85
86/* define what weighted cpu is.  */
87#define weighted_cpu(pct, pp) (PP((pp), p_swtime) == 0 ? 0.0 : \
88                         ((pct) / (1.0 - exp(PP((pp), p_swtime) * logcpu))))
89
90/* what we consider to be process size: */
91#define PROCSIZE(pp) (VP((pp), vm_tsize) + VP((pp), vm_dsize) + VP((pp), vm_ssize))
92
93/* definitions for indices in the nlist array */
94
95
96static struct nlist nlst[] = {
97#define X_CCPU          0
98    { "_ccpu" },                /* 0 */
99#define X_CP_TIME       1
100    { "_cp_time" },             /* 1 */
101#define X_HZ            2
102    { "_hz" },                  /* 2 */
103#define X_STATHZ        3
104    { "_stathz" },              /* 3 */
105#define X_AVENRUN       4
106    { "_averunnable" },         /* 4 */
107#ifdef USE_SWAP
108#define VM_SWAPLIST     5
109        { "_swaplist" },/* list of free swap areas */
110#define VM_SWDEVT       6
111        { "_swdevt" },  /* list of swap devices and sizes */
112#define VM_NSWAP        7
113        { "_nswap" },   /* size of largest swap device */
114#define VM_NSWDEV       8
115        { "_nswdev" },  /* number of swap devices */
116#define VM_DMMAX        9
117        { "_dmmax" },   /* maximum size of a swap block */
118#endif
119#ifdef VM_REAL
120#ifdef USE_SWAP
121#define X_CNT           10
122#else
123#define X_CNT           5
124#endif
125    { "_cnt" },                 /* struct vmmeter cnt */
126#endif
127
128#ifdef LASTPID
129#if (defined USE_SWAP && defined VM_REAL)
130#define X_LASTPID       11
131#elif (defined VM_REAL)
132#define X_LASTPID       6
133#else
134#define X_LASTPID       5
135#endif
136#ifdef LASTPID_FIXED
137    { "_nextpid" },             
138#else
139    { "_nextpid.178" },         /* lastpid, compiler depended
140                                 * should be changed
141                                 * in /sys/kern/kern_fork.c */
142#endif
143#endif
144
145    { 0 }
146};
147
148/*
149 *  These definitions control the format of the per-process area
150 */
151
152static char header[] =
153  "  PID X        PRI NICE   SIZE   RES STATE   TIME   WCPU    CPU COMMAND";
154/* 0123456   -- field to fill in starts at header+6 */
155#define UNAME_START 6
156
157#define Proc_format \
158        "%5d %-8.8s %3d %4d%7s %5s %-5s%7s %5.2f%% %5.2f%% %.14s"
159
160
161/* process state names for the "STATE" column of the display */
162/* the extra nulls in the string "run" are for adding a slash and
163   the processor number when needed */
164
165char *state_abbrev[] =
166{
167    "", "start", "run\0\0\0", "sleep", "stop", "zomb", "WAIT"
168};
169
170
171static kvm_t *kd;
172
173/* values that we stash away in _init and use in later routines */
174
175static double logcpu;
176
177/* these are retrieved from the kernel in _init */
178
179static          long hz;
180static load_avg  ccpu;
181
182/* these are offsets obtained via nlist and used in the get_ functions */
183
184static unsigned long cp_time_offset;
185static unsigned long avenrun_offset;
186#ifdef LASTPID
187static unsigned long lastpid_offset;
188static long lastpid;
189#endif
190#ifdef VM_REAL
191static unsigned long cnt_offset;
192static long cnt;
193#endif
194/* these are for calculating cpu state percentages */
195
196static long cp_time[CPUSTATES];
197static long cp_old[CPUSTATES];
198static long cp_diff[CPUSTATES];
199
200/* these are for detailing the process states */
201
202int process_states[7];
203char *procstatenames[] = {
204    "", " starting, ", " running, ", " sleeping, ", " stopped, ",
205    " zombie, ", " ABANDONED, ",
206    NULL
207};
208
209/* these are for detailing the cpu states */
210
211int cpu_states[CPUSTATES];
212char *cpustatenames[] = {
213    "user", "nice", "system", "interrupt", "idle", NULL
214};
215
216/* these are for detailing the memory statistics */
217
218int memory_stats[8];
219char *memorynames[] = {
220#ifndef VM_REAL
221    "Real: ", "K/", "K ", "Virt: ", "K/",
222    "K ", "Free: ", "K", NULL
223#else
224#if 0
225    "K Act ", "K Inact ", "K Wired ", "K Free ", "% Swap, ",
226    "K/", "K SWIO",
227#else
228    "K Act ", "K Inact ", "K Wired ", "K Free ", "% Swap, ",
229    "Kin ", "Kout",
230#endif
231    NULL
232#endif
233};
234
235/* these are for keeping track of the proc array */
236
237static int nproc;
238static int onproc = -1;
239static int pref_len;
240static struct kinfo_proc *pbase;
241static struct kinfo_proc **pref;
242
243/* these are for getting the memory statistics */
244
245static int pageshift;           /* log base 2 of the pagesize */
246
247/* define pagetok in terms of pageshift */
248
249#define pagetok(size) ((size) << pageshift)
250
251/* useful externals */
252long percentages();
253
254int
255machine_init(statics)
256
257struct statics *statics;
258
259{
260    register int i = 0;
261    register int pagesize;
262
263    if ((kd = kvm_open(NULL, NULL, NULL, O_RDONLY, "kvm_open")) == NULL)
264        return -1;
265
266
267    /* get the list of symbols we want to access in the kernel */
268    (void) kvm_nlist(kd, nlst);
269    if (nlst[0].n_type == 0)
270    {
271        fprintf(stderr, "top: nlist failed\n");
272        return(-1);
273    }
274
275    /* make sure they were all found */
276    if (i > 0 && check_nlist(nlst) > 0)
277    {
278        return(-1);
279    }
280
281    /* get the symbol values out of kmem */
282    (void) getkval(nlst[X_STATHZ].n_value, (int *)(&hz), sizeof(hz), "!");
283    if (!hz) {
284        (void) getkval(nlst[X_HZ].n_value, (int *)(&hz), sizeof(hz),
285                       nlst[X_HZ].n_name);
286    }
287
288
289#if (defined DEBUG)
290    fprintf(stderr, "Hertz: %d\n", hz);
291#endif
292
293    (void) getkval(nlst[X_CCPU].n_value,   (int *)(&ccpu),      sizeof(ccpu),
294            nlst[X_CCPU].n_name);
295
296    /* stash away certain offsets for later use */
297    cp_time_offset = nlst[X_CP_TIME].n_value;
298    avenrun_offset = nlst[X_AVENRUN].n_value;
299#ifdef LASTPID
300    lastpid_offset =  nlst[X_LASTPID].n_value;
301#endif
302#ifdef VM_REAL
303    cnt_offset = nlst[X_CNT].n_value;
304#endif
305
306    /* this is used in calculating WCPU -- calculate it ahead of time */
307    logcpu = log(loaddouble(ccpu));
308
309    pbase = NULL;
310    pref = NULL;
311    nproc = 0;
312    onproc = -1;
313    /* get the page size with "getpagesize" and calculate pageshift from it */
314    pagesize = getpagesize();
315    pageshift = 0;
316    while (pagesize > 1)
317    {
318        pageshift++;
319        pagesize >>= 1;
320    }
321
322    /* we only need the amount of log(2)1024 for our conversion */
323    pageshift -= LOG1024;
324
325    /* fill in the statics information */
326    statics->procstate_names = procstatenames;
327    statics->cpustate_names = cpustatenames;
328    statics->memory_names = memorynames;
329
330    /* all done! */
331    return(0);
332}
333
334char *format_header(uname_field)
335
336register char *uname_field;
337
338{
339    register char *ptr;
340
341    ptr = header + UNAME_START;
342    while (*uname_field != '\0')
343    {
344        *ptr++ = *uname_field++;
345    }
346
347    return(header);
348}
349
350static int swappgsin = -1;
351static int swappgsout = -1;
352extern struct timeval timeout;
353
354void
355get_system_info(si)
356
357struct system_info *si;
358
359{
360    long total;
361    load_avg avenrun[3];
362
363    /* get the cp_time array */
364    (void) getkval(cp_time_offset, (int *)cp_time, sizeof(cp_time),
365                   nlst[X_CP_TIME].n_name);
366    (void) getkval(avenrun_offset, (int *)avenrun, sizeof(avenrun),
367                   nlst[X_AVENRUN].n_name);
368
369#ifdef LASTPID
370    (void) getkval(lastpid_offset, (int *)(&lastpid), sizeof(lastpid),
371                   "!");
372#endif
373
374    /* convert load averages to doubles */
375    {
376        register int i;
377        register double *infoloadp;
378        load_avg *avenrunp;
379
380#ifdef notyet
381        struct loadavg sysload;
382        int size;
383        getkerninfo(KINFO_LOADAVG, &sysload, &size, 0);
384#endif
385
386        infoloadp = si->load_avg;
387        avenrunp = avenrun;
388        for (i = 0; i < 3; i++)
389        {
390#ifdef notyet
391            *infoloadp++ = ((double) sysload.ldavg[i]) / sysload.fscale;
392#endif
393            *infoloadp++ = loaddouble(*avenrunp++);
394        }
395    }
396
397    /* convert cp_time counts to percentages */
398    total = percentages(CPUSTATES, cpu_states, cp_time, cp_old, cp_diff);
399
400    /* sum memory statistics */
401    {
402
403#ifndef VM_REAL
404        struct vmtotal total;
405        int size = sizeof(total);
406        static int mib[] = { CTL_VM, VM_METER };
407
408        /* get total -- systemwide main memory usage structure */
409        if (sysctl(mib, 2, &total, &size, NULL, 0) < 0) {
410            (void) fprintf(stderr, "top: sysctl failed: %s\n", strerror(errno));
411            bzero(&total, sizeof(total));
412        }
413        /* convert memory stats to Kbytes */
414        memory_stats[0] = -1;
415        memory_stats[1] = pagetok(total.t_arm);
416        memory_stats[2] = pagetok(total.t_rm);
417        memory_stats[3] = -1;
418        memory_stats[4] = pagetok(total.t_avm);
419        memory_stats[5] = pagetok(total.t_vm);
420        memory_stats[6] = -1;
421        memory_stats[7] = pagetok(total.t_free);
422    }
423#else
424        struct vmmeter sum;
425        static unsigned int swap_delay = 0;
426
427        (void) getkval(cnt_offset, (int *)(&sum), sizeof(sum),
428                   "_cnt");
429
430        /* convert memory stats to Kbytes */
431        memory_stats[0] = pagetok(sum.v_active_count);
432        memory_stats[1] = pagetok(sum.v_inactive_count);
433        memory_stats[2] = pagetok(sum.v_wire_count);
434        memory_stats[3] = pagetok(sum.v_free_count);
435
436        if (swappgsin < 0) {
437            memory_stats[5] = 0;
438            memory_stats[6] = 0;
439        } else {
440            memory_stats[5] = pagetok(((sum.v_swappgsin - swappgsin)));
441            memory_stats[6] = pagetok(((sum.v_swappgsout - swappgsout)));
442        }
443        swappgsin = sum.v_swappgsin;
444        swappgsout = sum.v_swappgsout;
445
446#ifdef USE_SWAP
447        if ((memory_stats[5] > 0 || memory_stats[6]) > 0 || swap_delay == 0) {
448            memory_stats[4] = swapmode();
449        }
450        swap_delay++;
451#else
452        memory_stats[4] = 0;
453#endif
454
455
456        memory_stats[7] = -1;
457    }
458#endif
459    /* set arrays and strings */
460    si->cpustates = cpu_states;
461    si->memory = memory_stats;
462#ifdef LASTPID
463    if(lastpid > 0) {
464        si->last_pid = lastpid;
465    } else {
466        si->last_pid = -1;
467    }
468#else
469    si->last_pid = -1;
470#endif
471
472}
473
474static struct handle handle;
475
476caddr_t get_process_info(si, sel, compare)
477
478struct system_info *si;
479struct process_select *sel;
480int (*compare)();
481
482{
483    register int i;
484    register int total_procs;
485    register int active_procs;
486    register struct kinfo_proc **prefp;
487    register struct kinfo_proc *pp;
488
489    /* these are copied out of sel for speed */
490    int show_idle;
491    int show_system;
492    int show_uid;
493    int show_command;
494
495   
496    pbase = kvm_getprocs(kd, KERN_PROC_ALL, 0, &nproc);
497    if (nproc > onproc)
498        pref = (struct kinfo_proc **) realloc(pref, sizeof(struct kinfo_proc *)
499                * (onproc = nproc));
500    if (pref == NULL || pbase == NULL) {
501        (void) fprintf(stderr, "top: Out of memory.\n");
502        quit(23);
503    }
504    /* get a pointer to the states summary array */
505    si->procstates = process_states;
506
507    /* set up flags which define what we are going to select */
508    show_idle = sel->idle;
509    show_system = sel->system;
510    show_uid = sel->uid != -1;
511    show_command = sel->command != NULL;
512
513    /* count up process states and get pointers to interesting procs */
514    total_procs = 0;
515    active_procs = 0;
516    memset((char *)process_states, 0, sizeof(process_states));
517    prefp = pref;
518    for (pp = pbase, i = 0; i < nproc; pp++, i++)
519    {
520        /*
521         *  Place pointers to each valid proc structure in pref[].
522         *  Process slots that are actually in use have a non-zero
523         *  status field.  Processes with P_SYSTEM set are system
524         *  processes---these get ignored unless show_sysprocs is set.
525         */
526        if (PP(pp, p_stat) != 0 &&
527            (show_system || ((PP(pp, p_flag) & P_SYSTEM) == 0)))
528        {
529            total_procs++;
530            process_states[(unsigned char) PP(pp, p_stat)]++;
531            if ((PP(pp, p_stat) != SZOMB) &&
532                (show_idle || (PP(pp, p_pctcpu) != 0) ||
533                 (PP(pp, p_stat) == SRUN)) &&
534                (!show_uid || EP(pp, e_pcred.p_ruid) == (uid_t)sel->uid))
535            {
536                *prefp++ = pp;
537                active_procs++;
538            }
539        }
540    }
541
542    /* if requested, sort the "interesting" processes */
543    if (compare != NULL)
544    {
545        qsort((char *)pref, active_procs, sizeof(struct kinfo_proc *), compare);
546    }
547
548    /* remember active and total counts */
549    si->p_total = total_procs;
550    si->p_active = pref_len = active_procs;
551
552    /* pass back a handle */
553    handle.next_proc = pref;
554    handle.remaining = active_procs;
555    return((caddr_t)&handle);
556}
557
558char fmt[128];          /* static area where result is built */
559
560char *format_next_process(handle, get_userid)
561
562caddr_t handle;
563char *(*get_userid)();
564
565{
566    register struct kinfo_proc *pp;
567    register long cputime;
568    register double pct;
569    struct handle *hp;
570
571    /* find and remember the next proc structure */
572    hp = (struct handle *)handle;
573    pp = *(hp->next_proc++);
574    hp->remaining--;
575   
576
577    /* get the process's user struct and set cputime */
578    if ((PP(pp, p_flag) & P_INMEM) == 0) {
579        /*
580         * Print swapped processes as <pname>
581         */
582        char *comm = PP(pp, p_comm);
583#define COMSIZ sizeof(PP(pp, p_comm))
584        char buf[COMSIZ];
585        (void) strncpy(buf, comm, COMSIZ);
586        comm[0] = '<';
587        (void) strncpy(&comm[1], buf, COMSIZ - 2);
588        comm[COMSIZ - 2] = '\0';
589        (void) strncat(comm, ">", COMSIZ - 1);
590        comm[COMSIZ - 1] = '\0';
591    }
592
593#if 0
594    /* This does not produce the correct results */
595    cputime = PP(pp, p_uticks) + PP(pp, p_sticks) + PP(pp, p_iticks);
596#endif
597    cputime = PP(pp, p_rtime).tv_sec;   /* This does not count interrupts */
598
599    /* calculate the base for cpu percentages */
600    pct = pctdouble(PP(pp, p_pctcpu));
601
602    /* format this entry */
603    sprintf(fmt,
604            Proc_format,
605            PP(pp, p_pid),
606            (*get_userid)(EP(pp, e_pcred.p_ruid)),
607            PP(pp, p_priority) - PZERO,
608            PP(pp, p_nice) - NZERO,
609            format_k(pagetok(PROCSIZE(pp))),
610            format_k(pagetok(VP(pp, vm_rssize))),
611            state_abbrev[(unsigned char) PP(pp, p_stat)],
612            format_time(cputime),
613            10000.0 * weighted_cpu(pct, pp) / hz,
614            10000.0 * pct / hz,
615            printable(PP(pp, p_comm)));
616
617    /* return the result */
618    return(fmt);
619}
620
621
622/*
623 * check_nlist(nlst) - checks the nlist to see if any symbols were not
624 *              found.  For every symbol that was not found, a one-line
625 *              message is printed to stderr.  The routine returns the
626 *              number of symbols NOT found.
627 */
628
629static int check_nlist(nlst)
630
631register struct nlist *nlst;
632
633{
634    register int i;
635
636    /* check to see if we got ALL the symbols we requested */
637    /* this will write one line to stderr for every symbol not found */
638
639    i = 0;
640    while (nlst->n_name != NULL)
641    {
642        if (nlst->n_type == 0)
643        {
644            /* this one wasn't found */
645            (void) fprintf(stderr, "kernel: no symbol named `%s'\n",
646                           nlst->n_name);
647            i = 1;
648        }
649        nlst++;
650    }
651
652    return(i);
653}
654
655
656/*
657 *  getkval(offset, ptr, size, refstr) - get a value out of the kernel.
658 *      "offset" is the byte offset into the kernel for the desired value,
659 *      "ptr" points to a buffer into which the value is retrieved,
660 *      "size" is the size of the buffer (and the object to retrieve),
661 *      "refstr" is a reference string used when printing error meessages,
662 *          if "refstr" starts with a '!', then a failure on read will not
663 *          be fatal (this may seem like a silly way to do things, but I
664 *          really didn't want the overhead of another argument).
665 *     
666 */
667
668static int getkval(offset, ptr, size, refstr)
669
670unsigned long offset;
671int *ptr;
672int size;
673char *refstr;
674
675{
676    if (kvm_read(kd, offset, (char *) ptr, size) != size)
677    {
678        if (*refstr == '!')
679        {
680            return(0);
681        }
682        else
683        {
684            fprintf(stderr, "top: kvm_read for %s: %s\n",
685                refstr, strerror(errno));
686            quit(23);
687        }
688    }
689    return(1);
690}
691   
692/* comparison routine for qsort */
693
694/*
695 *  proc_compare - comparison function for "qsort"
696 *      Compares the resource consumption of two processes using five
697 *      distinct keys.  The keys (in descending order of importance) are:
698 *      percent cpu, cpu ticks, state, resident set size, total virtual
699 *      memory usage.  The process states are ordered as follows (from least
700 *      to most important):  WAIT, zombie, sleep, stop, start, run.  The
701 *      array declaration below maps a process state index into a number
702 *      that reflects this ordering.
703 */
704
705static unsigned char sorted_state[] =
706{
707    0,  /* not used             */
708    3,  /* sleep                */
709    1,  /* ABANDONED (WAIT)     */
710    6,  /* run                  */
711    5,  /* start                */
712    2,  /* zombie               */
713    4   /* stop                 */
714};
715 
716int
717proc_compare(pp1, pp2)
718
719struct proc **pp1;
720struct proc **pp2;
721
722{
723    register struct kinfo_proc *p1;
724    register struct kinfo_proc *p2;
725    register int result;
726    register pctcpu lresult;
727
728    /* remove one level of indirection */
729    p1 = *(struct kinfo_proc **) pp1;
730    p2 = *(struct kinfo_proc **) pp2;
731
732    /* compare percent cpu (pctcpu) */
733    if ((lresult = PP(p2, p_pctcpu) - PP(p1, p_pctcpu)) == 0)
734    {
735        /* use cpticks to break the tie */
736        if ((result = PP(p2, p_cpticks) - PP(p1, p_cpticks)) == 0)
737        {
738            /* use process state to break the tie */
739            if ((result = sorted_state[(unsigned char) PP(p2, p_stat)] -
740                          sorted_state[(unsigned char) PP(p1, p_stat)])  == 0)
741            {
742                /* use priority to break the tie */
743                if ((result = PP(p2, p_priority) - PP(p1, p_priority)) == 0)
744                {
745                    /* use resident set size (rssize) to break the tie */
746                    if ((result = VP(p2, vm_rssize) - VP(p1, vm_rssize)) == 0)
747                    {
748                        /* use total memory to break the tie */
749                        result = PROCSIZE(p2) - PROCSIZE(p1);
750                    }
751                }
752            }
753        }
754    }
755    else
756    {
757        result = lresult < 0 ? -1 : 1;
758    }
759
760    return(result);
761}
762
763
764/*
765 * proc_owner(pid) - returns the uid that owns process "pid", or -1 if
766 *              the process does not exist.
767 *              It is EXTREMLY IMPORTANT that this function work correctly.
768 *              If top runs setuid root (as in SVR4), then this function
769 *              is the only thing that stands in the way of a serious
770 *              security problem.  It validates requests for the "kill"
771 *              and "renice" commands.
772 */
773
774int proc_owner(pid)
775
776int pid;
777
778{
779    register int cnt;
780    register struct kinfo_proc **prefp;
781    register struct kinfo_proc *pp;
782
783    prefp = pref;
784    cnt = pref_len;
785    while (--cnt >= 0)
786    {
787        pp = *prefp++; 
788        if (PP(pp, p_pid) == (pid_t)pid)
789        {
790            return((int)EP(pp, e_pcred.p_ruid));
791        }
792    }
793    return(-1);
794}
795
796
797#ifdef USE_SWAP
798/*
799 * swapmode is based on a program called swapinfo written
800 * by Kevin Lahey <kml@rokkaku.atl.ga.us>.
801 */
802
803#define SVAR(var) __STRING(var) /* to force expansion */
804#define KGET(idx, var)                                                  \
805        KGET1(idx, &var, sizeof(var), SVAR(var))
806#define KGET1(idx, p, s, msg)                                           \
807        KGET2(nlst[idx].n_value, p, s, msg)
808#define KGET2(addr, p, s, msg)                                          \
809        if (kvm_read(kd, (u_long)(addr), p, s) != s)                    \
810                warnx("cannot read %s: %s", msg, kvm_geterr(kd))
811#define KGETRET(addr, p, s, msg)                                        \
812        if (kvm_read(kd, (u_long)(addr), p, s) != s) {                  \
813                warnx("cannot read %s: %s", msg, kvm_geterr(kd));       \
814                return (0);                                             \
815        }
816
817
818int
819swapmode()
820{
821        char *header;
822        int hlen, nswap, nswdev, dmmax;
823        int i, div, avail, nfree, npfree, used;
824        struct swdevt *sw;
825        long blocksize, *perdev;
826        struct rlist head;
827        struct rlist *swaplist;
828
829        KGET(VM_NSWAP, nswap);
830        KGET(VM_NSWDEV, nswdev);
831        KGET(VM_DMMAX, dmmax);
832        KGET(VM_SWAPLIST, swaplist);
833        if ((sw = (struct swdevt *)malloc(nswdev * sizeof(*sw))) == NULL ||
834            (perdev = (long *)malloc(nswdev * sizeof(*perdev))) == NULL)
835                err(1, "malloc");
836        KGET1(VM_SWDEVT, sw, nswdev * sizeof(*sw), "swdevt");
837
838        /* Count up swap space. */
839        nfree = 0;
840        memset(perdev, 0, nswdev * sizeof(*perdev));
841        while (swaplist) {
842                int     top, bottom, next_block;
843
844                KGET2(swaplist, &head, sizeof(struct rlist), "swaplist");
845
846                top = head.rl_end;
847                bottom = head.rl_start;
848
849                nfree += top - bottom + 1;
850
851                /*
852                 * Swap space is split up among the configured disks.
853                 *
854                 * For interleaved swap devices, the first dmmax blocks
855                 * of swap space some from the first disk, the next dmmax
856                 * blocks from the next, and so on up to nswap blocks.
857                 *
858                 * The list of free space joins adjacent free blocks,
859                 * ignoring device boundries.  If we want to keep track
860                 * of this information per device, we'll just have to
861                 * extract it ourselves.
862                 */
863                while (top / dmmax != bottom / dmmax) {
864                        next_block = ((bottom + dmmax) / dmmax);
865                        perdev[(bottom / dmmax) % nswdev] +=
866                                next_block * dmmax - bottom;
867                        bottom = next_block * dmmax;
868                }
869                perdev[(bottom / dmmax) % nswdev] +=
870                        top - bottom + 1;
871
872                swaplist = head.rl_next;
873        }
874
875        header = getbsize(&hlen, &blocksize);
876        div = blocksize / 512;
877        avail = npfree = 0;
878        for (i = 0; i < nswdev; i++) {
879                int xsize, xfree;
880
881                /*
882                 * Don't report statistics for partitions which have not
883                 * yet been activated via swapon(8).
884                 */
885
886                xsize = sw[i].sw_nblks;
887                xfree = perdev[i];
888                used = xsize - xfree;
889                npfree++;
890                avail += xsize;
891        }
892
893        /*
894         * If only one partition has been set up via swapon(8), we don't
895         * need to bother with totals.
896         */
897        used = avail - nfree;
898        free(sw); free(perdev);
899        return  (int)(((double)used / (double)avail * 100.0) + 0.5);
900}
901
902#endif
903
Note: See TracBrowser for help on using the repository browser.