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 SGI machine running IRIX 6.2 and up
39 * This is the machine-dependent module for IRIX as supplied by
42 * CFLAGS: -DHAVE_GETOPT -D_OLD_TERMIOS -DORDER
44 * AUTHOR: Sandeep Cariapa <cariapa@sgi.com>
45 * AUTHOR: Larry McVoy <lm@sgi.com>
46 * Sandeep did all the hard work; I ported to 6.2 and fixed up some formats.
47 * AUTHOR: John Schimmel <jes@sgi.com>
48 * He did the all irix merge.
49 * AUTHOR: Ariel Faigon <ariel@sgi.com>
50 * Ported to Ficus/Kudzu (IRIX 6.4+).
51 * Got rid of all nlist and different (elf64, elf32, COFF) kernel
53 * Various small fixes and enhancements: multiple CPUs, nicer formats.
54 * Added -DORDER process display ordering
55 * cleaned most -fullwarn'ings.
56 * Need -D_OLD_TERMIOS when compiling on IRIX 6.4 to work on 6.2 systems
57 * Support much bigger values in memory sizes (over Peta-byte)
58 * AUTHOR: William LeFebvre
59 * Converted to ANSI C and updated to new module interface
66 #include <sys/types.h>
71 #include <sys/procfs.h>
72 #include <sys/sysinfo.h>
73 #include <sys/sysmp.h>
74 #include <sys/utsname.h>
75 #include <sys/schedctl.h> /* for < 6.4 NDPHIMAX et al. */
91 #define KMEM "/dev/kmem"
93 typedef double load_avg
;
94 #define loaddouble(la) (la)
95 #define intload(i) ((double)(i))
98 * Structure for keeping track of CPU times from last time around
99 * the program. We keep these things in a hash table, which is
100 * recreated at every cycle.
107 static int oldprocs
; /* size of table */
108 static struct oldproc
*oldbase
;
109 #define HASH(x) ((x << 1) % oldprocs)
112 #define pagetok(pages) ((((uint64_t) pages) * pagesize) >> 10)
115 * Ugly hack, save space and complexity of allocating and maintaining
116 * parallel arrays to the prpsinfo array: use spare space (pr_fill area)
117 * in prpsinfo structures to store %CPU calculated values
119 #define D_align(addr) (((unsigned long)(addr) & ~0x0fU))
120 #define percent_cpu(pp) (* (double *) D_align(&((pp)->pr_fill[0])))
121 #define weighted_cpu(pp) (* (double *) D_align(&((pp)->pr_fill[4])))
124 /* Username field to fill in starts at: */
125 #define UNAME_START 16
128 * These definitions control the format of the per-process area
130 static char header
[] =
131 " PID PGRP X PRI SIZE RES STATE TIME %WCPU %CPU COMMAND";
133 012345678901234567890123456789012345678901234567890123456789012345678901234567
137 /* PID PGRP USER PRI SIZE RES STATE TIME %WCPU %CPU CMD */
138 #define Proc_format \
139 "%7d %7d %-8.8s %4.4s %6.6s %5.5s %-6.6s %6.6s %5.2f %5.2f %-.10s"
143 * these are for detailing the cpu states
144 * Data is taken from the sysinfo structure (see <sys/sysinfo.h>)
145 * We rely on the following values:
149 * #define CPU_KERNEL 2
151 * #define CPU_SXBRK 4
154 #ifndef CPU_STATES /* defined only in 6.4 and up */
155 # define CPU_STATES 6
158 int cpu_states
[CPU_STATES
];
159 char *cpustatenames
[] = {
160 "idle", "usr", "ker", "wait", "xbrk", "intr",
164 /* these are for detailing the memory statistics */
167 int memory_stats
[MEMSTATS
];
168 char *memorynames
[] = {
169 "K max, ", "K avail, ", "K free, ", "K swap, ", "K free swap", NULL
174 static char fmt
[MAX_COLS
+ 2];
177 /* useful externals */
179 extern char *sys_errlist
[];
182 extern char *format_k();
183 extern char *format_time();
184 extern long percentages();
187 static unsigned long avenrun_offset
;
189 static float irix_ver
; /* for easy numeric comparison */
191 static struct prpsinfo
*pbase
;
192 static struct prpsinfo
**pref
;
193 static struct oldproc
*oldbase
;
194 static int oldprocs
; /* size of table */
198 static int ptable_size
; /* allocated process table size */
199 static int nproc
; /* estimated process table size */
202 /* get_process_info passes back a handle. This is what it looks like: */
204 struct prpsinfo
**next_proc
; /* points to next valid proc pointer */
205 int remaining
; /* number of pointers remaining */
208 static struct handle handle
;
210 void getptable(struct prpsinfo
*baseptr
);
211 void size(int fd
, struct prpsinfo
*ps
);
213 extern char *ordernames
[];
216 * Process states letters are mapped into numbers
217 * 6.5 seems to have changed the semantics of prpsinfo.pr_state
218 * so we rely, (like ps does) on the char value pr_sname.
219 * The order we use here is what may be most interesting
220 * to top users: Most interesting state on top, least on bottom.
221 * 'S' (sleeping) is the most common case so I put it _after_
222 * zombie, even though it is more "active" than zombie.
224 * State letters and their meanings:
226 * R Process is running (may not have a processor yet)
227 * I Process is in intermediate state of creation
228 * X Process is waiting for memory
229 * T Process is stopped
230 * Z Process is terminated and parent not waiting (zombie)
231 * S Process is sleeping, waiting for a resource
234 /* abbreviated process states */
235 static char *state_abbrev
[] =
236 { "", "sleep", "zomb", "stop", "swap", "start", "ready", "run", NULL
};
238 /* Same but a little "wordier", used in CPU activity summary */
239 int process_states
[8]; /* per state counters */
240 char *procstatenames
[] = {
241 /* ready to run is considered running here */
242 "", " sleeping, ", " zombie, ", " stopped, ",
243 " swapped, ", " starting, ", " ready, ", " running, ",
255 #define IS_ACTIVE(pp) \
256 (first_screen ? proc_state(pp) >= S_STARTING : percent_cpu(pp) > 0.0)
260 * map the pr_sname value to an integer.
261 * used as an index into state_abbrev[]
262 * as well as an "order" key
264 static int proc_state(struct prpsinfo
*pp
)
266 char psname
= pp
->pr_sname
;
270 (pp
->pr_sonproc
>= 0 && pp
->pr_sonproc
< numcpus
) ?
271 S_RUNNING
/* on a processor */ : S_READY
;
272 case 'I': return S_STARTING
;
273 case 'X': return S_SWAPPED
;
274 case 'T': return S_STOPPED
;
275 case 'Z': return S_ZOMBIE
;
276 case 'S': return S_SLEEPING
;
283 * To avoid nlist'ing the kernel (with all the different kernel type
284 * complexities), we estimate the size of the needed working process
285 * table by scanning /proc/pinfo and taking the number of entries
286 * multiplied by some reasonable factor.
287 * Assume current dir is _PATH_PROCFSPI
289 static int active_proc_count()
294 if ((dirp
= opendir(".")) == NULL
) {
295 (void) fprintf(stderr
, "%s: Unable to open %s\n",
296 myname
, _PATH_PROCFSPI
);
299 for (pcnt
= 0; readdir(dirp
) != NULL
; pcnt
++)
307 * allocate space for:
308 * proc structure array
309 * array of pointers to the above (used for sorting)
310 * array for storing per-process old CPU usage
313 allocate_proc_tables()
315 int n_active
= active_proc_count();
317 if (pbase
!= NULL
) /* && n_active < ptable_size */
320 /* Need to realloc if we exceed, but factor should be enough */
321 nproc
= n_active
* 5;
322 oldprocs
= 2 * nproc
;
324 pbase
= (struct prpsinfo
*)
325 malloc(nproc
* sizeof(struct prpsinfo
));
326 pref
= (struct prpsinfo
**)
327 malloc(nproc
* sizeof(struct prpsinfo
*));
328 oldbase
= (struct oldproc
*)
329 malloc (oldprocs
* sizeof(struct oldproc
));
333 if (pbase
== NULL
|| pref
== NULL
|| oldbase
== NULL
) {
334 (void) fprintf(stderr
, "%s: malloc: out of memory\n", myname
);
340 machine_init(struct statics
*statics
)
342 struct oldproc
*op
, *endbase
;
344 struct utsname utsname
;
348 irix_ver
= (float) atof((const char *)utsname
.release
);
349 strncpy(tmpbuf
, utsname
.release
, 9);
351 sprintf(uname_str
, "%s %-.14s %s %s",
352 utsname
.sysname
, utsname
.nodename
,
353 tmpbuf
, utsname
.machine
);
355 pagesize
= getpagesize();
357 if ((kmem
= open(KMEM
, O_RDONLY
)) == -1) {
362 if (chdir(_PATH_PROCFSPI
)) {
363 /* handy for later on when we're reading it */
364 (void) fprintf(stderr
, "%s: Unable to chdir to %s\n",
365 myname
, _PATH_PROCFSPI
);
368 if ((procdir
= opendir(".")) == NULL
) {
369 (void) fprintf(stderr
, "%s: Unable to open %s\n",
370 myname
, _PATH_PROCFSPI
);
374 if ((avenrun_offset
= sysmp(MP_KERNADDR
, MPKA_AVENRUN
)) == -1) {
375 perror("sysmp(MP_KERNADDR, MPKA_AVENRUN)");
379 allocate_proc_tables();
381 oldprocs
= 2 * nproc
;
382 endbase
= oldbase
+ oldprocs
;
383 for (op
= oldbase
; op
< endbase
; op
++) {
387 statics
->cpustate_names
= cpustatenames
;
388 statics
->memory_names
= memorynames
;
389 statics
->order_names
= ordernames
;
390 statics
->procstate_names
= procstatenames
;
396 format_header(register char *uname_field
)
401 ptr
= header
+ UNAME_START
;
402 while (*uname_field
!= '\0') {
403 *ptr
++ = *uname_field
++;
410 get_system_info(struct system_info
*si
)
415 struct rminfo realmem
;
416 struct sysinfo sysinfo
;
417 static time_t cp_old
[CPU_STATES
];
418 static time_t cp_diff
[CPU_STATES
]; /* for cpu state percentages */
419 off_t fswap
; /* current free swap in blocks */
420 off_t tswap
; /* total swap in blocks */
422 (void) getkval(avenrun_offset
, (int *) avenrun
, sizeof(avenrun
), "avenrun");
424 for (i
= 0; i
< 3; i
++) {
425 si
->load_avg
[i
] = loaddouble(avenrun
[i
]);
426 si
->load_avg
[i
] /= 1024.0;
429 if ((numcpus
= sysmp(MP_NPROCS
)) == -1) {
430 perror("sysmp(MP_NPROCS)");
434 if (sysmp(MP_SAGET
, MPSA_RMINFO
, &realmem
, sizeof(realmem
)) == -1) {
435 perror("sysmp(MP_SAGET,MPSA_RMINFO, ...)");
439 swapctl(SC_GETFREESWAP
, &fswap
);
440 swapctl(SC_GETSWAPTOT
, &tswap
);
442 memory_stats
[0] = pagetok(realmem
.physmem
);
443 memory_stats
[1] = pagetok(realmem
.availrmem
);
444 memory_stats
[2] = pagetok(realmem
.freemem
);
445 memory_stats
[3] = tswap
/ 2;
446 memory_stats
[4] = fswap
/ 2;
448 if (sysmp(MP_SAGET
,MPSA_SINFO
, &sysinfo
,sizeof(struct sysinfo
)) == -1) {
449 perror("sysmp(MP_SAGET,MPSA_SINFO)");
452 (void) percentages(CPU_STATES
, cpu_states
, sysinfo
.cpu
, cp_old
, cp_diff
);
454 si
->cpustates
= cpu_states
;
455 si
->memory
= memory_stats
;
462 get_process_info(struct system_info
*si
, struct process_select
*sel
, int compare_index
)
465 int i
, total_procs
, active_procs
;
466 struct prpsinfo
**prefp
;
469 static char first_screen
= 1;
471 /* read all the proc structures */
474 /* get a pointer to the states summary array */
475 si
->procstates
= process_states
;
477 /* set up flags which define what we are going to select */
478 show_uid
= sel
->uid
!= -1;
480 /* count up process states and get pointers to interesting procs */
483 (void) memset(process_states
, 0, sizeof(process_states
));
486 for (pp
= pbase
, i
= 0; i
< nproc
; pp
++, i
++) {
488 * Place pointers to each valid proc structure in pref[].
489 * Process slots that are actually in use have a non-zero
490 * status field. Processes with SSYS set are system
491 * processes---these get ignored unless show_system is set.
492 * Ariel: IRIX 6.4 had to redefine "system processes"
493 * They do not exist outside the kernel in new kernels.
494 * Now defining as uid==0 and ppid==1 (init children)
497 (sel
->system
|| !(pp
->pr_uid
==0 && pp
->pr_ppid
==1))) {
499 process_states
[proc_state(pp
)]++;
501 * zombies are actually interesting (to avoid)
502 * although they are not active, so I leave them
505 if (/* (! pp->pr_zomb) && */
506 (sel
->idle
|| IS_ACTIVE(pp
)) &&
507 (! show_uid
|| pp
->pr_uid
== (uid_t
) sel
->uid
)) {
515 /* if requested, sort the "interesting" processes */
516 qsort((char *) pref
, active_procs
, sizeof(struct prpsinfo
*),
517 proc_compares
[compare_index
]);
519 /* remember active and total counts */
520 si
->p_total
= total_procs
;
521 si
->p_active
= active_procs
;
523 /* pass back a handle */
524 handle
.next_proc
= pref
;
525 handle
.remaining
= active_procs
;
526 return ((caddr_t
) &handle
);
530 * Added cpu_id to running processes, add 'ready' (to run) state
533 format_state(struct prpsinfo
*pp
)
536 static char state_str
[16];
537 int state
= proc_state(pp
);
539 if (state
== S_RUNNING
) {
541 * Alert: 6.2 (MP only?) binary incompatibility
542 * pp->pr_sonproc apparently (?) has a different
543 * offset on 6.2 machines... I've seen cases where
544 * a 6.4 compiled top running on 6.2 printed
545 * a garbage CPU-id. To be safe, I print the CPU-id
546 * only if it falls within range [0..numcpus-1]
548 sprintf(state_str
, "run/%d", pp
->pr_sonproc
);
553 return state_abbrev
[state
];
557 format_prio(struct prpsinfo
*pp
)
560 static char prio_str
[10];
562 if (irix_ver
< 6.4) {
564 * Note: this is _compiled_ on 6.x where x >= 4 but I would like
565 * it to run on 6.2 6.3 as well (backward binary compatibility).
566 * Scheduling is completely different between these IRIX versions
567 * and some scheduling classes may even have different names.
569 * The solution: have more than one style of 'priority' depending
572 * See npri(1) + nice(2) + realtime(5) for scheduling classes,
573 * and priority values.
575 if (pp
->pr_pri
<= NDPHIMIN
) /* real time? */
576 sprintf(prio_str
, "+%d", pp
->pr_pri
);
577 else if (pp
->pr_pri
<= NDPNORMMIN
) /* normal interactive */
578 sprintf(prio_str
, "%d", pp
->pr_pri
);
579 else /* batch: low prio */
580 sprintf(prio_str
, "b%d", pp
->pr_pri
);
584 /* copied from Kostadis's code */
586 if (strcmp(pp
->pr_clname
, "RT") == 0) /* real time */
587 sprintf(prio_str
, "+%d", pp
->pr_pri
);
588 else if (strcmp(pp
->pr_clname
, "DL") == 0) /* unsupported ? */
589 sprintf(prio_str
, "d%d", pp
->pr_pri
);
590 else if (strcmp(pp
->pr_clname
, "GN") == 0)
591 sprintf(prio_str
, "g%d", pp
->pr_pri
);
592 else if (strcmp(pp
->pr_clname
, "GB") == 0)
593 sprintf(prio_str
, "p%d", pp
->pr_pri
);
595 else if (strcmp(pp
->pr_clname
, "WL") == 0) /* weightless */
597 else if (strcmp(pp
->pr_clname
, "BC") == 0)
598 return "bc"; /* batch critical */
599 else if (strcmp(pp
->pr_clname
, "B") == 0)
600 return "b"; /* batch */
602 sprintf(prio_str
, "%d", pp
->pr_pri
);
608 clip_percent(double pct
)
613 } else if (pct
>= 100) {
620 format_next_process(caddr_t handle
, char *(*get_userid
)())
627 /* find and remember the next proc structure */
628 hp
= (struct handle
*) handle
;
629 pp
= *(hp
->next_proc
++);
632 /* get the process cpu usage since startup */
633 cputime
= pp
->pr_time
.tv_sec
;
635 /* format this entry */
640 (*get_userid
) (pp
->pr_uid
),
642 format_k(pagetok(pp
->pr_size
)),
643 format_k(pagetok(pp
->pr_rssize
)),
645 format_time(cputime
),
646 clip_percent(weighted_cpu(pp
)),
647 clip_percent(percent_cpu(pp
)),
648 printable(pp
->pr_fname
));
650 /* return the result */
655 * getkval(offset, ptr, size, refstr) - get a value out of the kernel.
656 * "offset" is the byte offset into the kernel for the desired value,
657 * "ptr" points to a buffer into which the value is retrieved,
658 * "size" is the size of the buffer (and the object to retrieve),
659 * "refstr" is a reference string used when printing error meessages,
660 * if "refstr" starts with a '!', then a failure on read will not
661 * be fatal (this may seem like a silly way to do things, but I
662 * really didn't want the overhead of another argument).
667 getkval(unsigned long offset
, int *ptr
, int size
, char *refstr
)
670 if (lseek(kmem
, (long) offset
, SEEK_SET
) == -1) {
673 (void) fprintf(stderr
, "%s: %s: lseek to %s: %s\n",
674 myname
, KMEM
, refstr
, strerror(errno
));
677 if (read(kmem
, (char *) ptr
, size
) == -1) {
681 (void) fprintf(stderr
, "%s: %s: reading %s: %s\n",
682 myname
, KMEM
, refstr
, strerror(errno
));
690 * compare_K - comparison functions for "qsort"
691 * Compares the resource consumption of two processes using five
692 * distinct keys. The keys are:
693 * percent cpu, cpu ticks, state, resident set size, total virtual
694 * memory usage. The process states are ordered as follows (from least
695 * to most important): WAIT, zombie, sleep, stop, idle, run.
696 * Different comparison functions are used for different orderings.
699 /* these are names given to allowed sorting orders -- first is default */
700 char *ordernames
[] = {
702 * Aliases for user convenience/friendliness:
706 "cpu", "size", "mem", "res", "rss",
707 "time", "state", "command", "prio", NULL
710 /* forward definitions for comparison functions */
711 int compare_cpu(struct prpsinfo
**pp1
, struct prpsinfo
**pp2
);
712 int compare_size(struct prpsinfo
**pp1
, struct prpsinfo
**pp2
);
713 int compare_res(struct prpsinfo
**pp1
, struct prpsinfo
**pp2
);
714 int compare_time(struct prpsinfo
**pp1
, struct prpsinfo
**pp2
);
715 int compare_state(struct prpsinfo
**pp1
, struct prpsinfo
**pp2
);
716 int compare_cmd(struct prpsinfo
**pp1
, struct prpsinfo
**pp2
);
717 int compare_prio(struct prpsinfo
**pp1
, struct prpsinfo
**pp2
);
719 int (*proc_compares
[])() = {
734 * The possible comparison expressions. These are defined in such a way
735 * that they can be merely listed in the source code to define the actual
739 #define ORDERKEY_PCTCPU \
740 if (dresult = percent_cpu(p2) - percent_cpu(p1),\
741 (result = dresult > 0.0 ? 1 : dresult < 0.0 ? -1 : 0) == 0)
742 #define ORDERKEY_CPTICKS \
743 if ((result = p2->pr_time.tv_sec - p1->pr_time.tv_sec) == 0)
744 #define ORDERKEY_STATE if ((result = proc_state(p2) - proc_state(p1)) == 0)
745 #define ORDERKEY_PRIO if ((result = p2->pr_oldpri - p1->pr_oldpri) == 0)
746 #define ORDERKEY_RSSIZE if ((result = p2->pr_rssize - p1->pr_rssize) == 0)
747 #define ORDERKEY_MEM if ((result = (p2->pr_size - p1->pr_size)) == 0)
748 #define ORDERKEY_CMD if ((result = strcmp(p1->pr_fname,p2->pr_fname)) == 0)
750 int compare_cpu(struct prpsinfo
**pp1
, struct prpsinfo
**pp2
)
752 struct prpsinfo
*p1
, *p2
;
756 /* remove one level of indirection */
760 * order by various keys, resorting to the next one
761 * whenever there's a tie in comparisons
773 int compare_size(struct prpsinfo
**pp1
, struct prpsinfo
**pp2
)
775 struct prpsinfo
*p1
, *p2
;
779 /* remove one level of indirection */
783 * order by various keys, resorting to the next one
784 * whenever there's a tie in comparisons
796 int compare_res(struct prpsinfo
**pp1
, struct prpsinfo
**pp2
)
798 struct prpsinfo
*p1
, *p2
;
802 /* remove one level of indirection */
806 * order by various keys, resorting to the next one
807 * whenever there's a tie in comparisons
819 int compare_time(struct prpsinfo
**pp1
, struct prpsinfo
**pp2
)
821 struct prpsinfo
*p1
, *p2
;
825 /* remove one level of indirection */
829 * order by various keys, resorting to the next one
830 * whenever there's a tie in comparisons
842 int compare_cmd(struct prpsinfo
**pp1
, struct prpsinfo
**pp2
)
844 struct prpsinfo
*p1
, *p2
;
848 /* remove one level of indirection */
852 * order by various keys, resorting to the next one
853 * whenever there's a tie in comparisons
863 int compare_state(struct prpsinfo
**pp1
, struct prpsinfo
**pp2
)
865 struct prpsinfo
*p1
, *p2
;
869 /* remove one level of indirection */
873 * order by various keys, resorting to the next one
874 * whenever there's a tie in comparisons
884 int compare_prio(struct prpsinfo
**pp1
, struct prpsinfo
**pp2
)
886 struct prpsinfo
*p1
, *p2
;
890 /* remove one level of indirection */
894 * order by various keys, resorting to the next one
895 * whenever there's a tie in comparisons
905 /* return the owner of the specified process. */
907 proc_owner(pid_t pid
)
910 register struct prpsinfo
*p
;
913 for (i
= 0, p
= pbase
; i
< nproc
; i
++, p
++)
914 if (p
->pr_pid
== pid
)
922 size(int fd
, struct prpsinfo
*ps
)
925 prmap_sgi_arg_t maparg
;
926 struct prmap_sgi maps
[256];
931 maparg
.pr_vaddr
= (caddr_t
) maps
;
932 maparg
.pr_size
= sizeof maps
;
933 if ((nmaps
= ioctl(fd
, PIOCMAP_SGI
, &maparg
)) == -1) {
934 /* XXX - this will be confusing */
937 for (i
= 0, sz
= 0; i
< nmaps
; ++i
) {
938 sz
+= (double) maps
[i
].pr_wsize
/ MA_WSIZE_FRAC
;
940 ps
->pr_rssize
= (long) sz
;
944 /* get process table */
946 getptable(struct prpsinfo
*baseptr
)
949 struct prpsinfo
*currproc
; /* ptr to current proc struct */
951 struct dirent
*direntp
;
952 struct oldproc
*op
, *endbase
;
953 static struct timeval lasttime
, thistime
;
954 static double timediff
, alpha
, beta
;
956 /* measure time between last call to getptable and current call */
957 gettimeofday (&thistime
, NULL
);
960 * To avoid divides, we keep times in nanoseconds. This is
961 * scaled by 1e7 rather than 1e9 so that when we divide we
964 timediff
= ((double) thistime
.tv_sec
* 1.0e7
-
965 (double) lasttime
.tv_sec
* 1.0e7
)
967 ((double) thistime
.tv_usec
* 10 -
968 (double) lasttime
.tv_usec
* 10);
971 * Under extreme load conditions, sca has experienced
972 * an assert(timediff > 0) failure here. His guess is that
973 * sometimes timed resets the time backwards and gettimeofday
974 * returns a lower number on a later call.
975 * To be on the safe side I fix it here by setting timediff
976 * to some arbitrary small value (in nanoseconds).
978 if (timediff
<= 0.0) timediff
= 100.0;
980 lasttime
= thistime
; /* prepare for next round */
983 * constants for exponential decaying average.
984 * avg = alpha * new + beta * avg
985 * The goal is 50% decay in 30 sec. However if the sample period
986 * is greater than 30 sec, there's not a lot we can do.
988 if (timediff
< 30.0e7
) {
989 alpha
= 0.5 * (timediff
/ 15.0e7
);
995 assert(alpha
>= 0); assert(alpha
<= 1);
996 assert(beta
>= 0); assert(beta
<= 1);
998 endbase
= oldbase
+ oldprocs
;
1001 for (numprocs
= 0, rewinddir(procdir
); direntp
= readdir(procdir
);) {
1004 if ((fd
= open(direntp
->d_name
, O_RDONLY
)) < 0)
1007 currproc
= baseptr
+ numprocs
;
1009 if (ioctl(fd
, PIOCPSINFO
, currproc
) < 0) {
1015 * SVR4 doesn't keep track of CPU% in the kernel,
1016 * so we have to do our own.
1017 * See if we've heard of this process before.
1018 * If so, compute % based on CPU since last time.
1020 op
= oldbase
+ HASH (currproc
->pr_pid
);
1022 if (op
->oldpid
== -1) /* not there */
1024 if (op
->oldpid
== currproc
->pr_pid
) {
1025 /* found old data */
1026 percent_cpu(currproc
) =
1027 ((currproc
->pr_time
.tv_sec
* 1.0e9
+
1028 currproc
->pr_time
.tv_nsec
)
1029 - op
->oldtime
) / timediff
;
1031 weighted_cpu(currproc
) =
1033 percent_cpu(currproc
) * alpha
;
1037 op
++; /* try next entry in hash table */
1038 if (op
== endbase
) /* table wrap around */
1042 /* Otherwise, it's new, so use all of its CPU time */
1043 if (op
->oldpid
== -1) {
1044 if (lasttime
.tv_sec
) {
1045 percent_cpu(currproc
) =
1046 (currproc
->pr_time
.tv_sec
* 1.0e9
+
1047 currproc
->pr_time
.tv_nsec
) / timediff
;
1049 weighted_cpu(currproc
) = percent_cpu(currproc
);
1051 /* first screen -- no difference is possible */
1052 percent_cpu(currproc
) = 0.0;
1053 weighted_cpu(currproc
) = 0.0;
1064 * Bug: in case process count grew so dramatically
1065 * as to exceed to table size. We give up on a full scan.
1066 * the chances of this to happen are extremely slim due to
1067 * the big factor we're using. getting nproc from nlist
1068 * is not worth the headache. realloc wouldn't work either
1069 * because we have pointers to the proc table so we cannot
1072 if (numprocs
>= ptable_size
) {
1074 "preallocated proc table size (%d) exceeded, "
1075 "skipping some processes\n", ptable_size
);
1082 * Save current CPU time for next time around
1083 * For the moment recreate the hash table each time, as the code
1084 * is easier that way.
1086 oldprocs
= 2 * nproc
;
1087 endbase
= oldbase
+ oldprocs
;
1089 for (op
= oldbase
; op
< endbase
; op
++)
1092 for (i
= 0, currproc
= baseptr
; i
< nproc
; i
++, currproc
++) {
1094 /* find an empty spot */
1095 op
= oldbase
+ HASH (currproc
->pr_pid
);
1097 if (op
->oldpid
== -1)
1103 op
->oldpid
= currproc
->pr_pid
;
1104 op
->oldtime
= (currproc
->pr_time
.tv_sec
* 1.0e9
+
1105 currproc
->pr_time
.tv_nsec
);
1106 op
->oldpct
= weighted_cpu(currproc
);