2 * Copyright (c) 1984 through 2008, William LeFebvre
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
8 * * Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
11 * * Redistributions in binary form must reproduce the above
12 * copyright notice, this list of conditions and the following disclaimer
13 * in the documentation and/or other materials provided with the
16 * * Neither the name of William LeFebvre nor the names of other
17 * contributors may be used to endorse or promote products derived from
18 * this software without specific prior written permission.
20 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
23 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
24 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
26 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
34 * top - a top users display for Unix
36 * SYNOPSIS: PowerPC running AIX 4.2 or higher
39 * This is the machine-dependent module for AIX 4.2 and higher
40 * It is currenlty only tested on PowerPC architectures.
44 * CFLAGS: -DORDER -DHAVE_GETOPT
46 * LIBS: -bD:0x18000000
48 * AUTHOR: Joep Vesseur <joep@fwi.uva.nl>
50 * PATCHES: Antoine Tabary <tabary@bruyeres.cea.fr>
59 #include <sys/sysinfo.h>
62 #include <sys/times.h>
63 #include <sys/param.h>
70 #define PROCRESS(p) (((p)->pi_trss + (p)->pi_drss)*4)
71 #define PROCSIZE(p) (((p)->pi_tsize/1024+(p)->pi_dvm)*4)
72 #define PROCTIME(pi) (pi->pi_ru.ru_utime.tv_sec + pi->pi_ru.ru_stime.tv_sec)
76 * structure definition taken from 'monitor' by Jussi Maki (jmaki@hut.fi)
79 uint n0
,n1
,n2
,n3
,n4
,n5
,n6
,n7
,n8
;
81 uint badmem
; /* this is used in RS/6000 model 220 */
84 uint numperm
; /* this seems to keep other than text and data segment
85 usage; name taken from /usr/lpp/bos/samples/vmtune.c */
86 uint totalvmem
,freevmem
;
87 uint n15
, n16
, n17
, n18
, n19
;
91 #define KMEM "/dev/kmem"
93 /* Indices in the nlist array */
100 static struct nlist nlst
[] = {
101 { "avenrun", 0, 0, 0, 0, 0 }, /* 0 */
102 { "sysinfo", 0, 0, 0, 0, 0 }, /* 1 */
103 { "vmker", 0, 0, 0, 0, 0 }, /* 2 */
104 { "proc", 0, 0, 0, 0, 0 }, /* 3 */
105 { "v", 0, 0, 0, 0, 0 }, /* 4 */
106 { NULL
, 0, 0, 0, 0, 0 }
110 /* get_process_info returns handle. definition is here */
113 struct procsinfo
**next_proc
;
118 * These definitions control the format of the per-process area
120 static char header
[] =
121 " PID X PRI NICE SIZE RES STATE TIME WCPU CPU COMMAND";
122 /* 0123456 -- field to fill in starts at header+6 */
123 #define UNAME_START 7
125 #define Proc_format \
126 "%6d %-8.8s %3d %4d %5d%c %4d%c %-5s %6s %5.2f%% %5.2f%% %.14s%s"
129 /* these are for detailing the process states */
130 int process_states
[9];
131 char *procstatenames
[] = {
132 " none, ", " sleeping, ", " state2, ", " runnable, ",
133 " idle, ", " zombie, ", " stopped, ", " running, ", " swapped, ",
138 /* these are for detailing the cpu states */
140 char *cpustatenames
[] = {
141 "idle", "user", "kernel", "wait",
145 /* these are for detailing the memory statistics */
146 long memory_stats
[4];
147 char *memorynames
[] = {
148 "K Total, ", "K Free, ", "K Buffers", NULL
155 char *swapnames
[] = {
156 "K Total, ", "K Free", NULL
162 char *state_abbrev
[] = {
163 "", "sleep", "", "", "sleep", "zomb", "stop", "run", "swap"
166 /* sorting orders. first is default */
167 char *ordernames
[] = {
168 "cpu", "size", "res", "time", "pri", NULL
171 /* compare routines */
172 int compare_cpu(), compare_size(), compare_res(), compare_time(),
175 int (*proc_compares
[])() = {
184 /* useful externals */
186 extern char *sys_errlist
[];
193 int kmem
; /* file descriptor */
195 /* offsets in kernel */
196 static unsigned long avenrun_offset
;
197 static unsigned long sysinfo_offset
;
198 static unsigned long vmker_offset
;
199 static unsigned long proc_offset
;
200 static unsigned long v_offset
;
202 /* used for calculating cpu state percentages */
203 static long cp_time
[CPU_NTIMES
];
204 static long cp_old
[CPU_NTIMES
];
205 static long cp_diff
[CPU_NTIMES
];
207 /* the runqueue length is a cumulative value. keep old value */
211 struct var v_info
; /* to determine nprocs */
212 int nprocs
; /* maximum nr of procs in proctab */
213 int ncpus
; /* nr of cpus installed */
215 int ptsize
; /* size of process table in bytes */
216 struct proc
*p_proc
; /* a copy of the process table */
217 struct procsinfo
*p_info
; /* needed for vm and ru info */
218 struct procsinfo
**pref
; /* processes selected for display */
219 int pref_len
; /* number of processes selected */
221 /* needed to calculate WCPU */
222 unsigned long curtime
;
226 * Initialize globals, get kernel offsets and stuff...
228 machine_init(struct statics
*statics
)
234 if ((kmem
= open(KMEM
, O_RDONLY
)) == -1) {
239 /* get kernel symbol offsets */
240 if (knlist(nlst
, 5, sizeof(struct nlist
)) != 0) {
244 avenrun_offset
= nlst
[X_AVENRUN
].n_value
;
245 sysinfo_offset
= nlst
[X_SYSINFO
].n_value
;
246 vmker_offset
= nlst
[X_VMKER
].n_value
;
247 proc_offset
= nlst
[X_PROC
].n_value
;
248 v_offset
= nlst
[X_V
].n_value
;
250 getkval(v_offset
, (caddr_t
)&v_info
, sizeof v_info
, "v");
252 ncpus
= v_info
.v_ncpus
; /* number of cpus */
253 nprocs
= PROCMASK(PIDMAX
);
254 if (nprocs
> 1024) nprocs
= 1024;
256 ptsize
= nprocs
* sizeof (struct proc
);
257 p_proc
= (struct proc
*)malloc(ptsize
);
258 p_info
= (struct procsinfo
*)malloc(nprocs
* sizeof (struct procsinfo
));
259 pref
= (struct procsinfo
**)malloc(nprocs
* sizeof (struct procsinfo
*));
261 if (!p_proc
|| !p_info
|| !pref
) {
262 fprintf(stderr
, "top: not enough memory\n");
268 uptime
= times(&tbuf
) / HZ
;
269 statics
->boottime
= now
- uptime
;
271 statics
->procstate_names
= procstatenames
;
272 statics
->cpustate_names
= cpustatenames
;
273 statics
->memory_names
= memorynames
;
274 statics
->order_names
= ordernames
;
275 statics
->swap_names
= swapnames
;
282 char *format_header(char *uname_field
)
287 ptr
= header
+ UNAME_START
;
288 while (*uname_field
!= '\0')
290 *ptr
++ = *uname_field
++;
299 get_system_info(struct system_info
*si
)
303 struct sysinfo s_info
;
308 /* get the load avarage array */
309 getkval(avenrun_offset
, (caddr_t
)load_avg
, sizeof load_avg
, "avenrun");
311 /* get the sysinfo structure */
312 getkval(sysinfo_offset
, (caddr_t
)&s_info
, sizeof s_info
, "sysinfo");
314 /* get vmker structure */
315 getkval(vmker_offset
, (caddr_t
)&m_info
, sizeof m_info
, "vmker");
317 /* convert load avarages to doubles */
318 for (i
= 0; i
< 3; i
++)
319 si
->load_avg
[i
] = (double)load_avg
[i
]/65536.0;
321 /* calculate cpu state in percentages */
322 for (i
= 0; i
< CPU_NTIMES
; i
++) {
323 cp_old
[i
] = cp_time
[i
];
324 cp_time
[i
] = s_info
.cpu
[i
];
325 cp_diff
[i
] = cp_time
[i
] - cp_old
[i
];
329 total
= total
/1000.0; /* top itself will correct this */
330 for (i
= 0; i
< CPU_NTIMES
; i
++) {
331 cpu_states
[i
] = cp_diff
[i
] / total
;
334 /* calculate memory statistics, scale 4K pages to megabytes */
335 #define PAGE_TO_MB(a) ((a)*4/1024)
336 memory_stats
[M_REAL
] = PAGE_TO_MB(m_info
.totalmem
);
337 memory_stats
[M_REALFREE
] = PAGE_TO_MB(m_info
.freemem
);
338 memory_stats
[M_BUFFERS
] = PAGE_TO_MB(m_info
.numperm
);
339 swap_stats
[M_VIRTUAL
] = PAGE_TO_MB(m_info
.totalvmem
);
340 swap_stats
[M_VIRTFREE
] = PAGE_TO_MB(m_info
.freevmem
);
342 /* runnable processes */
343 process_states
[0] = s_info
.runque
- old_runque
;
344 old_runque
= s_info
.runque
;
346 si
->cpustates
= cpu_states
;
347 si
->memory
= memory_stats
;
348 si
->swap
= swap_stats
;
351 static struct handle handle
;
354 get_process_info(struct system_info
*si
, struct process_select
*sel
, int compare_index
)
359 int active_procs
= 0, total_procs
= 0;
360 struct procsinfo
*pp
, **p_pref
= pref
;
361 unsigned long pctcpu
;
362 pid_t procsindex
= 0;
365 si
->procstates
= process_states
;
369 /* get the procsinfo structures of all running processes */
370 nproc
= getprocs(p_info
, sizeof (struct procsinfo
), NULL
, 0,
371 &procsindex
, nprocs
);
377 /* the swapper has no cmd-line attached */
378 strcpy(p_info
[0].pi_comm
, "swapper");
381 ptsize_util
= (PROCMASK(p_info
[nproc
-1].pi_pid
)+1) * sizeof(struct proc
);
382 getkval(proc_offset
, (caddr_t
)p_proc
, ptsize_util
, "proc");
384 memset(process_states
, 0, sizeof process_states
);
386 /* build a list of pointers to processes to show. walk through the
387 * list of procsinfo structures instead of the proc table since the
388 * mapping of procsinfo -> proctable is easy, the other way around
391 for (pp
= p_info
, i
= 0; i
< nproc
; pp
++, i
++) {
393 p
= &p_proc
[PROCMASK(pp
->pi_pid
)];
395 /* AIX marks all runnable processes as ACTIVE. We want to know
396 which processes are sleeping, so check used cpu ticks and adjust
397 status field accordingly
399 if (p
->p_stat
== SACTIVE
&& p
->p_cpticks
== 0)
402 if (pp
->pi_state
&& (sel
->system
|| ((pp
->pi_flags
& SKPROC
) == 0))) {
404 process_states
[p
->p_stat
]++;
405 if ( (pp
->pi_state
!= SZOMB
) &&
406 (sel
->idle
|| p
->p_cpticks
!= 0 || (p
->p_stat
== SACTIVE
))
407 && (sel
->uid
== -1 || pp
->pi_uid
== (uid_t
)sel
->uid
)) {
414 /* the pref array now holds pointers to the procsinfo structures in
415 * the p_info array that were selected for display
418 /* sort if requested */
420 qsort((char *)pref
, active_procs
, sizeof (struct procsinfo
*),
421 proc_compares
[compare_index
]);
423 si
->last_pid
= -1; /* no way to figure out last used pid */
424 si
->p_total
= total_procs
;
425 si
->p_active
= pref_len
= active_procs
;
427 handle
.next_proc
= pref
;
428 handle
.remaining
= active_procs
;
430 return((caddr_t
)&handle
);
433 char fmt
[MAX_COLS
]; /* static area where result is built */
435 /* define what weighted cpu is. use definition of %CPU from 'man ps(1)' */
436 #define weighted_cpu(pp) (PROCTIME(pp) == 0 ? 0.0 : \
437 (((PROCTIME(pp)*100.0)/(curtime-pi->pi_start)/ncpus)))
438 #define double_pctcpu(p) ((double)p->p_pctcpu/(double)FLT_MODULO)
441 format_next_process(caddr_t handle
, char *(*get_userid
)())
444 register struct handle
*hp
;
445 register struct procsinfo
*pi
;
446 register struct proc
*p
;
449 int proc_size
, proc_ress
;
450 char size_unit
= 'K';
451 char ress_unit
= 'K';
453 hp
= (struct handle
*)handle
;
454 if (hp
->remaining
== 0) { /* safe guard */
458 pi
= *(hp
->next_proc
++);
460 p
= &p_proc
[PROCMASK(pi
->pi_pid
)];
462 cpu_time
= PROCTIME(pi
);
464 /* we disply sizes up to 10M in KiloBytes, beyond 10M in MegaBytes */
465 if ((proc_size
= (pi
->pi_tsize
/1024+pi
->pi_dvm
)*4) > 10240) {
469 if ((proc_ress
= (pi
->pi_trss
+ pi
->pi_drss
)*4) > 10240) {
474 sprintf(fmt
, Proc_format
,
475 pi
->pi_pid
, /* PID */
476 (*get_userid
)(pi
->pi_uid
), /* login name */
477 getpriority(PRIO_PROCESS
, pi
->pi_pid
),
478 EXTRACT_NICE(p
), /* fixed or vari */
479 proc_size
, /* size */
480 size_unit
, /* K or M */
481 proc_ress
, /* resident */
482 ress_unit
, /* K or M */
483 state_abbrev
[p
->p_stat
], /* process state */
484 format_time(cpu_time
), /* time used */
485 weighted_cpu(pi
), /* WCPU */
486 100.0 * double_pctcpu(p
), /* CPU */
487 printable(pi
->pi_comm
), /* COMM */
488 (pi
->pi_flags
& SKPROC
) == 0 ? "" : " (sys)" /* kernel process? */
495 * getkval(offset, ptr, size, refstr) - get a value out of the kernel.
496 * "offset" is the byte offset into the kernel for the desired value,
497 * "ptr" points to a buffer into which the value is retrieved,
498 * "size" is the size of the buffer (and the object to retrieve),
499 * "refstr" is a reference string used when printing error meessages,
500 * if "refstr" starts with a '!', then a failure on read will not
501 * be fatal (this may seem like a silly way to do things, but I
502 * really didn't want the overhead of another argument).
507 getkval(unsigned long offset
, caddr_t ptr
, int size
, char *refstr
)
512 /* reads above 2Gb are done by seeking to offset%2Gb, and supplying
513 * 1 (opposed to 0) as fourth parameter to readx (see 'man kmem')
515 if (offset
> 1<<31) {
517 offset
&= 0x7fffffff;
520 if (lseek(kmem
, offset
, SEEK_SET
) != offset
) {
521 fprintf(stderr
, "top: lseek failed\n");
525 if (readx(kmem
, ptr
, size
, upper_2gb
) != size
) {
529 fprintf(stderr
, "top: kvm_read for %s: %s\n", refstr
,
538 /* comparison routine for qsort */
540 * The following code is taken from the solaris module and adjusted
544 #define ORDERKEY_PCTCPU \
545 if (lresult = p2->p_pctcpu - p1->p_pctcpu, \
546 (result = lresult > 0 ? 1 : lresult < 0 ? -1 : 0) == 0)
548 #define ORDERKEY_CPTICKS \
549 if ((result = PROCTIME(pi2) - PROCTIME(pi1)) == 0)
552 #define ORDERKEY_STATE \
553 if ((result = sorted_state[p2->p_stat] \
554 - sorted_state[p1->p_stat]) == 0)
556 /* Nice values directly reflect the process' priority, and are always >0 ;-) */
557 #define ORDERKEY_PRIO \
558 if ((result = EXTRACT_NICE(p1) - EXTRACT_NICE(p2)) == 0)
560 #define ORDERKEY_RSSIZE \
561 if ((result = PROCRESS(pi2) - PROCRESS(pi1)) == 0)
562 #define ORDERKEY_MEM \
563 if ((result = PROCSIZE(pi2) - PROCSIZE(pi1)) == 0)
565 static unsigned char sorted_state
[] =
578 /* compare_cpu - the comparison function for sorting by cpu percentage */
581 compare_cpu(struct procsinfo
**ppi1
, struct procsinfo
**ppi2
)
584 register struct procsinfo
*pi1
= *ppi1
, *pi2
= *ppi2
;
585 register struct proc
*p1
;
586 register struct proc
*p2
;
588 register long lresult
;
590 p1
= &p_proc
[PROCMASK(pi1
->pi_pid
)];
591 p2
= &p_proc
[PROCMASK(pi2
->pi_pid
)];
605 /* compare_size - the comparison function for sorting by total memory usage */
608 compare_size(struct procsinfo
**ppi1
, struct procsinfo
**ppi2
)
611 register struct procsinfo
*pi1
= *ppi1
, *pi2
= *ppi2
;
612 register struct proc
*p1
;
613 register struct proc
*p2
;
615 register long lresult
;
617 p1
= &p_proc
[PROCMASK(pi1
->pi_pid
)];
618 p2
= &p_proc
[PROCMASK(pi2
->pi_pid
)];
632 /* compare_res - the comparison function for sorting by resident set size */
635 compare_res(struct procsinfo
**ppi1
, struct procsinfo
**ppi2
)
638 register struct procsinfo
*pi1
= *ppi1
, *pi2
= *ppi2
;
639 register struct proc
*p1
;
640 register struct proc
*p2
;
642 register long lresult
;
644 p1
= &p_proc
[PROCMASK(pi1
->pi_pid
)];
645 p2
= &p_proc
[PROCMASK(pi2
->pi_pid
)];
659 /* compare_time - the comparison function for sorting by total cpu time */
662 compare_time(struct procsinfo
**ppi1
, struct procsinfo
**ppi2
)
665 register struct procsinfo
*pi1
= *ppi1
, *pi2
= *ppi2
;
666 register struct proc
*p1
;
667 register struct proc
*p2
;
669 register long lresult
;
671 p1
= &p_proc
[PROCMASK(pi1
->pi_pid
)];
672 p2
= &p_proc
[PROCMASK(pi2
->pi_pid
)];
686 /* compare_prio - the comparison function for sorting by cpu percentage */
689 compare_prio(struct procsinfo
**ppi1
, struct procsinfo
**ppi2
)
692 register struct procsinfo
*pi1
= *ppi1
, *pi2
= *ppi2
;
693 register struct proc
*p1
;
694 register struct proc
*p2
;
696 register long lresult
;
698 p1
= &p_proc
[PROCMASK(pi1
->pi_pid
)];
699 p2
= &p_proc
[PROCMASK(pi2
->pi_pid
)];
717 register struct procsinfo
**prefp
= pref
;
718 register int cnt
= pref_len
;
721 if ((*prefp
)->pi_pid
== pid
)
722 return (*prefp
)->pi_uid
;