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 hp9000 running hpux version 8 (may work with 9)
39 * This is the machine-dependent module for HPUX 8 and is rumored to work
40 * for version 9 as well. This makes top work on (at least) the
48 * AUTHOR: Christos Zoulas <christos@ee.cornell.edu>
52 #include <sys/types.h>
53 #include <sys/signal.h>
54 #include <sys/param.h>
67 # define P_RSSIZE(p) (p)->p_rssize
68 # define P_TSIZE(p) (p)->p_tsize
69 # define P_DSIZE(p) (p)->p_dsize
70 # define P_SSIZE(p) (p)->p_ssize
72 # include <sys/pstat.h>
73 # define __PST2P(p, field) \
74 ((p)->p_upreg ? ((struct pst_status *) (p)->p_upreg)->field : 0)
75 # define P_RSSIZE(p) __PST2P(p, pst_rssize)
76 # define P_TSIZE(p) __PST2P(p, pst_tsize)
77 # define P_DSIZE(p) __PST2P(p, pst_dsize)
78 # define P_SSIZE(p) __PST2P(p, pst_ssize)
85 #define VMUNIX "/hp-ux"
86 #define KMEM "/dev/kmem"
87 #define MEM "/dev/mem"
89 #define SWAP "/dev/dmem"
92 /* get_process_info passes back a handle. This is what it looks like: */
96 struct proc
**next_proc
; /* points to next valid proc pointer */
97 int remaining
; /* number of pointers remaining */
100 /* declarations for load_avg */
103 /* define what weighted cpu is. */
104 #define weighted_cpu(pct, pp) ((pp)->p_time == 0 ? 0.0 : \
105 ((pct) / (1.0 - exp((pp)->p_time * logcpu))))
107 /* what we consider to be process size: */
108 #define PROCSIZE(pp) (P_TSIZE(pp) + P_DSIZE(pp) + P_SSIZE(pp))
110 /* definitions for indices in the nlist array */
120 * Steinar Haug from University of Trondheim, NORWAY pointed out that
121 * the HP 9000 system 800 doesn't have _hz defined in the kernel. He
122 * provided a patch to work around this. We've improved on this patch
123 * here and set the constant X_HZ only when _hz is available in the
124 * kernel. Code in this module that uses X_HZ is surrounded with
125 * appropriate ifdefs.
133 static struct nlist nlst
[] = {
134 { "_avenrun" }, /* 0 */
136 { "_nproc" }, /* 2 */
138 { "_total" }, /* 4 */
139 { "_cp_time" }, /* 5 */
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
156 #define Proc_format \
157 "%5d %-8.8s %3d %4d %5s %5s %-5s %6s %5.2f%% %5.2f%% %s"
160 /* process state names for the "STATE" column of the display */
161 /* the extra nulls in the string "run" are for adding a slash and
162 the processor number when needed */
164 char *state_abbrev
[] =
166 "", "sleep", "WAIT", "run\0\0\0", "start", "zomb", "stop"
172 /* values that we stash away in _init and use in later routines */
174 static double logcpu
;
176 /* these are retrieved from the kernel in _init */
178 static unsigned long proc
;
181 static load_avg ccpu
;
184 /* these are offsets obtained via nlist and used in the get_ functions */
185 static unsigned long mpid_offset
;
186 static unsigned long avenrun_offset
;
187 static unsigned long total_offset
;
188 static unsigned long cp_time_offset
;
190 /* these are for calculating cpu state percentages */
192 static long cp_time
[CPUSTATES
];
193 static long cp_old
[CPUSTATES
];
194 static long cp_diff
[CPUSTATES
];
196 /* these are for detailing the process states */
198 int process_states
[7];
199 char *procstatenames
[] = {
200 "", " sleeping, ", " ABANDONED, ", " running, ", " starting, ",
201 " zombie, ", " stopped, ",
205 /* these are for detailing the cpu states */
208 char *cpustatenames
[] = {
209 "usr", "nice", "sys", "idle", "", "", "", "intr", "ker",
213 /* these are for detailing the memory statistics */
215 long memory_stats
[8];
216 char *memorynames
[] = {
217 "Real: ", "K act, ", "K tot ", "Virtual: ", "K act, ",
218 "K tot, ", "K free", NULL
221 /* these are for keeping track of the proc array */
225 static struct proc
*pbase
;
226 static struct proc
**pref
;
227 static struct pst_status
*pst
;
229 /* these are for getting the memory statistics */
231 static int pageshift
; /* log base 2 of the pagesize */
233 /* define pagetok in terms of pageshift */
235 #define pagetok(size) ((size) << pageshift)
237 /* useful externals */
239 extern char *sys_errlist
[];
244 machine_init(statics
)
246 struct statics
*statics
;
250 register int pagesize
;
252 if ((kmem
= open(KMEM
, O_RDONLY
)) == -1) {
257 /* 800 names don't have leading underscores */
258 for (i
= 0; nlst
[i
].n_name
; nlst
[i
++].n_name
++)
262 /* get the list of symbols we want to access in the kernel */
263 (void) nlist(VMUNIX
, nlst
);
264 if (nlst
[0].n_type
== 0)
266 fprintf(stderr
, "top: nlist failed\n");
270 /* make sure they were all found */
271 if (check_nlist(nlst
) > 0)
276 /* get the symbol values out of kmem */
277 (void) getkval(nlst
[X_PROC
].n_value
, (int *)(&proc
), sizeof(proc
),
278 nlst
[X_PROC
].n_name
);
279 (void) getkval(nlst
[X_NPROC
].n_value
, &nproc
, sizeof(nproc
),
280 nlst
[X_NPROC
].n_name
);
281 (void) getkval(nlst
[X_CCPU
].n_value
, (int *)(&ccpu
), sizeof(ccpu
),
282 nlst
[X_CCPU
].n_name
);
284 (void) getkval(nlst
[X_HZ
].n_value
, (int *)(&hz
), sizeof(hz
),
290 /* stash away certain offsets for later use */
291 mpid_offset
= nlst
[X_MPID
].n_value
;
292 avenrun_offset
= nlst
[X_AVENRUN
].n_value
;
293 total_offset
= nlst
[X_TOTAL
].n_value
;
294 cp_time_offset
= nlst
[X_CP_TIME
].n_value
;
296 /* this is used in calculating WCPU -- calculate it ahead of time */
297 logcpu
= log(loaddouble(ccpu
));
299 /* allocate space for proc structure array and array of pointers */
300 bytes
= nproc
* sizeof(struct proc
);
301 pbase
= (struct proc
*)malloc(bytes
);
302 pref
= (struct proc
**)malloc(nproc
* sizeof(struct proc
*));
303 pst
= (struct pst_status
*)malloc(nproc
* sizeof(struct pst_status
));
305 /* Just in case ... */
306 if (pbase
== (struct proc
*)NULL
|| pref
== (struct proc
**)NULL
)
308 fprintf(stderr
, "top: can't allocate sufficient memory\n");
312 /* get the page size with "getpagesize" and calculate pageshift from it */
313 pagesize
= getpagesize();
321 /* we only need the amount of log(2)1024 for our conversion */
322 pageshift
-= LOG1024
;
324 /* fill in the statics information */
325 statics
->procstate_names
= procstatenames
;
326 statics
->cpustate_names
= cpustatenames
;
327 statics
->memory_names
= memorynames
;
333 char *format_header(uname_field
)
335 register char *uname_field
;
340 ptr
= header
+ UNAME_START
;
341 while (*uname_field
!= '\0')
343 *ptr
++ = *uname_field
++;
352 struct system_info
*si
;
358 /* get the cp_time array */
359 (void) getkval(cp_time_offset
, (int *)cp_time
, sizeof(cp_time
),
362 /* get load average array */
363 (void) getkval(avenrun_offset
, (int *)avenrun
, sizeof(avenrun
),
366 /* get mpid -- process id of last process */
367 (void) getkval(mpid_offset
, &(si
->last_pid
), sizeof(si
->last_pid
),
370 /* convert load averages to doubles */
373 register double *infoloadp
;
374 register load_avg
*sysloadp
;
376 infoloadp
= si
->load_avg
;
378 for (i
= 0; i
< 3; i
++)
380 *infoloadp
++ = loaddouble(*sysloadp
++);
384 /* convert cp_time counts to percentages */
385 total
= percentages(CPUSTATES
, cpu_states
, cp_time
, cp_old
, cp_diff
);
387 /* sum memory statistics */
389 struct vmtotal total
;
391 /* get total -- systemwide main memory usage structure */
392 (void) getkval(total_offset
, (int *)(&total
), sizeof(total
),
394 /* convert memory stats to Kbytes */
395 memory_stats
[0] = -1;
396 memory_stats
[1] = pagetok(total
.t_arm
);
397 memory_stats
[2] = pagetok(total
.t_rm
);
398 memory_stats
[3] = -1;
399 memory_stats
[4] = pagetok(total
.t_avm
);
400 memory_stats
[5] = pagetok(total
.t_vm
);
401 memory_stats
[6] = pagetok(total
.t_free
);
404 /* set arrays and strings */
405 si
->cpustates
= cpu_states
;
406 si
->memory
= memory_stats
;
409 static struct handle handle
;
411 caddr_t
get_process_info(si
, sel
, i
)
413 struct system_info
*si
;
414 struct process_select
*sel
;
419 register int total_procs
;
420 register int active_procs
;
421 register struct proc
**prefp
;
422 register struct proc
*pp
;
424 /* these are copied out of sel for speed */
430 /* read all the proc structures in one fell swoop */
431 (void) getkval(proc
, (int *)pbase
, bytes
, "proc array");
432 for (i
= 0; i
< nproc
; ++i
) {
433 if (pstat(PSTAT_PROC
, &pst
[i
], sizeof(pst
[i
]), 0, pbase
[i
].p_pid
) != 1)
434 pbase
[i
].p_upreg
= (preg_t
*) 0;
436 pbase
[i
].p_upreg
= (preg_t
*) &pst
[i
];
437 pbase
[i
].p_nice
= pst
[i
].pst_nice
;
438 pbase
[i
].p_cpticks
= pst
[i
].pst_cpticks
;
442 /* get a pointer to the states summary array */
443 si
->procstates
= process_states
;
445 /* set up flags which define what we are going to select */
446 show_idle
= sel
->idle
;
447 show_system
= sel
->system
;
448 show_uid
= sel
->uid
!= -1;
449 show_command
= sel
->command
!= NULL
;
451 /* count up process states and get pointers to interesting procs */
454 memset((char *)process_states
, 0, sizeof(process_states
));
456 for (pp
= pbase
, i
= 0; i
< nproc
; pp
++, i
++)
459 * Place pointers to each valid proc structure in pref[].
460 * Process slots that are actually in use have a non-zero
461 * status field. Processes with SSYS set are system
462 * processes---these get ignored unless show_sysprocs is set.
464 if (pp
->p_stat
!= 0 &&
465 (show_system
|| ((pp
->p_flag
& SSYS
) == 0)))
468 process_states
[pp
->p_stat
]++;
469 if ((pp
->p_stat
!= SZOMB
) &&
470 (show_idle
|| (pp
->p_pctcpu
!= 0) || (pp
->p_stat
== SRUN
)) &&
471 (!show_uid
|| pp
->p_uid
== (uid_t
)sel
->uid
))
479 /* if requested, sort the "interesting" processes */
482 qsort((char *)pref
, active_procs
, sizeof(struct proc
*), proc_compare
);
485 /* remember active and total counts */
486 si
->p_total
= total_procs
;
487 si
->p_active
= pref_len
= active_procs
;
489 /* pass back a handle */
490 handle
.next_proc
= pref
;
491 handle
.remaining
= active_procs
;
492 return((caddr_t
)&handle
);
495 char fmt
[MAX_COLS
]; /* static area where result is built */
497 char *format_next_process(handle
, get_userid
)
500 char *(*get_userid
)();
503 register struct proc
*pp
;
504 register long cputime
;
510 /* find and remember the next proc structure */
511 hp
= (struct handle
*)handle
;
512 pp
= *(hp
->next_proc
++);
516 /* get the process's user struct and set cputime */
517 where
= getu(pp
, &u
);
520 (void) strcpy(u
.u_comm
, "<swapped>");
527 /* set u_comm for system processes */
528 if (u
.u_comm
[0] == '\0')
532 (void) strcpy(u
.u_comm
, "Swapper");
534 else if (pp
->p_pid
== 2)
536 (void) strcpy(u
.u_comm
, "Pager");
541 * Print swapped processes as <pname>
543 char buf
[sizeof(u
.u_comm
)];
544 (void) strncpy(buf
, u
.u_comm
, sizeof(u
.u_comm
));
546 (void) strncpy(&u
.u_comm
[1], buf
, sizeof(u
.u_comm
) - 2);
547 u
.u_comm
[sizeof(u
.u_comm
) - 2] = '\0';
548 (void) strncat(u
.u_comm
, ">", sizeof(u
.u_comm
) - 1);
549 u
.u_comm
[sizeof(u
.u_comm
) - 1] = '\0';
552 cputime
= __PST2P(pp
, pst_cptickstotal
) / hz
;
555 /* calculate the base for cpu percentages */
556 pct
= pctdouble(pp
->p_pctcpu
);
558 /* format this entry */
562 (*get_userid
)(pp
->p_uid
),
565 format_k(pagetok(PROCSIZE(pp
))),
566 format_k(pagetok(P_RSSIZE(pp
))),
567 state_abbrev
[pp
->p_stat
],
568 format_time(cputime
),
569 100.0 * weighted_cpu(pct
, pp
),
571 printable(u
.u_comm
));
573 /* return the result */
578 * getu(p, u) - get the user structure for the process whose proc structure
579 * is pointed to by p. The user structure is put in the buffer pointed
580 * to by u. Return 0 if successful, -1 on failure (such as the process
581 * being swapped out).
587 register struct proc
*p
;
591 struct pst_status
*ps
;
595 if ((ps
= (struct pst_status
*) p
->p_upreg
) == NULL
)
598 memset(u
, 0, sizeof(struct user
));
600 ps
->pst_cmd
[PST_CLEN
- 1] = '\0'; /* paranoia */
601 s
= strtok(ps
->pst_cmd
, "\t \n");
603 if (c
= strrchr(s
, '/'))
610 for (; i
< MAXCOMLEN
; i
++) {
611 if (*c
== '\0' || *c
== ' ' || *c
== '/')
616 return ((p
->p_flag
& SLOAD
) == 0 ? 1 : 0);
622 * check_nlist(nlst) - checks the nlist to see if any symbols were not
623 * found. For every symbol that was not found, a one-line
624 * message is printed to stderr. The routine returns the
625 * number of symbols NOT found.
628 int check_nlist(nlst
)
630 register struct nlist
*nlst
;
635 /* check to see if we got ALL the symbols we requested */
636 /* this will write one line to stderr for every symbol not found */
639 while (nlst
->n_name
!= NULL
)
641 if (nlst
->n_type
== 0)
643 /* this one wasn't found */
644 fprintf(stderr
, "kernel: no symbol named `%s'\n", nlst
->n_name
);
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).
666 getkval(offset
, ptr
, size
, refstr
)
668 unsigned long offset
;
674 if (lseek(kmem
, (long)offset
, L_SET
) == -1) {
677 (void) fprintf(stderr
, "%s: lseek to %s: %s\n", KMEM
,
678 refstr
, strerror(errno
));
681 if (read(kmem
, (char *) ptr
, size
) == -1) {
685 (void) fprintf(stderr
, "%s: reading %s: %s\n", KMEM
,
686 refstr
, strerror(errno
));
693 /* comparison routine for qsort */
696 * proc_compare - comparison function for "qsort"
697 * Compares the resource consumption of two processes using five
698 * distinct keys. The keys (in descending order of importance) are:
699 * percent cpu, cpu ticks, state, resident set size, total virtual
700 * memory usage. The process states are ordered as follows (from least
701 * to most important): WAIT, zombie, sleep, stop, start, run. The
702 * array declaration below maps a process state index into a number
703 * that reflects this ordering.
706 static unsigned char sorted_state
[] =
710 1, /* ABANDONED (WAIT) */
717 proc_compare(pp1
, pp2
)
723 register struct proc
*p1
;
724 register struct proc
*p2
;
726 register pctcpu lresult
;
728 /* remove one level of indirection */
732 /* compare percent cpu (pctcpu) */
733 if ((lresult
= p2
->p_pctcpu
- p1
->p_pctcpu
) == 0)
735 /* use cpticks to break the tie */
736 if ((result
= p2
->p_cpticks
- p1
->p_cpticks
) == 0)
738 /* use process state to break the tie */
739 if ((result
= sorted_state
[p2
->p_stat
] -
740 sorted_state
[p1
->p_stat
]) == 0)
742 /* use priority to break the tie */
743 if ((result
= p2
->p_pri
- p1
->p_pri
) == 0)
745 /* use resident set size (rssize) to break the tie */
746 if ((result
= P_RSSIZE(p2
) - P_RSSIZE(p1
)) == 0)
748 /* use total memory to break the tie */
749 result
= PROCSIZE(p2
) - PROCSIZE(p1
);
757 result
= lresult
< 0 ? -1 : 1;
764 void (*signal(sig
, func
))()
768 struct sigvec osv
, sv
;
771 * XXX: we should block the signal we are playing with,
772 * in case we get interrupted in here.
774 if (sigvector(sig
, NULL
, &osv
) == -1)
777 sv
.sv_handler
= func
;
779 sv
.sv_flags
|= SV_BSDSIG
;
781 if (sigvector(sig
, &sv
, NULL
) == -1)
783 return osv
.sv_handler
;
786 int getpagesize() { return 1 << PGSHIFT
; }
788 int setpriority(a
, b
, c
) { errno
= ENOSYS
; return -1; }
791 * proc_owner(pid) - returns the uid that owns process "pid", or -1 if
792 * the process does not exist.
793 * It is EXTREMLY IMPORTANT that this function work correctly.
794 * If top runs setuid root (as in SVR4), then this function
795 * is the only thing that stands in the way of a serious
796 * security problem. It validates requests for the "kill"
797 * and "renice" commands.
806 register struct proc
**prefp
;
807 register struct proc
*pp
;
813 if ((pp
= *prefp
++)->p_pid
== (pid_t
)pid
)
815 return((int)pp
->p_uid
);