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: Intel based System V Release 4
39 * System V release 4.0.x for i486
40 * System V release 4 for Okidata M88100
41 * System V release 4 for NCR 3000 series OS Rel 1.00 to 2.02
42 * System V release 4 for NCR 3000 series OS Rel 02.03.00 and above
43 * and probably other svr4 ports
47 * AUTHORS: Andrew Herbert <andrew@werple.apana.org.au>
48 * Robert Boucher <boucher@sofkin.ca>
49 * Ported to System 3000 Release 2.03 by:
50 * Jeff Janvrin <jeff.janvrinColumbiaSC.NCR.COM>
64 #if TIME_WITH_SYS_TIME
65 # include <sys/time.h>
69 # include <sys/time.h>
74 #include <sys/types.h>
76 #include <sys/param.h>
77 #include <sys/procfs.h>
78 #include <sys/sysinfo.h>
79 #include <sys/sysmacros.h>
80 #include <sys/vmmeter.h>
82 #include <sys/priocntl.h>
83 #include <sys/rtpriocntl.h>
84 #include <sys/tspriocntl.h>
85 #include <sys/procset.h>
88 #define UNIX "/stand/unix"
89 #define KMEM "/dev/kmem"
90 #define PROCFS "/proc"
101 #define FSHIFT 8 /* bits to right of fixed binary point */
102 #define FSCALE (1<<FSHIFT)
105 #define loaddouble(x) ((double)(x) / FSCALE)
106 #define percent_cpu(x) ((double)(x)->pr_cpu / FSCALE)
107 #define weighted_cpu(pct, pp) ( ((pp)->pr_time.tv_sec) == 0 ? 0.0 : \
108 ((pp)->pr_cpu) / ((pp)->pr_time.tv_sec) )
109 #define pagetok(size) ctob(size) >> LOG1024
111 /* definitions for the index in the nlist array */
120 static struct nlist nlst
[] =
126 {"anoninfo"}, /* 4 */
132 static unsigned long avenrun_offset
;
133 static unsigned long mpid_offset
;
134 static unsigned long nproc_offset
;
135 static unsigned long anoninfo_offset
;
136 static unsigned long total_offset
;
137 static unsigned long sysinfo_offset
;
139 /* get_process_info passes back a handle. This is what it looks like: */
143 struct prpsinfo
**next_proc
;/* points to next valid proc pointer */
144 int remaining
; /* number of pointers remaining */
148 * These definitions control the format of the per-process area
151 static char header
[] =
152 " PID X PRI NICE SIZE RES STATE TIME WCPU CPU COMMAND";
153 /* 0123456 -- field to fill in starts at header+6 */
154 #define UNAME_START 6
155 #define Proc_format \
156 "%5d %-8.8s %3d %4d %5s %5s %-5s %6s %3d.0%% %5.2f%% %.16s"
158 char *state_abbrev
[] =
159 {"", "sleep", "run", "zombie", "stop", "start", "cpu", "swap"};
161 int process_states
[8];
162 char *procstatenames
[] =
164 "", " sleeping, ", " running, ", " zombie, ", " stopped, ",
165 " starting, ", " on cpu, ", " swapped, ",
169 int cpu_states
[CPUSTATES
];
170 char *cpustatenames
[] =
171 {"idle", "user", "kernel", "wait", "swap", NULL
};
173 /* these are for detailing the memory statistics */
175 long memory_stats
[5];
176 char *memorynames
[] =
177 {"K real, ", "K active, ", "K free, ", "K swap, ", "K free swap", NULL
};
179 /* forward reference for qsort comparison function */
182 static int kmem
= -1;
185 static int use_stats
= 0;
186 static struct prpsinfo
*pbase
;
187 static struct prpsinfo
**pref
;
188 static DIR *proc_dir
;
190 /* useful externals */
192 extern char *sys_errlist
[];
194 extern int check_nlist ();
195 extern int getkval ();
196 extern void perror ();
197 extern void getptable ();
202 machine_init (struct statics
*statics
)
206 /* fill in the statics information */
207 statics
->procstate_names
= procstatenames
;
208 statics
->cpustate_names
= cpustatenames
;
209 statics
->memory_names
= memorynames
;
211 /* get the list of symbols we want to access in the kernel */
212 if (nlist (UNIX
, nlst
))
214 (void) fprintf (stderr
, "Unable to nlist %s\n", UNIX
);
218 /* make sure they were all found */
219 if (check_nlist (nlst
) > 0)
222 /* open kernel memory */
223 if ((kmem
= open (KMEM
, O_RDONLY
)) == -1)
229 /* get the symbol values out of kmem */
230 /* NPROC Tuning parameter for max number of processes */
231 (void) getkval (nlst
[X_V
].n_value
, &v
, sizeof (struct var
), nlst
[X_V
].n_name
);
234 /* stash away certain offsets for later use */
235 mpid_offset
= nlst
[X_MPID
].n_value
;
236 nproc_offset
= nlst
[X_NPROC
].n_value
;
237 avenrun_offset
= nlst
[X_AVENRUN
].n_value
;
238 anoninfo_offset
= nlst
[X_ANONINFO
].n_value
;
239 total_offset
= nlst
[X_TOTAL
].n_value
;
240 /* JJ this may need to be changed */
241 sysinfo_offset
= nlst
[X_SYSINFO
].n_value
;
243 /* allocate space for proc structure array and array of pointers */
244 bytes
= nproc
* sizeof (struct prpsinfo
);
245 pbase
= (struct prpsinfo
*) malloc (bytes
);
246 pref
= (struct prpsinfo
**) malloc (nproc
* sizeof (struct prpsinfo
*));
248 /* Just in case ... */
249 if (pbase
== (struct prpsinfo
*) NULL
|| pref
== (struct prpsinfo
**) NULL
)
251 (void) fprintf (stderr
, "%s: can't allocate sufficient memory\n", myname
);
255 if (!(proc_dir
= opendir (PROCFS
)))
257 (void) fprintf (stderr
, "Unable to open %s\n", PROCFS
);
262 { /* handy for later on when we're reading it */
263 (void) fprintf (stderr
, "Unable to chdir to %s\n", PROCFS
);
272 format_header (char *uname_field
)
276 ptr
= header
+ UNAME_START
;
277 while (*uname_field
!= '\0')
278 *ptr
++ = *uname_field
++;
284 get_system_info (struct system_info
*si
)
287 struct sysinfo sysinfo
;
288 static struct sysinfo
*mpinfo
= NULL
; /* array, per-processor sysinfo structures. */
289 struct vmtotal total
;
290 struct anoninfo anoninfo
;
291 static long cp_old
[CPUSTATES
];
292 static long cp_diff
[CPUSTATES
]; /* for cpu state percentages */
294 static int fd_cpu
= 0;
297 if ( use_stats
== 1) {
299 if ((fd_cpu
= open("/stats/cpuinfo", O_RDONLY
)) == -1) {
300 (void) fprintf (stderr
, "%s: Open of /stats/cpuinfo failed\n", myname
);
303 if (read(fd_cpu
, &num_cpus
, sizeof(int)) != sizeof(int)) {
304 (void) fprintf (stderr
, "%s: Read of /stats/cpuinfo failed\n", myname
);
309 if (mpinfo
== NULL
) {
310 mpinfo
= (struct sysinfo
*)calloc(num_cpus
, sizeof(mpinfo
[0]));
311 if (mpinfo
== NULL
) {
312 (void) fprintf (stderr
, "%s: can't allocate space for per-processor sysinfos\n", myname
);
316 /* Read the per cpu sysinfo structures into mpinfo struct. */
317 read_sysinfos(num_cpus
, mpinfo
);
318 /* Add up all of the percpu sysinfos to get global sysinfo */
319 sysinfo_data(num_cpus
, &sysinfo
, mpinfo
);
321 (void) getkval (sysinfo_offset
, &sysinfo
, sizeof (struct sysinfo
), "sysinfo");
324 /* convert cp_time counts to percentages */
325 (void) percentages (CPUSTATES
, cpu_states
, sysinfo
.cpu
, cp_old
, cp_diff
);
327 /* get mpid -- process id of last process */
328 (void) getkval (mpid_offset
, &(si
->last_pid
), sizeof (si
->last_pid
),
331 /* get load average array */
332 (void) getkval (avenrun_offset
, (int *) avenrun
, sizeof (avenrun
), "avenrun");
334 /* convert load averages to doubles */
335 for (i
= 0; i
< 3; i
++)
336 si
->load_avg
[i
] = loaddouble (avenrun
[i
]);
338 /* get total -- systemwide main memory usage structure */
339 (void) getkval (total_offset
, (int *) (&total
), sizeof (total
), "total");
340 /* convert memory stats to Kbytes */
341 memory_stats
[0] = pagetok (total
.t_rm
);
342 memory_stats
[1] = pagetok (total
.t_arm
);
343 memory_stats
[2] = pagetok (total
.t_free
);
344 (void) getkval (anoninfo_offset
, (int *) (&anoninfo
), sizeof (anoninfo
),
346 memory_stats
[3] = pagetok (anoninfo
.ani_max
- anoninfo
.ani_free
);
347 memory_stats
[4] = pagetok (anoninfo
.ani_max
- anoninfo
.ani_resv
);
349 /* set arrays and strings */
350 si
->cpustates
= cpu_states
;
351 si
->memory
= memory_stats
;
354 static struct handle handle
;
358 struct system_info
*si
,
359 struct process_select
*sel
,
363 register int total_procs
;
364 register int active_procs
;
365 register struct prpsinfo
**prefp
;
366 register struct prpsinfo
*pp
;
368 /* these are copied out of sel for speed */
373 /* Get current number of processes */
374 (void) getkval (nproc_offset
, (int *) (&nproc
), sizeof (nproc
), "nproc");
376 /* read all the proc structures */
379 /* get a pointer to the states summary array */
380 si
->procstates
= process_states
;
382 /* set up flags which define what we are going to select */
383 show_idle
= sel
->idle
;
384 show_system
= sel
->system
;
385 show_uid
= sel
->uid
!= -1;
387 /* count up process states and get pointers to interesting procs */
390 (void) memset (process_states
, 0, sizeof (process_states
));
393 for (pp
= pbase
, i
= 0; i
< nproc
; pp
++, i
++)
396 * Place pointers to each valid proc structure in pref[].
397 * Process slots that are actually in use have a non-zero
398 * status field. Processes with SSYS set are system
399 * processes---these get ignored unless show_sysprocs is set.
401 if (pp
->pr_state
!= 0 &&
402 (show_system
|| ((pp
->pr_flag
& SSYS
) == 0)))
405 process_states
[pp
->pr_state
]++;
406 if ((!pp
->pr_zomb
) &&
407 (show_idle
|| (pp
->pr_state
== SRUN
) || (pp
->pr_state
== SONPROC
)) &&
408 (!show_uid
|| pp
->pr_uid
== (uid_t
) sel
->uid
))
416 /* if requested, sort the "interesting" processes */
417 qsort ((char *) pref
, active_procs
, sizeof (struct prpsinfo
*), proc_compare
);
419 /* remember active and total counts */
420 si
->p_total
= total_procs
;
421 si
->p_active
= active_procs
;
423 /* pass back a handle */
424 handle
.next_proc
= pref
;
425 handle
.remaining
= active_procs
;
426 return ((caddr_t
) & handle
);
429 char fmt
[MAX_COLS
]; /* static area where result is built */
432 format_next_process (
434 char *(*get_userid
) ())
436 register struct prpsinfo
*pp
;
438 register long cputime
;
439 register double pctcpu
;
441 /* find and remember the next proc structure */
442 hp
= (struct handle
*) handle
;
443 pp
= *(hp
->next_proc
++);
446 /* get the cpu usage and calculate the cpu percentages */
447 cputime
= pp
->pr_time
.tv_sec
;
448 pctcpu
= percent_cpu (pp
);
450 /* format this entry */
454 (*get_userid
) (pp
->pr_uid
),
457 format_k(pagetok (pp
->pr_size
)),
458 format_k(pagetok (pp
->pr_rssize
)),
459 state_abbrev
[pp
->pr_state
],
460 format_time(cputime
),
463 printable(pp
->pr_fname
));
465 /* return the result */
470 * check_nlist(nlst) - checks the nlist to see if any symbols were not
471 * found. For every symbol that was not found, a one-line
472 * message is printed to stderr. The routine returns the
473 * number of symbols NOT found.
476 check_nlist (register struct nlist
*nlst
)
479 struct stat stat_buf
;
481 /* check to see if we got ALL the symbols we requested */
482 /* this will write one line to stderr for every symbol not found */
485 while (nlst
->n_name
!= NULL
)
487 if (nlst
->n_type
== 0)
489 if (strcmp("sysinfo", nlst
->n_name
) == 0)
491 /* check to see if /stats file system exists. If so, */
493 if ( !((stat("/stats/sysinfo", &stat_buf
) == 0) &&
494 (stat_buf
.st_mode
& S_IFREG
)) )
496 (void) fprintf (stderr
, "kernel: no symbol named `%s'\n", nlst
->n_name
);
503 /* this one wasn't found */
504 (void) fprintf (stderr
, "kernel: no symbol named `%s'\n", nlst
->n_name
);
515 * getkval(offset, ptr, size, refstr) - get a value out of the kernel.
516 * "offset" is the byte offset into the kernel for the desired value,
517 * "ptr" points to a buffer into which the value is retrieved,
518 * "size" is the size of the buffer (and the object to retrieve),
519 * "refstr" is a reference string used when printing error meessages,
520 * if "refstr" starts with a '!', then a failure on read will not
521 * be fatal (this may seem like a silly way to do things, but I
522 * really didn't want the overhead of another argument).
527 unsigned long offset
,
533 if (lseek (kmem
, (long) (offset
& 0x7fffffff), 0) == -1)
535 if (lseek (kmem
, (long) offset
, 0) == -1)
540 (void) fprintf (stderr
, "%s: lseek to %s: %s\n",
541 myname
, refstr
, sys_errlist
[errno
]);
544 if (read (kmem
, (char *) ptr
, size
) == -1)
546 /* we lost the race with the kernel, process isn't in memory */
550 (void) fprintf (stderr
, "%s: reading %s: %s\n",
551 myname
, refstr
, sys_errlist
[errno
]);
557 /* comparison routine for qsort */
560 * proc_compare - comparison function for "qsort"
561 * Compares the resource consumption of two processes using five
562 * distinct keys. The keys (in descending order of importance) are:
563 * percent cpu, cpu ticks, state, resident set size, total virtual
564 * memory usage. The process states are ordered as follows (from least
565 * to most important): WAIT, zombie, sleep, stop, start, run. The
566 * array declaration below maps a process state index into a number
567 * that reflects this ordering.
571 unsigned char sorted_state
[] =
579 7, /* run on a processor */
580 1 /* being swapped (WAIT) */
585 struct prpsinfo
**pp1
,
586 struct prpsinfo
**pp2
)
588 register struct prpsinfo
*p1
;
589 register struct prpsinfo
*p2
;
590 register long result
;
592 /* remove one level of indirection */
596 /* compare percent cpu (pctcpu) */
597 if ((result
= (long) (p2
->pr_cpu
- p1
->pr_cpu
)) == 0)
599 /* use cpticks to break the tie */
600 if ((result
= p2
->pr_time
.tv_sec
- p1
->pr_time
.tv_sec
) == 0)
602 /* use process state to break the tie */
603 if ((result
= (long) (sorted_state
[p2
->pr_state
] -
604 sorted_state
[p1
->pr_state
])) == 0)
606 /* use priority to break the tie */
607 if ((result
= p2
->pr_oldpri
- p1
->pr_oldpri
) == 0)
609 /* use resident set size (rssize) to break the tie */
610 if ((result
= p2
->pr_rssize
- p1
->pr_rssize
) == 0)
612 /* use total memory to break the tie */
613 result
= (p2
->pr_size
- p1
->pr_size
);
626 getptable (struct prpsinfo
*baseptr
)
628 struct prpsinfo
*currproc
; /* pointer to current proc structure */
630 struct dirent
*direntp
;
632 for (rewinddir (proc_dir
); direntp
= readdir (proc_dir
);)
636 if ((fd
= open (direntp
->d_name
, O_RDONLY
)) < 0)
639 currproc
= &baseptr
[numprocs
];
640 if (ioctl (fd
, PIOCPSINFO
, currproc
) < 0)
650 if (nproc
!= numprocs
)
654 /* return the owner of the specified process, for use in commands.c as we're
655 running setuid root */
659 register struct prpsinfo
*p
;
661 for (i
= 0, p
= pbase
; i
< nproc
; i
++, p
++)
662 if (p
->pr_pid
== (pid_t
)pid
)
668 #ifndef HAVE_SETPRIORITY
670 setpriority (int dummy
, int who
, int niceval
)
678 strcpy (pcinfo
.pc_clname
, "TS");
679 if (priocntl (0, 0, PC_GETCID
, (caddr_t
) & pcinfo
) == -1)
685 else if (prio
< PRIO_MIN
)
688 tsparms
= (tsparms_t
*) pcparms
.pc_clparms
;
689 scale
= ((tsinfo_t
*) pcinfo
.pc_clinfo
)->ts_maxupri
;
690 tsparms
->ts_uprilim
= tsparms
->ts_upri
= -(scale
* prio
) / 20;
691 pcparms
.pc_cid
= pcinfo
.pc_cid
;
693 if (priocntl (P_PID
, who
, PC_SETPARMS
, (caddr_t
) & pcparms
) == -1)
700 /****************************************************************
701 * read_sysinfos() - *
702 * Read all of the CPU specific sysinfo sturctures in from *
703 * the /stats file system. *
704 ****************************************************************/
705 read_sysinfos(num_cpus
, buf
)
710 static int fd1
=0; /* file descriptor for /stats/sysinfo */
713 /* Open /stats/sysinfo one time only and leave it open */
715 if ((fd1
= open("/stats/sysinfo", O_RDONLY
)) == -1)
716 (void) fprintf (stderr
, "%s: Open of /stats/sysinfo failed\n", myname
);
718 /* reset the read pointer to the beginning of the file */
719 if (lseek(fd1
, 0L, SEEK_SET
) == -1)
720 (void) fprintf (stderr
, "%s: lseek to beginning of /stats/sysinfo failed\n", myname
);
721 read_sz
= num_cpus
* sizeof(buf
[0]);
722 if (read(fd1
, buf
, read_sz
) != read_sz
)
723 (void) fprintf (stderr
, "%s: Read of /stats/sysinfo failed\n", myname
);
726 /****************************************************************
728 * Add up all of the CPU specific sysinfo sturctures to *
729 * make the GLOBAL sysinfo. *
730 ****************************************************************/
731 sysinfo_data(num_cpus
, global_si
, percpu_si
)
733 struct sysinfo
*global_si
;
734 struct sysinfo
*percpu_si
;
736 struct sysinfo
*percpu_p
;
737 int cpu
, i
, *global
, *src
;
739 /* null out the global statistics from last sample */
740 memset(global_si
, 0, sizeof(struct sysinfo
));
742 percpu_p
= (struct sysinfo
*)percpu_si
;
743 for(cpu
= 0; cpu
< num_cpus
; cpu
++) {
744 global
= (int *)global_si
;
745 src
= (int *)percpu_p
;
747 /* assume sysinfo ends on an int boundary */
748 /* Currently, all of the struct sysinfo members are the same
749 * size as an int. If that changes, we may not be able to
750 * do this. But this should be safe.
752 for(i
=0; i
<sizeof(struct sysinfo
)/sizeof(int); i
++) {