etc/protocols - sync with NetBSD-8
[minix.git] / external / bsd / top / dist / machine / m_irixsgi.c
blobcd9fa9c9f2b58118ac0edda24f2533aa42242463
1 /*
2 * Copyright (c) 1984 through 2008, William LeFebvre
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 *
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
14 * distribution.
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
38 * DESCRIPTION:
39 * This is the machine-dependent module for IRIX as supplied by
40 * engineers at SGI.
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
52 * dependencies
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
62 #define _KMEMUSER
64 #include "config.h"
66 #include <sys/types.h>
67 #include <sys/time.h>
68 #include <sys/stat.h>
69 #include <sys/swap.h>
70 #include <sys/proc.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. */
76 #include <paths.h>
77 #include <assert.h>
78 #include <values.h>
79 #include <dirent.h>
80 #include <stdio.h>
81 #include <unistd.h>
82 #include <stdlib.h>
83 #include <errno.h>
84 #include <fcntl.h>
85 #include <dlfcn.h>
87 #include "top.h"
88 #include "machine.h"
89 #include "utils.h"
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.
102 struct oldproc {
103 pid_t oldpid;
104 double oldtime;
105 double oldpct;
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
134 10 20 30 40 50 60 70
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:
147 * #define CPU_IDLE 0
148 * #define CPU_USER 1
149 * #define CPU_KERNEL 2
150 * #define CPU_WAIT 3
151 * #define CPU_SXBRK 4
152 * #define CPU_INTR 5
154 #ifndef CPU_STATES /* defined only in 6.4 and up */
155 # define CPU_STATES 6
156 #endif
158 int cpu_states[CPU_STATES];
159 char *cpustatenames[] = {
160 "idle", "usr", "ker", "wait", "xbrk", "intr",
161 NULL
164 /* these are for detailing the memory statistics */
166 #define MEMSTATS 10
167 int memory_stats[MEMSTATS];
168 char *memorynames[] = {
169 "K max, ", "K avail, ", "K free, ", "K swap, ", "K free swap", NULL
172 char uname_str[40];
173 double load[3];
174 static char fmt[MAX_COLS + 2];
175 int numcpus;
177 /* useful externals */
178 extern int errno;
179 extern char *sys_errlist[];
181 extern char *myname;
182 extern char *format_k();
183 extern char *format_time();
184 extern long percentages();
186 static int kmem;
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 */
196 static DIR *procdir;
198 static int ptable_size; /* allocated process table size */
199 static int nproc; /* estimated process table size */
200 static int pagesize;
202 /* get_process_info passes back a handle. This is what it looks like: */
203 struct handle {
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, ",
244 NULL
247 #define S_RUNNING 7
248 #define S_READY 6
249 #define S_STARTING 5
250 #define S_SWAPPED 4
251 #define S_STOPPED 3
252 #define S_ZOMBIE 2
253 #define S_SLEEPING 1
255 #define IS_ACTIVE(pp) \
256 (first_screen ? proc_state(pp) >= S_STARTING : percent_cpu(pp) > 0.0)
259 * proc_state
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;
268 switch (psname) {
269 case 'R': return
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;
277 default : return 0;
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()
291 DIR *dirp;
292 int pcnt;
294 if ((dirp = opendir(".")) == NULL) {
295 (void) fprintf(stderr, "%s: Unable to open %s\n",
296 myname, _PATH_PROCFSPI);
297 exit(1);
299 for (pcnt = 0; readdir(dirp) != NULL; pcnt++)
301 closedir(dirp);
303 return 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
312 void
313 allocate_proc_tables()
315 int n_active = active_proc_count();
317 if (pbase != NULL) /* && n_active < ptable_size */
318 return;
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));
331 ptable_size = nproc;
333 if (pbase == NULL || pref == NULL || oldbase == NULL) {
334 (void) fprintf(stderr, "%s: malloc: out of memory\n", myname);
335 exit (1);
340 machine_init(struct statics *statics)
342 struct oldproc *op, *endbase;
343 int pcnt = 0;
344 struct utsname utsname;
345 char tmpbuf[20];
347 uname(&utsname);
348 irix_ver = (float) atof((const char *)utsname.release);
349 strncpy(tmpbuf, utsname.release, 9);
350 tmpbuf[9] = '\0';
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) {
358 perror(KMEM);
359 return -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);
366 return -1;
368 if ((procdir = opendir(".")) == NULL) {
369 (void) fprintf(stderr, "%s: Unable to open %s\n",
370 myname, _PATH_PROCFSPI);
371 return -1;
374 if ((avenrun_offset = sysmp(MP_KERNADDR, MPKA_AVENRUN)) == -1) {
375 perror("sysmp(MP_KERNADDR, MPKA_AVENRUN)");
376 return -1;
379 allocate_proc_tables();
381 oldprocs = 2 * nproc;
382 endbase = oldbase + oldprocs;
383 for (op = oldbase; op < endbase; op++) {
384 op->oldpid = -1;
387 statics->cpustate_names = cpustatenames;
388 statics->memory_names = memorynames;
389 statics->order_names = ordernames;
390 statics->procstate_names = procstatenames;
392 return (0);
395 char *
396 format_header(register char *uname_field)
399 register char *ptr;
401 ptr = header + UNAME_START;
402 while (*uname_field != '\0') {
403 *ptr++ = *uname_field++;
406 return (header);
409 void
410 get_system_info(struct system_info *si)
413 int i;
414 int avenrun[3];
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)");
431 return;
434 if (sysmp(MP_SAGET, MPSA_RMINFO, &realmem, sizeof(realmem)) == -1) {
435 perror("sysmp(MP_SAGET,MPSA_RMINFO, ...)");
436 return;
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)");
450 return;
452 (void) percentages(CPU_STATES, cpu_states, sysinfo.cpu, cp_old, cp_diff);
454 si->cpustates = cpu_states;
455 si->memory = memory_stats;
456 si->last_pid = -1;
458 return;
461 caddr_t
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;
467 struct prpsinfo *pp;
468 int show_uid;
469 static char first_screen = 1;
471 /* read all the proc structures */
472 getptable(pbase);
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 */
481 total_procs = 0;
482 active_procs = 0;
483 (void) memset(process_states, 0, sizeof(process_states));
484 prefp = pref;
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)
496 if (pp->pr_state &&
497 (sel->system || !(pp->pr_uid==0 && pp->pr_ppid==1))) {
498 total_procs++;
499 process_states[proc_state(pp)]++;
501 * zombies are actually interesting (to avoid)
502 * although they are not active, so I leave them
503 * displayed.
505 if (/* (! pp->pr_zomb) && */
506 (sel->idle || IS_ACTIVE(pp)) &&
507 (! show_uid || pp->pr_uid == (uid_t) sel->uid)) {
508 *prefp++ = pp;
509 active_procs++;
513 first_screen = 0;
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
532 static char *
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);
549 return state_str;
552 /* default */
553 return state_abbrev[state];
556 static char *
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
570 * on the OS version.
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);
582 } else {
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 */
596 return "w";
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 */
601 else
602 sprintf(prio_str, "%d", pp->pr_pri);
604 return prio_str;
607 static double
608 clip_percent(double pct)
611 if (pct < 0) {
612 return 0.0;
613 } else if (pct >= 100) {
614 return 99.99;
616 return pct;
619 char *
620 format_next_process(caddr_t handle, char *(*get_userid)())
623 struct prpsinfo *pp;
624 struct handle *hp;
625 long cputime;
627 /* find and remember the next proc structure */
628 hp = (struct handle *) handle;
629 pp = *(hp->next_proc++);
630 hp->remaining--;
632 /* get the process cpu usage since startup */
633 cputime = pp->pr_time.tv_sec;
635 /* format this entry */
636 sprintf(fmt,
637 Proc_format,
638 pp->pr_pid,
639 pp->pr_pgrp,
640 (*get_userid) (pp->pr_uid),
641 format_prio(pp),
642 format_k(pagetok(pp->pr_size)),
643 format_k(pagetok(pp->pr_rssize)),
644 format_state(pp),
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 */
651 return (fmt);
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) {
671 if (*refstr == '!')
672 refstr++;
673 (void) fprintf(stderr, "%s: %s: lseek to %s: %s\n",
674 myname, KMEM, refstr, strerror(errno));
675 exit(0);
677 if (read(kmem, (char *) ptr, size) == -1) {
678 if (*refstr == '!')
679 return (0);
680 else {
681 (void) fprintf(stderr, "%s: %s: reading %s: %s\n",
682 myname, KMEM, refstr, strerror(errno));
683 exit(0);
686 return (1);
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:
703 * mem == size
704 * rss == res
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[])() = {
720 compare_cpu,
721 compare_size,
722 compare_size,
723 compare_res,
724 compare_res,
725 compare_time,
726 compare_state,
727 compare_cmd,
728 compare_prio,
729 NULL
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
736 * desired ordering.
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;
753 int result;
754 double dresult;
756 /* remove one level of indirection */
757 p1 = *pp1;
758 p2 = *pp2;
760 * order by various keys, resorting to the next one
761 * whenever there's a tie in comparisons
763 ORDERKEY_PCTCPU
764 ORDERKEY_CPTICKS
765 ORDERKEY_STATE
766 ORDERKEY_PRIO
767 ORDERKEY_RSSIZE
768 ORDERKEY_MEM
770 return (result);
773 int compare_size(struct prpsinfo **pp1, struct prpsinfo **pp2)
775 struct prpsinfo *p1, *p2;
776 int result;
777 double dresult;
779 /* remove one level of indirection */
780 p1 = *pp1;
781 p2 = *pp2;
783 * order by various keys, resorting to the next one
784 * whenever there's a tie in comparisons
786 ORDERKEY_MEM
787 ORDERKEY_RSSIZE
788 ORDERKEY_PCTCPU
789 ORDERKEY_CPTICKS
790 ORDERKEY_STATE
791 ORDERKEY_PRIO
793 return (result);
796 int compare_res(struct prpsinfo **pp1, struct prpsinfo **pp2)
798 struct prpsinfo *p1, *p2;
799 int result;
800 double dresult;
802 /* remove one level of indirection */
803 p1 = *pp1;
804 p2 = *pp2;
806 * order by various keys, resorting to the next one
807 * whenever there's a tie in comparisons
809 ORDERKEY_RSSIZE
810 ORDERKEY_MEM
811 ORDERKEY_PCTCPU
812 ORDERKEY_CPTICKS
813 ORDERKEY_STATE
814 ORDERKEY_PRIO
816 return (result);
819 int compare_time(struct prpsinfo **pp1, struct prpsinfo **pp2)
821 struct prpsinfo *p1, *p2;
822 int result;
823 double dresult;
825 /* remove one level of indirection */
826 p1 = *pp1;
827 p2 = *pp2;
829 * order by various keys, resorting to the next one
830 * whenever there's a tie in comparisons
832 ORDERKEY_CPTICKS
833 ORDERKEY_RSSIZE
834 ORDERKEY_MEM
835 ORDERKEY_PCTCPU
836 ORDERKEY_STATE
837 ORDERKEY_PRIO
839 return (result);
842 int compare_cmd(struct prpsinfo **pp1, struct prpsinfo **pp2)
844 struct prpsinfo *p1, *p2;
845 int result;
846 double dresult;
848 /* remove one level of indirection */
849 p1 = *pp1;
850 p2 = *pp2;
852 * order by various keys, resorting to the next one
853 * whenever there's a tie in comparisons
855 ORDERKEY_CMD
856 ORDERKEY_PCTCPU
857 ORDERKEY_CPTICKS
858 ORDERKEY_RSSIZE
860 return (result);
863 int compare_state(struct prpsinfo **pp1, struct prpsinfo **pp2)
865 struct prpsinfo *p1, *p2;
866 int result;
867 double dresult;
869 /* remove one level of indirection */
870 p1 = *pp1;
871 p2 = *pp2;
873 * order by various keys, resorting to the next one
874 * whenever there's a tie in comparisons
876 ORDERKEY_STATE
877 ORDERKEY_PCTCPU
878 ORDERKEY_CPTICKS
879 ORDERKEY_RSSIZE
881 return (result);
884 int compare_prio(struct prpsinfo **pp1, struct prpsinfo **pp2)
886 struct prpsinfo *p1, *p2;
887 int result;
888 double dresult;
890 /* remove one level of indirection */
891 p1 = *pp1;
892 p2 = *pp2;
894 * order by various keys, resorting to the next one
895 * whenever there's a tie in comparisons
897 ORDERKEY_PRIO
898 ORDERKEY_PCTCPU
900 return (result);
905 /* return the owner of the specified process. */
906 uid_t
907 proc_owner(pid_t pid)
910 register struct prpsinfo *p;
911 int i;
913 for (i = 0, p = pbase; i < nproc; i++, p++)
914 if (p->pr_pid == pid)
915 return (p->pr_uid);
917 return (-1);
920 #ifdef DO_MAPSIZE
921 static void
922 size(int fd, struct prpsinfo *ps)
925 prmap_sgi_arg_t maparg;
926 struct prmap_sgi maps[256];
927 int nmaps;
928 double sz;
929 int i;
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 */
935 return;
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;
942 #endif
944 /* get process table */
945 void
946 getptable(struct prpsinfo *baseptr)
949 struct prpsinfo *currproc; /* ptr to current proc struct */
950 int i, numprocs;
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
962 * get percent.
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);
990 beta = 1.0 - alpha;
991 } else {
992 alpha = 0.5;
993 beta = 0.5;
995 assert(alpha >= 0); assert(alpha <= 1);
996 assert(beta >= 0); assert(beta <= 1);
998 endbase = oldbase + oldprocs;
999 currproc = baseptr;
1001 for (numprocs = 0, rewinddir(procdir); direntp = readdir(procdir);) {
1002 int fd;
1004 if ((fd = open(direntp->d_name, O_RDONLY)) < 0)
1005 continue;
1007 currproc = baseptr + numprocs;
1009 if (ioctl(fd, PIOCPSINFO, currproc) < 0) {
1010 (void) close(fd);
1011 continue;
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);
1021 for (;;) {
1022 if (op->oldpid == -1) /* not there */
1023 break;
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) =
1032 op->oldpct * beta +
1033 percent_cpu(currproc) * alpha;
1035 break;
1037 op++; /* try next entry in hash table */
1038 if (op == endbase) /* table wrap around */
1039 op = oldbase;
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);
1050 } else {
1051 /* first screen -- no difference is possible */
1052 percent_cpu(currproc) = 0.0;
1053 weighted_cpu(currproc) = 0.0;
1057 #ifdef DO_MAPSIZE
1058 size(fd, currproc);
1059 #endif
1060 numprocs++;
1061 (void) close(fd);
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
1070 * move it around.
1072 if (numprocs >= ptable_size) {
1073 fprintf(stderr,
1074 "preallocated proc table size (%d) exceeded, "
1075 "skipping some processes\n", ptable_size);
1076 break;
1079 nproc = numprocs;
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++)
1090 op->oldpid = -1;
1092 for (i = 0, currproc = baseptr; i < nproc; i++, currproc++) {
1094 /* find an empty spot */
1095 op = oldbase + HASH (currproc->pr_pid);
1096 for (;;) {
1097 if (op->oldpid == -1)
1098 break;
1099 op++;
1100 if (op == endbase)
1101 op = oldbase;
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);