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

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