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: any uniprocessor, 32 bit SGI machine running IRIX 5.3
39 * This is the machine-dependent module for IRIX 5.3.
40 * It has been tested on Indys running 5.3 and Indigos running 5.3XFS
43 * CFLAGS: -DHAVE_GETOPT
45 * AUTHOR: Sandeep Cariapa <cariapa@sgi.com>
46 * This is not a supported product of Silicon Graphics, Inc.
47 * Please do not call SGI for support.
55 #include <sys/types.h>
60 #include <sys/procfs.h>
61 #include <sys/sysinfo.h>
62 #include <sys/sysmp.h>
80 #define KMEM "/dev/kmem"
84 #define FSHIFT 8 /* bits to right of fixed binary point */
85 #define FSCALE (1<<FSHIFT)
89 typedef long load_avg
;
90 # define loaddouble(la) ((double)(la) / FIXED_LOADAVG)
91 # define intload(i) ((int)((i) * FIXED_LOADAVG))
93 typedef double load_avg
;
94 # define loaddouble(la) (la)
95 # define intload(i) ((double)(i))
98 #define percent_cpu(pp) (*(double *)pp->pr_fill)
99 #define weighted_cpu(pp) (*(double *)&pp->pr_fill[2])
102 #define pagetok(size) ((size)*pagesize)
107 * These definitions control the format of the per-process area
110 static char header
[] =
111 " PID X PRI NICE SIZE RES STATE TIME WCPU CPU COMMAND";
112 /* 0123456 -- field to fill in starts at header+6 */
113 #define UNAME_START 6
115 #define Proc_format \
116 "%5d %-8.8s %3d %4d %5s %5s %-5s %6s %5.2f%% %5.2f%% %.16s"
118 /* these are for detailing the process states */
119 char *state_abbrev
[] =
120 {"", "sleep", "run\0\0\0", "zombie", "stop", "idle", "", "swap"};
122 int process_states
[8];
123 char *procstatenames
[] = {
124 "", " sleeping, ", " running, ", " zombie, ", " stopped, ",
125 " idle, ", "", " swapped, ",
129 /* these are for detailing the cpu states */
130 int cpu_states
[CPUSTATES
];
131 char *cpustatenames
[] = {
132 "idle", "usr", "ker", "wait", "swp", "intr",
136 /* these are for detailing the memory statistics */
138 long memory_stats
[5];
139 char *memorynames
[] = {
140 "K max, ", "K avail, ", "K free, ", "K swap, ", "K free swap", NULL
143 /* useful externals */
146 extern char *sys_errlist
[];
147 extern char *format_k();
148 extern char *format_time();
149 extern long percentages();
151 /* forward references */
152 int proc_compare (void *pp1
, void *pp2
);
158 #define X_AVAILRMEM 4
161 static struct nlist nlst
[] = {
162 { "avenrun" }, /* 0. Array containing the 3 load averages. */
163 { "nproc" }, /* 1. Kernel parameter: Max number of processes. */
164 { "freemem" }, /* 2. Amount of free memory in system. */
165 { "maxmem" }, /* 3. Maximum amount of memory usable by system. */
166 { "availrmem" }, /* 4. Available real memory. */
168 { "mpid" }, /* 5. PID of last process. */
172 static unsigned long avenrun_offset
;
173 static unsigned long nproc_offset
;
174 static unsigned long freemem_offset
;
175 static unsigned long maxmem_offset
;
176 static unsigned long availrmem_offset
;
177 static unsigned long mpid_offset
;
183 static struct prpsinfo
*pbase
;
184 static struct prpsinfo
**pref
;
187 /* get_process_info passes back a handle. This is what it looks like: */
189 struct prpsinfo
**next_proc
;/* points to next valid proc pointer */
190 int remaining
; /* number of pointers remaining */
193 static struct handle handle
;
197 * Structure for keeping track of CPU times from last time around
198 * the program. We keep these things in a hash table, which is
199 * recreated at every cycle.
207 static int oldprocs
; /* size of table */
208 static struct oldproc
*oldbase
;
209 #define HASH(x) ((x << 1) % oldprocs)
210 #define PRPSINFOSIZE (sizeof(struct prpsinfo))
212 int machine_init(statics
)
213 struct statics
*statics
;
215 struct oldproc
*op
, *endbase
;
217 if ((kmem
= open(KMEM
, O_RDONLY
)) == -1) {
222 /* get the list of symbols we want to access in the kernel */
223 (void) nlist(UNIX
, nlst
);
224 if (nlst
[0].n_type
== 0) {
225 fprintf(stderr
, "%s: nlist failed\n", myname
);
229 /* Check if we got all of 'em. */
230 if (check_nlist(nlst
) > 0) {
233 avenrun_offset
= nlst
[X_AVENRUN
].n_value
;
234 nproc_offset
= nlst
[X_NPROC
].n_value
;
235 freemem_offset
= nlst
[X_FREEMEM
].n_value
;
236 maxmem_offset
= nlst
[X_MAXMEM
].n_value
;
237 availrmem_offset
= nlst
[X_AVAILRMEM
].n_value
;
239 mpid_offset
= nlst
[X_MPID
].n_value
;
242 /* Got to do this first so that we can map real estate for the
244 (void) getkval(nproc_offset
, (int *) (&nproc
), sizeof(nproc
), "nproc");
246 /* allocate space for proc structure array and array of pointers */
247 bytes
= nproc
* sizeof (struct prpsinfo
);
248 pbase
= (struct prpsinfo
*) malloc (bytes
);
249 pref
= (struct prpsinfo
**) malloc (nproc
* sizeof (struct prpsinfo
*));
250 oldbase
= (struct oldproc
*) malloc (2 * nproc
* sizeof (struct oldproc
));
252 /* Just in case ... */
253 if (pbase
== (struct prpsinfo
*) NULL
|| pref
== (struct prpsinfo
**) NULL
||
254 oldbase
== (struct oldproc
*)NULL
) {
255 (void) fprintf (stderr
, "%s: can't allocate sufficient memory\n", myname
);
259 oldprocs
= 2 * nproc
;
260 endbase
= oldbase
+ oldprocs
;
261 for (op
= oldbase
; op
< endbase
; op
++) {
265 if (!(procdir
= opendir (_PATH_PROCFSPI
))) {
266 (void) fprintf (stderr
, "Unable to open %s\n", _PATH_PROCFSPI
);
270 if (chdir (_PATH_PROCFSPI
)) {
271 /* handy for later on when we're reading it */
272 (void) fprintf (stderr
, "Unable to chdir to %s\n", _PATH_PROCFSPI
);
276 statics
->procstate_names
= procstatenames
;
277 statics
->cpustate_names
= cpustatenames
;
278 statics
->memory_names
= memorynames
;
280 pagesize
= getpagesize()/1024;
286 char *format_header(uname_field
)
287 register char *uname_field
;
292 ptr
= header
+ UNAME_START
;
293 while (*uname_field
!= '\0') {
294 *ptr
++ = *uname_field
++;
300 void get_system_info(si
)
301 struct system_info
*si
;
308 static int availrmem
;
309 struct sysinfo sysinfo
;
310 static long cp_new
[CPUSTATES
];
311 static long cp_old
[CPUSTATES
];
312 static long cp_diff
[CPUSTATES
]; /* for cpu state percentages */
313 off_t fswap
; /* current free swap in blocks */
314 off_t tswap
; /* total swap in blocks */
316 (void) getkval(avenrun_offset
, (int *)avenrun
, sizeof(avenrun
), "avenrun");
317 for (i
= 0; i
< 3; i
++) {
318 si
->load_avg
[i
] = loaddouble (avenrun
[i
]);
319 si
->load_avg
[i
] = si
->load_avg
[i
]/1024.0;
322 (void) getkval(freemem_offset
, (int *) (&freemem
), sizeof(freemem
),
324 (void) getkval(maxmem_offset
, (int *) (&maxmem
), sizeof(maxmem
), "maxmem");
325 (void) getkval(availrmem_offset
, (int *) (&availrmem
), sizeof(availrmem
),
330 (void) getkval(mpid_offset
, &(si
->last_pid
), sizeof (si
->last_pid
), "mpid");
332 swapctl(SC_GETFREESWAP
, &fswap
);
333 swapctl(SC_GETSWAPTOT
, &tswap
);
334 memory_stats
[0] = pagetok(maxmem
);
335 memory_stats
[1] = pagetok(availrmem
);
336 memory_stats
[2] = pagetok(freemem
);
337 memory_stats
[3] = tswap
/ 2;
338 memory_stats
[4] = fswap
/ 2;
340 /* use sysmp() to get current sysinfo usage. Can run into all kinds of
341 problems if you try to nlist this kernel variable. */
342 if (sysmp(MP_SAGET
, MPSA_SINFO
, &sysinfo
, sizeof(struct sysinfo
)) == -1) {
346 /* copy sysinfo.cpu to an array of longs, as expected by percentages() */
347 for (i
= 0; i
< CPUSTATES
; i
++) {
348 cp_new
[i
] = sysinfo
.cpu
[i
];
350 (void) percentages (CPUSTATES
, cpu_states
, cp_new
, cp_old
, cp_diff
);
352 si
->cpustates
= cpu_states
;
353 si
->memory
= memory_stats
;
355 numcpus
= sysmp(MP_NPROCS
);
357 /* add a slash to the "run" state abbreviation */
359 state_abbrev
[SRUN
][3] = '/';
365 caddr_t
get_process_info(si
, sel
, x
)
366 struct system_info
*si
;
367 struct process_select
*sel
;
371 register int total_procs
;
372 register int active_procs
;
373 register struct prpsinfo
**prefp
;
374 register struct prpsinfo
*pp
;
376 /* these are copied out of sel for speed */
381 /* read all the proc structures */
384 /* get a pointer to the states summary array */
385 si
->procstates
= process_states
;
387 /* set up flags which define what we are going to select */
388 show_idle
= sel
->idle
;
389 show_system
= sel
->system
;
390 show_uid
= sel
->uid
!= -1;
392 /* count up process states and get pointers to interesting procs */
395 (void) memset (process_states
, 0, sizeof (process_states
));
398 for (pp
= pbase
, i
= 0; i
< nproc
; pp
++, i
++) {
400 * Place pointers to each valid proc structure in pref[].
401 * Process slots that are actually in use have a non-zero
402 * status field. Processes with SSYS set are system
403 * processes---these get ignored unless show_system is set.
405 if (pp
->pr_state
!= 0 &&
406 (show_system
|| ((pp
->pr_flag
& SSYS
) == 0))) {
408 process_states
[pp
->pr_state
]++;
409 if ((!pp
->pr_zomb
) &&
410 (show_idle
|| (pp
->pr_state
== SRUN
)) &&
411 (!show_uid
|| pp
->pr_uid
== (uid_t
) sel
->uid
)) {
418 /* if requested, sort the "interesting" processes */
420 qsort ((char *) pref
, active_procs
, sizeof (struct prpsinfo
*), proc_compare
);
422 /* remember active and total counts */
423 si
->p_total
= total_procs
;
424 si
->p_active
= active_procs
;
426 /* pass back a handle */
427 handle
.next_proc
= pref
;
428 handle
.remaining
= active_procs
;
429 return((caddr_t
)&handle
);
432 char *format_next_process(handle
, get_userid
)
434 char *(*get_userid
)();
437 register struct prpsinfo
*pp
;
439 register long cputime
;
440 register double pctcpu
;
442 /* find and remember the next proc structure */
443 hp
= (struct handle
*) handle
;
444 pp
= *(hp
->next_proc
++);
447 /* get the cpu usage and calculate the cpu percentages */
448 cputime
= pp
->pr_time
.tv_sec
;
449 pctcpu
= percent_cpu (pp
);
452 if (pp
->pr_sonproc
< 0)
453 state_abbrev
[SRUN
][4] = '*';
455 state_abbrev
[SRUN
][4] = pp
->pr_sonproc
+ '0';
458 /* format this entry */
462 (*get_userid
) (pp
->pr_uid
),
465 format_k(pagetok(pp
->pr_size
)),
466 format_k(pagetok(pp
->pr_rssize
)),
467 state_abbrev
[pp
->pr_state
],
468 format_time(cputime
),
473 /* return the result */
478 * getkval(offset, ptr, size, refstr) - get a value out of the kernel.
479 * "offset" is the byte offset into the kernel for the desired value,
480 * "ptr" points to a buffer into which the value is retrieved,
481 * "size" is the size of the buffer (and the object to retrieve),
482 * "refstr" is a reference string used when printing error meessages,
483 * if "refstr" starts with a '!', then a failure on read will not
484 * be fatal (this may seem like a silly way to do things, but I
485 * really didn't want the overhead of another argument).
489 int getkval(offset
, ptr
, size
, refstr
)
496 if (lseek(kmem
, offset
, SEEK_SET
) == -1) {
499 (void) fprintf(stderr
, "%s: lseek to %s: %s\n", KMEM
,
500 refstr
, strerror(errno
));
503 if (read(kmem
, (char *) ptr
, size
) == -1) {
507 (void) fprintf(stderr
, "%s: reading %s: %s\n", KMEM
,
508 refstr
, strerror(errno
));
516 * proc_compare - comparison function for "qsort"
517 * Compares the resource consumption of two processes using five
518 * distinct keys. The keys (in descending order of importance) are:
519 * percent cpu, cpu ticks, state, resident set size, total virtual
520 * memory usage. The process states are ordered as follows (from least
521 * to most important): WAIT, zombie, sleep, stop, idle, run. The
522 * array declaration below maps a process state index into a number
523 * that reflects this ordering.
527 unsigned char sorted_state
[] =
536 1 /* being swapped (WAIT) */
539 int proc_compare (pp1
, pp2
)
543 register struct prpsinfo
*p1
;
544 register struct prpsinfo
*p2
;
545 register long result
;
547 /* remove one level of indirection */
548 p1
= *(struct prpsinfo
**)pp1
;
549 p2
= *(struct prpsinfo
**)pp2
;
551 /* compare percent cpu (pctcpu) */
552 if ((result
= (long) (p2
->pr_cpu
- p1
->pr_cpu
)) == 0) {
553 /* use cpticks to break the tie */
554 if ((result
= p2
->pr_time
.tv_sec
- p1
->pr_time
.tv_sec
) == 0) {
555 /* use process state to break the tie */
556 if ((result
= (long) (sorted_state
[p2
->pr_state
] -
557 sorted_state
[p1
->pr_state
])) == 0) {
558 /* use priority to break the tie */
559 if ((result
= p2
->pr_oldpri
- p1
->pr_oldpri
) == 0) {
560 /* use resident set size (rssize) to break the tie */
561 if ((result
= p2
->pr_rssize
- p1
->pr_rssize
) == 0) {
562 /* use total memory to break the tie */
563 result
= (p2
->pr_size
- p1
->pr_size
);
572 /* return the owner of the specified process. */
576 register struct prpsinfo
*p
;
579 for (i
= 0, p
= pbase
; i
< nproc
; i
++, p
++)
580 if (p
->pr_pid
== (oid_t
)pid
)
581 return ((int)p
->pr_uid
);
587 * check_nlist(nlst) - checks the nlist to see if any symbols were not
588 * found. For every symbol that was not found, a one-line
589 * message is printed to stderr. The routine returns the
590 * number of symbols NOT found.
593 int check_nlist(nlst
)
594 register struct nlist
*nlst
;
599 /* check to see if we got ALL the symbols we requested */
600 /* this will write one line to stderr for every symbol not found */
603 while (nlst
->n_name
!= NULL
) {
604 if (nlst
->n_type
== 0) {
605 /* this one wasn't found */
606 fprintf(stderr
, "kernel: no symbol named `%s'\n", nlst
->n_name
);
615 /* get process table */
616 void getptable (baseptr
)
617 struct prpsinfo
*baseptr
;
619 struct prpsinfo
*currproc
; /* pointer to current proc structure */
622 struct dirent
*directp
;
624 static struct timeval lasttime
=
626 struct timeval thistime
;
627 struct timezone thiszone
;
630 struct oldproc
*endbase
;
632 gettimeofday (&thistime
, &thiszone
);
635 * To avoid divides, we keep times in nanoseconds. This is
636 * scaled by 1e7 rather than 1e9 so that when we divide we
640 timediff
= ((double) thistime
.tv_sec
* 1.0e7
+
641 ((double) thistime
.tv_usec
* 10.0)) -
642 ((double) lasttime
.tv_sec
* 1.0e7
+
643 ((double) lasttime
.tv_usec
* 10.0));
648 * constants for exponential average. avg = alpha * new + beta * avg
649 * The goal is 50% decay in 30 sec. However if the sample period
650 * is greater than 30 sec, there's not a lot we can do.
652 if (timediff
< 30.0e7
)
654 alpha
= 0.5 * (timediff
/ 30.0e7
);
663 endbase
= oldbase
+ oldprocs
;
667 for (rewinddir (procdir
); directp
= readdir (procdir
);)
671 if ((fd
= open (directp
->d_name
, O_RDONLY
)) < 0)
674 currproc
= &baseptr
[numprocs
];
675 if (ioctl (fd
, PIOCPSINFO
, currproc
) < 0)
682 * SVr4 doesn't keep track of CPU% in the kernel, so we have
683 * to do our own. See if we've heard of this process before.
684 * If so, compute % based on CPU since last time.
686 op
= oldbase
+ HASH (currproc
->pr_pid
);
689 if (op
->oldpid
== -1) /* not there */
691 if (op
->oldpid
== currproc
->pr_pid
)
692 { /* found old data */
693 percent_cpu (currproc
) =
694 ((currproc
->pr_time
.tv_sec
* 1.0e9
+
695 currproc
->pr_time
.tv_nsec
)
696 - op
->oldtime
) / timediff
;
697 weighted_cpu (currproc
) =
698 op
->oldpct
* beta
+ percent_cpu (currproc
) * alpha
;
702 op
++; /* try next entry in hash table */
703 if (op
== endbase
) /* table wrapped around */
707 /* Otherwise, it's new, so use all of its CPU time */
708 if (op
->oldpid
== -1)
712 percent_cpu (currproc
) =
713 (currproc
->pr_time
.tv_sec
* 1.0e9
+
714 currproc
->pr_time
.tv_nsec
) / timediff
;
715 weighted_cpu (currproc
) =
716 percent_cpu (currproc
);
719 { /* first screen -- no difference is possible */
720 percent_cpu (currproc
) = 0.0;
721 weighted_cpu (currproc
) = 0.0;
729 if (nproc
!= numprocs
)
733 * Save current CPU time for next time around
734 * For the moment recreate the hash table each time, as the code
735 * is easier that way.
737 oldprocs
= 2 * nproc
;
738 endbase
= oldbase
+ oldprocs
;
739 for (op
= oldbase
; op
< endbase
; op
++)
741 for (i
= 0, currproc
= baseptr
;
743 i
++, currproc
= (struct prpsinfo
*) ((char *) currproc
+ PRPSINFOSIZE
))
745 /* find an empty spot */
746 op
= oldbase
+ HASH (currproc
->pr_pid
);
749 if (op
->oldpid
== -1)
755 op
->oldpid
= currproc
->pr_pid
;
756 op
->oldtime
= (currproc
->pr_time
.tv_sec
* 1.0e9
+
757 currproc
->pr_time
.tv_nsec
);
758 op
->oldpct
= weighted_cpu (currproc
);