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

Revision 16185, 16.9 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:  PowerPC running AIX 4.2 or higher
5 *
6 * DESCRIPTION:
7 * This is the machine-dependent module for AIX 4.2 and higher
8 * It is currenlty only tested on PowerPC architectures.
9 *
10 * TERMCAP: -lcurses
11 *
12 * CFLAGS: -DORDER -DHAVE_GETOPT
13 *
14 * LIBS: -bD:0x18000000
15 *
16 * AUTHOR:  Joep Vesseur <joep@fwi.uva.nl>
17 *
18 * PATCHES: Antoine Tabary <tabary@bruyeres.cea.fr>
19 */
20
21#include <stdlib.h>
22#include <stdio.h>
23#include <fcntl.h>
24#include <nlist.h>
25#include <sys/sysinfo.h>
26#include <procinfo.h>
27#include <sys/proc.h>
28#include <pwd.h>
29#include "top.h"
30#include "machine.h"
31
32
33#define PROCRESS(p) (((p)->pi_trss + (p)->pi_drss)*4)
34#define PROCSIZE(p) (((p)->pi_tsize/1024+(p)->pi_dvm)*4)
35#define PROCTIME(pi) (pi->pi_ru.ru_utime.tv_sec + pi->pi_ru.ru_stime.tv_sec)
36
37
38/*
39 * structure definition taken from 'monitor' by Jussi Maki (jmaki@hut.fi)
40 */
41struct vmker {
42    uint n0,n1,n2,n3,n4,n5,n6,n7,n8;
43    uint totalmem;
44    uint badmem; /* this is used in RS/6000 model 220 */
45    uint freemem;
46    uint n12;
47    uint numperm;   /* this seems to keep other than text and data segment
48                       usage; name taken from /usr/lpp/bos/samples/vmtune.c */
49    uint totalvmem,freevmem;
50    uint n15, n16, n17, n18, n19;
51};
52
53
54#define KMEM "/dev/kmem"
55
56/* Indices in the nlist array */
57#define X_AVENRUN       0
58#define X_SYSINFO       1
59#define X_VMKER         2
60#define X_PROC          3
61#define X_V             4
62
63static struct nlist nlst[] = {
64    { "avenrun", 0, 0, 0, 0, 0 }, /* 0 */
65    { "sysinfo", 0, 0, 0, 0, 0 }, /* 1 */
66    { "vmker",   0, 0, 0, 0, 0 }, /* 2 */
67    { "proc",    0, 0, 0, 0, 0 }, /* 3 */
68    { "v",       0, 0, 0, 0, 0 }, /* 4 */
69    {  NULL, 0, 0, 0, 0, 0 }
70};
71
72
73/* get_process_info returns handle. definition is here */
74struct handle
75{
76        struct procsinfo **next_proc;
77        int remaining;
78};
79
80/*
81 *  These definitions control the format of the per-process area
82 */
83static char header[] =
84  "   PID X        PRI NICE   SIZE   RES STATE   TIME   WCPU    CPU COMMAND";
85/* 0123456   -- field to fill in starts at header+6 */
86#define UNAME_START 7
87
88#define Proc_format \
89        "%6d %-8.8s %3d %4d %5d%c %4d%c %-5s %6s %5.2f%% %5.2f%% %.14s%s"
90
91
92/* these are for detailing the process states */
93int process_states[9];
94char *procstatenames[] = {
95    " none, ", " sleeping, ", " state2, ", " runnable, ",
96    " idle, ", " zombie, ", " stopped, ", " running, ", " swapped, ",
97    NULL
98};
99
100
101/* these are for detailing the cpu states */
102int cpu_states[4];
103char *cpustatenames[] = {
104    "idle", "user", "kernel", "wait",
105    NULL
106};
107
108/* these are for detailing the memory statistics */
109int memory_stats[7];
110char *memorynames[] = {
111    "M Total. Real: ", "M, ", "M Free, ", "M Buffers. Virtual: ", "M, ", "M Free, ", NULL
112};
113#define M_TOTAL    0
114#define M_REAL     1
115#define M_REALFREE 2
116#define M_BUFFERS  3
117#define M_VIRTUAL  4
118#define M_VIRTFREE 5
119
120char *state_abbrev[] = {
121    "", "sleep", "", "", "sleep", "zomb", "stop", "run", "swap"
122};
123
124/* sorting orders. first is default */
125char *ordernames[] = {
126    "cpu", "size", "res", "time", "pri", NULL
127};
128
129/* compare routines */
130int compare_cpu(), compare_size(), compare_res(), compare_time(),
131    compare_prio();
132
133int (*proc_compares[])() = {
134    compare_cpu,
135    compare_size,
136    compare_res,
137    compare_time,
138    compare_prio,
139    NULL
140};
141
142/* useful externals */
143extern int errno;
144extern char *sys_errlist[];
145long lseek();
146long time();
147long percentages();
148
149
150/* useful globals */
151int kmem;                       /* file descriptor */
152
153/* offsets in kernel */
154static unsigned long avenrun_offset;
155static unsigned long sysinfo_offset;
156static unsigned long vmker_offset;
157static unsigned long proc_offset;
158static unsigned long v_offset;
159
160/* used for calculating cpu state percentages */
161static long cp_time[CPU_NTIMES];
162static long cp_old[CPU_NTIMES];
163static long cp_diff[CPU_NTIMES];
164
165/* the runqueue length is a cumulative value. keep old value */
166long old_runque;
167
168/* process info */
169struct var v_info;              /* to determine nprocs */
170int nprocs;                     /* maximum nr of procs in proctab */
171int ncpus;                      /* nr of cpus installed */
172
173int ptsize;                     /* size of process table in bytes */
174struct proc *p_proc;            /* a copy of the process table */
175struct procsinfo *p_info;       /* needed for vm and ru info */
176struct procsinfo **pref;        /* processes selected for display */
177int pref_len;                   /* number of processes selected */
178
179/* needed to calculate WCPU */
180unsigned long curtime;
181
182
183/*
184 * Initialize globals, get kernel offsets and stuff...
185 */
186machine_init(statics)
187    struct statics *statics;
188{
189    if ((kmem = open(KMEM, O_RDONLY)) == -1) {
190        perror(KMEM);
191        return -1;
192    }
193
194    /* get kernel symbol offsets */
195    if (knlist(nlst, 5, sizeof(struct nlist)) != 0) {
196        perror("knlist");
197        return -1;
198    }
199    avenrun_offset = nlst[X_AVENRUN].n_value;
200    sysinfo_offset = nlst[X_SYSINFO].n_value;
201    vmker_offset   = nlst[X_VMKER].n_value;
202    proc_offset    = nlst[X_PROC].n_value;
203    v_offset       = nlst[X_V].n_value;
204
205    getkval(v_offset, (caddr_t)&v_info, sizeof v_info, "v");
206
207    ncpus = v_info.v_ncpus;     /* number of cpus */
208    nprocs = PROCMASK(PIDMAX);
209
210    ptsize = nprocs * sizeof (struct proc);
211    p_proc = (struct proc *)malloc(ptsize);
212    p_info = (struct procsinfo *)malloc(nprocs * sizeof (struct procsinfo));
213    pref = (struct procsinfo **)malloc(nprocs * sizeof (struct procsinfo *));
214
215    if (!p_proc || !p_info || !pref) {
216        fprintf(stderr, "top: not enough memory\n");
217        return -1;
218    }
219
220    statics->procstate_names = procstatenames;
221    statics->cpustate_names = cpustatenames;
222    statics->memory_names = memorynames;
223    statics->order_names = ordernames;
224
225    return(0);
226}
227
228
229
230char *format_header(uname_field)
231    register char *uname_field;
232{
233    register char *ptr;
234
235    ptr = header + UNAME_START;
236    while (*uname_field != '\0')
237    {
238        *ptr++ = *uname_field++;
239    }
240
241    return(header);
242}
243
244
245
246
247get_system_info(si)
248    struct system_info *si;
249{
250    int load_avg[3];
251    struct sysinfo s_info;
252    struct vmker m_info;
253    int i;
254    double total = 0;
255
256    /* get the load avarage array */
257    getkval(avenrun_offset, (caddr_t)load_avg, sizeof load_avg, "avenrun");
258
259    /* get the sysinfo structure */
260    getkval(sysinfo_offset, (caddr_t)&s_info, sizeof s_info, "sysinfo");
261
262    /* get vmker structure */
263    getkval(vmker_offset, (caddr_t)&m_info, sizeof m_info, "vmker");
264
265    /* convert load avarages to doubles */
266    for (i = 0; i < 3; i++)
267        si->load_avg[i] = (double)load_avg[i]/65536.0;
268
269    /* calculate cpu state in percentages */
270    for (i = 0; i < CPU_NTIMES; i++) {
271        cp_old[i] = cp_time[i];
272        cp_time[i] = s_info.cpu[i];
273        cp_diff[i] = cp_time[i] - cp_old[i];
274        total += cp_diff[i];
275    }
276
277    total = total/1000.0;  /* top itself will correct this */
278    for (i = 0; i < CPU_NTIMES; i++) {
279        cpu_states[i] = cp_diff[i] / total;
280    }
281
282    /* calculate memory statistics, scale 4K pages to megabytes */
283#define PAGE_TO_MB(a) ((a)*4/1024)
284    memory_stats[M_TOTAL]    = PAGE_TO_MB(m_info.totalmem+m_info.totalvmem);
285    memory_stats[M_REAL]     = PAGE_TO_MB(m_info.totalmem);
286    memory_stats[M_REALFREE] = PAGE_TO_MB(m_info.freemem);
287    memory_stats[M_BUFFERS]  = PAGE_TO_MB(m_info.numperm);
288    memory_stats[M_VIRTUAL]  = PAGE_TO_MB(m_info.totalvmem);
289    memory_stats[M_VIRTFREE] = PAGE_TO_MB(m_info.freevmem);
290
291    /* runnable processes */
292    process_states[0] = s_info.runque - old_runque;
293    old_runque = s_info.runque;
294
295    si->cpustates = cpu_states;
296    si->memory = memory_stats;
297}
298
299static struct handle handle;
300
301caddr_t get_process_info(si, sel, compare)
302    struct system_info *si;
303    struct process_select *sel;
304    int (*compare)();
305{
306    int i, nproc;
307    int ptsize_util;
308    int active_procs = 0, total_procs = 0;
309    struct procsinfo *pp, **p_pref = pref;
310    unsigned long pctcpu;
311    pid_t procsindex = 0;
312    struct proc *p;
313
314    si->procstates = process_states;
315
316    curtime = time(0);
317
318    /* get the procsinfo structures of all running processes */
319    nproc = getprocs(p_info, sizeof (struct procsinfo), NULL, 0,
320                     &procsindex, nprocs);
321    if (nproc < 0) {
322        perror("getprocs");
323        quit(1);
324    }
325
326    /* the swapper has no cmd-line attached */
327    strcpy(p_info[0].pi_comm, "swapper");
328   
329    /* get proc table */
330    ptsize_util = (PROCMASK(p_info[nproc-1].pi_pid)+1) * sizeof(struct proc);
331    getkval(proc_offset, (caddr_t)p_proc, ptsize_util, "proc");
332
333    memset(process_states, 0, sizeof process_states);
334
335    /* build a list of pointers to processes to show. walk through the
336     * list of procsinfo structures instead of the proc table since the
337     * mapping of procsinfo -> proctable is easy, the other way around
338     * is cumbersome
339     */
340    for (pp = p_info, i = 0; i < nproc; pp++, i++) {
341
342        p = &p_proc[PROCMASK(pp->pi_pid)];
343
344        /* AIX marks all runnable processes as ACTIVE. We want to know
345           which processes are sleeping, so check used cpu ticks and adjust
346           status field accordingly
347         */
348        if (p->p_stat == SACTIVE && p->p_cpticks == 0)
349            p->p_stat = SIDL;
350
351        if (pp->pi_state && (sel->system || ((pp->pi_flags & SKPROC) == 0))) {
352            total_procs++;
353            process_states[p->p_stat]++;
354            if ( (pp->pi_state != SZOMB) &&
355                (sel->idle || p->p_cpticks != 0 || (p->p_stat == SACTIVE))
356                && (sel->uid == -1 || pp->pi_uid == (uid_t)sel->uid)) {
357                *p_pref++ = pp;
358                active_procs++;
359            }
360        }
361    }   
362
363    /* the pref array now holds pointers to the procsinfo structures in
364     * the p_info array that were selected for display
365     */
366
367    /* sort if requested */
368    if (compare != NULL)
369        qsort((char *)pref, active_procs, sizeof (struct procsinfo *),
370              compare);
371   
372    si->last_pid = -1;          /* no way to figure out last used pid */
373    si->p_total = total_procs;
374    si->p_active = pref_len = active_procs;
375
376    handle.next_proc = pref;
377    handle.remaining = active_procs;
378
379    return((caddr_t)&handle);
380}
381
382char fmt[128];          /* static area where result is built */
383
384/* define what weighted cpu is. use definition of %CPU from 'man ps(1)' */
385#define weighted_cpu(pp) (PROCTIME(pp) == 0 ? 0.0 : \
386                        (((PROCTIME(pp)*100.0)/(curtime-pi->pi_start)/ncpus)))
387#define double_pctcpu(p) ((double)p->p_pctcpu/(double)FLT_MODULO)
388
389char *format_next_process(handle, get_userid)
390    caddr_t handle;
391    char *(*get_userid)();
392{
393    register struct handle *hp;
394    register struct procsinfo *pi;
395    register struct proc *p;
396    char *uname;
397    long cpu_time;
398    int proc_size, proc_ress;
399    char size_unit = 'K';
400    char ress_unit = 'K';
401
402    hp = (struct handle *)handle;
403    if (hp->remaining == 0) {   /* safe guard */
404        fmt[0] = '\0';
405        return fmt;
406    }
407    pi = *(hp->next_proc++);
408    hp->remaining--;
409    p = &p_proc[PROCMASK(pi->pi_pid)];
410
411    cpu_time = PROCTIME(pi);
412
413    /* we disply sizes up to 10M in KiloBytes, beyond 10M in MegaBytes */
414    if ((proc_size = (pi->pi_tsize/1024+pi->pi_dvm)*4) > 10240) {
415        proc_size /= 1024;
416        size_unit = 'M';
417    }
418    if ((proc_ress = (pi->pi_trss + pi->pi_drss)*4) > 10240) {
419        proc_ress /= 1024;
420        ress_unit = 'M';
421    }
422
423    sprintf(fmt, Proc_format ,
424            pi->pi_pid,                                   /* PID */
425            (*get_userid)(pi->pi_uid),                    /* login name */
426            getpriority(PRIO_PROCESS, pi->pi_pid),
427            EXTRACT_NICE(p),                              /* fixed or vari */
428            proc_size,                                    /* size */
429            size_unit,                                    /* K or M */
430            proc_ress,                                    /* resident */
431            ress_unit,                                    /* K or M */
432            state_abbrev[p->p_stat],                      /* process state */
433            format_time(cpu_time),                        /* time used */
434            weighted_cpu(pi),                             /* WCPU */
435            100.0 * double_pctcpu(p),                     /* CPU */
436            printable(pi->pi_comm),                       /* COMM */
437            (pi->pi_flags & SKPROC) == 0 ? "" : " (sys)"  /* kernel process? */
438            );
439    return(fmt);
440}
441
442
443/*
444 *  getkval(offset, ptr, size, refstr) - get a value out of the kernel.
445 *      "offset" is the byte offset into the kernel for the desired value,
446 *      "ptr" points to a buffer into which the value is retrieved,
447 *      "size" is the size of the buffer (and the object to retrieve),
448 *      "refstr" is a reference string used when printing error meessages,
449 *          if "refstr" starts with a '!', then a failure on read will not
450 *          be fatal (this may seem like a silly way to do things, but I
451 *          really didn't want the overhead of another argument).
452 *     
453 */
454getkval(offset, ptr, size, refstr)
455    unsigned long offset;
456    caddr_t ptr;
457    int size;
458    char *refstr;
459{
460    int upper_2gb = 0;
461
462    /* reads above 2Gb are done by seeking to offset%2Gb, and supplying
463     * 1 (opposed to 0) as fourth parameter to readx (see 'man kmem')
464     */
465    if (offset > 1<<31) {
466        upper_2gb = 1;
467        offset &= 0x7fffffff;
468    }
469
470    if (lseek(kmem, offset, SEEK_SET) != offset) {
471        fprintf(stderr, "top: lseek failed\n");
472        quit(2);
473    }
474
475    if (readx(kmem, ptr, size, upper_2gb) != size) {
476        if (*refstr == '!')
477            return 0;
478        else {
479            fprintf(stderr, "top: kvm_read for %s: %s\n", refstr,
480                    sys_errlist[errno]);
481            quit(2);
482        }
483    }
484
485    return 1 ;
486}
487   
488/* comparison routine for qsort */
489/*
490 * The following code is taken from the solaris module and adjusted
491 * for AIX -- JV .
492 */
493
494#define ORDERKEY_PCTCPU \
495           if (lresult = p2->p_pctcpu - p1->p_pctcpu, \
496               (result = lresult > 0 ? 1 : lresult < 0 ? -1 : 0) == 0)
497
498#define ORDERKEY_CPTICKS \
499           if ((result = PROCTIME(pi2) - PROCTIME(pi1)) == 0)
500
501
502#define ORDERKEY_STATE \
503           if ((result = sorted_state[p2->p_stat]  \
504                         - sorted_state[p1->p_stat])  == 0)
505
506/* Nice values directly reflect the process' priority, and are always >0 ;-) */
507#define ORDERKEY_PRIO \
508           if ((result = EXTRACT_NICE(p1) - EXTRACT_NICE(p2)) == 0)
509
510#define ORDERKEY_RSSIZE \
511           if ((result = PROCRESS(pi2) - PROCRESS(pi1)) == 0)
512#define ORDERKEY_MEM \
513           if ((result = PROCSIZE(pi2) - PROCSIZE(pi1)) == 0)
514
515static unsigned char sorted_state[] =
516{
517    0, /* not used */
518    0,
519    0,
520    0,
521    3,                          /* sleep */
522    1,                          /* zombie */
523    4,                          /* stop */
524    6,                          /* run */
525    2,                          /* swap */
526};
527
528/* compare_cpu - the comparison function for sorting by cpu percentage */
529
530int
531compare_cpu(ppi1, ppi2)
532    struct procsinfo **ppi1;
533    struct procsinfo **ppi2;
534{
535    register struct procsinfo *pi1 = *ppi1, *pi2 = *ppi2;
536    register struct proc *p1;
537    register struct proc *p2;
538    register int result;
539    register long lresult;
540
541    p1 = &p_proc[PROCMASK(pi1->pi_pid)];
542    p2 = &p_proc[PROCMASK(pi2->pi_pid)];
543
544    ORDERKEY_PCTCPU
545    ORDERKEY_CPTICKS
546    ORDERKEY_STATE
547    ORDERKEY_PRIO
548    ORDERKEY_RSSIZE
549    ORDERKEY_MEM
550    ;
551
552    return result;
553}
554   
555
556/* compare_size - the comparison function for sorting by total memory usage */
557
558int
559compare_size(ppi1, ppi2)
560    struct procsinfo **ppi1;
561    struct procsinfo **ppi2;
562{
563    register struct procsinfo *pi1 = *ppi1, *pi2 = *ppi2;
564    register struct proc *p1;
565    register struct proc *p2;
566    register int result;
567    register long lresult;
568
569    p1 = &p_proc[PROCMASK(pi1->pi_pid)];
570    p2 = &p_proc[PROCMASK(pi2->pi_pid)];
571
572    ORDERKEY_MEM
573    ORDERKEY_RSSIZE
574    ORDERKEY_PCTCPU
575    ORDERKEY_CPTICKS
576    ORDERKEY_STATE
577    ORDERKEY_PRIO
578    ;
579
580    return result;
581}
582   
583
584/* compare_res - the comparison function for sorting by resident set size */
585
586int
587compare_res(ppi1, ppi2)
588    struct procsinfo **ppi1;
589    struct procsinfo **ppi2;
590{
591    register struct procsinfo *pi1 = *ppi1, *pi2 = *ppi2;
592    register struct proc *p1;
593    register struct proc *p2;
594    register int result;
595    register long lresult;
596
597    p1 = &p_proc[PROCMASK(pi1->pi_pid)];
598    p2 = &p_proc[PROCMASK(pi2->pi_pid)];
599
600    ORDERKEY_RSSIZE
601    ORDERKEY_MEM
602    ORDERKEY_PCTCPU
603    ORDERKEY_CPTICKS
604    ORDERKEY_STATE
605    ORDERKEY_PRIO
606    ;
607
608    return result;
609}
610   
611
612/* compare_time - the comparison function for sorting by total cpu time */
613
614int
615compare_time(ppi1, ppi2)
616    struct procsinfo **ppi1;
617    struct procsinfo **ppi2;
618{
619    register struct procsinfo *pi1 = *ppi1, *pi2 = *ppi2;
620    register struct proc *p1;
621    register struct proc *p2;
622    register int result;
623    register long lresult;
624
625    p1 = &p_proc[PROCMASK(pi1->pi_pid)];
626    p2 = &p_proc[PROCMASK(pi2->pi_pid)];
627
628    ORDERKEY_CPTICKS
629    ORDERKEY_PCTCPU
630    ORDERKEY_STATE
631    ORDERKEY_PRIO
632    ORDERKEY_MEM
633    ORDERKEY_RSSIZE
634    ;
635
636    return result;
637}
638   
639
640/* compare_prio - the comparison function for sorting by cpu percentage */
641
642int
643compare_prio(ppi1, ppi2)
644    struct procsinfo **ppi1;
645    struct procsinfo **ppi2;
646{
647    register struct procsinfo *pi1 = *ppi1, *pi2 = *ppi2;
648    register struct proc *p1;
649    register struct proc *p2;
650    register int result;
651    register long lresult;
652
653    p1 = &p_proc[PROCMASK(pi1->pi_pid)];
654    p2 = &p_proc[PROCMASK(pi2->pi_pid)];
655
656    ORDERKEY_PRIO
657    ORDERKEY_PCTCPU
658    ORDERKEY_CPTICKS
659    ORDERKEY_STATE
660    ORDERKEY_RSSIZE
661    ORDERKEY_MEM
662    ;
663
664    return result;
665}
666   
667
668proc_owner(pid)
669int pid;
670{
671   int uid;
672   register struct procsinfo **prefp = pref;
673   register int cnt = pref_len;
674
675   while (--cnt >= 0) {
676       if ((*prefp)->pi_pid == pid)
677           return (*prefp)->pi_uid;
678       prefp++;
679   }
680   
681   return(-1);
682}
683
684
Note: See TracBrowser for help on using the repository browser.