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 7 or earlier
39 * This is the machine-dependent module for Hpux 6.5 and 7.0.
40 * This makes top work on the following systems:
47 * AUTHOR: Christos Zoulas <christos@ee.cornell.edu>
51 #include <sys/types.h>
52 #include <sys/signal.h>
53 #include <sys/param.h>
70 #define VMUNIX "/hp-ux"
71 #define KMEM "/dev/kmem"
72 #define MEM "/dev/mem"
74 #define SWAP "/dev/swap"
77 /* get_process_info passes back a handle. This is what it looks like: */
81 struct proc
**next_proc
; /* points to next valid proc pointer */
82 int remaining
; /* number of pointers remaining */
85 /* declarations for load_avg */
88 /* define what weighted cpu is. */
89 #define weighted_cpu(pct, pp) ((pp)->p_time == 0 ? 0.0 : \
90 ((pct) / (1.0 - exp((pp)->p_time * logcpu))))
92 /* what we consider to be process size: */
93 #define PROCSIZE(pp) ((pp)->p_tsize + (pp)->p_dsize + (pp)->p_ssize)
95 /* definitions for indices in the nlist array */
103 # define X_USRPTMAP 6
114 static struct nlist nlst
[] = {
115 { "_avenrun" }, /* 0 */
117 { "_nproc" }, /* 2 */
119 { "_total" }, /* 4 */
120 { "_cp_time" }, /* 5 */
122 { "_Usrptmap" }, /* 6 */
123 { "_usrpt" }, /* 7 */
129 { "_npids" }, /* 8 */
130 { "_ubase" }, /* 9 */
136 * These definitions control the format of the per-process area
139 static char header
[] =
140 " PID X PRI NICE SIZE RES STATE TIME WCPU CPU COMMAND";
141 /* 0123456 -- field to fill in starts at header+6 */
142 #define UNAME_START 6
144 #define Proc_format \
145 "%5d %-8.8s %3d %4d %5s %5s %-5s %6s %5.2f%% %5.2f%% %s"
148 /* process state names for the "STATE" column of the display */
149 /* the extra nulls in the string "run" are for adding a slash and
150 the processor number when needed */
152 char *state_abbrev
[] =
154 "", "sleep", "WAIT", "run\0\0\0", "start", "zomb", "stop"
158 static int kmem
, mem
;
163 /* values that we stash away in _init and use in later routines */
165 static double logcpu
;
167 /* these are retrieved from the kernel in _init */
169 static unsigned long proc
;
172 static load_avg ccpu
;
175 /* these are offsets obtained via nlist and used in the get_ functions */
178 static unsigned long mpid_offset
;
181 static struct pte
*Usrptmap
, *usrpt
;
187 static unsigned long avenrun_offset
;
188 static unsigned long total_offset
;
189 static unsigned long cp_time_offset
;
191 /* these are for calculating cpu state percentages */
193 static long cp_time
[CPUSTATES
];
194 static long cp_old
[CPUSTATES
];
195 static long cp_diff
[CPUSTATES
];
197 /* these are for detailing the process states */
199 int process_states
[7];
200 char *procstatenames
[] = {
201 "", " sleeping, ", " ABANDONED, ", " running, ", " starting, ",
202 " zombie, ", " stopped, ",
206 /* these are for detailing the cpu states */
214 char *cpustatenames
[] = {
216 "usr", "nice", "sys", "idle", "", "", "", "intr", "ker",
219 "user", "nice", "system", "idle", "wait",
224 /* these are for detailing the memory statistics */
226 long memory_stats
[8];
227 char *memorynames
[] = {
228 "Real: ", "K active, ", "K total ", "Virtual: ", "K active, ",
229 "K total, ", "K free", NULL
232 /* these are for keeping track of the proc array */
236 static struct proc
*pbase
;
237 static struct proc
**pref
;
239 /* these are for getting the memory statistics */
241 static int pageshift
; /* log base 2 of the pagesize */
243 /* define pagetok in terms of pageshift */
245 #define pagetok(size) ((size) << pageshift)
247 /* useful externals */
249 extern char *sys_errlist
[];
254 machine_init(statics
)
256 struct statics
*statics
;
260 register int pagesize
;
262 if ((kmem
= open(KMEM
, O_RDONLY
)) == -1) {
266 if ((mem
= open(MEM
, O_RDONLY
)) == -1) {
272 if ((swap
= open(SWAP
, O_RDONLY
)) == -1) {
279 /* 800 names don't have leading underscores */
280 for (i
= 0; nlst
[i
].n_name
; nlst
[i
++].n_name
++)
284 /* get the list of symbols we want to access in the kernel */
285 (void) nlist(VMUNIX
, nlst
);
286 if (nlst
[0].n_type
== 0)
288 fprintf(stderr
, "top: nlist failed\n");
292 /* make sure they were all found */
293 if (i
> 0 && check_nlist(nlst
) > 0)
298 /* get the symbol values out of kmem */
299 (void) getkval(nlst
[X_PROC
].n_value
, (int *)(&proc
), sizeof(proc
),
300 nlst
[X_PROC
].n_name
);
301 (void) getkval(nlst
[X_NPROC
].n_value
, &nproc
, sizeof(nproc
),
302 nlst
[X_NPROC
].n_name
);
304 (void) getkval(nlst
[X_HZ
].n_value
, (int *)(&hz
), sizeof(hz
),
309 (void) getkval(nlst
[X_CCPU
].n_value
, (int *)(&ccpu
), sizeof(ccpu
),
310 nlst
[X_CCPU
].n_name
);
312 (void) getkval(nlst
[X_NPIDS
].n_value
, (int *)(&npids
), sizeof(npids
),
313 nlst
[X_NPIDS
].n_name
);
316 /* stash away certain offsets for later use */
319 ubase
= nlst
[X_UBASE
].n_value
;
325 Usrptmap
= (struct pte
*) nlst
[X_USRPTMAP
].n_value
;
326 usrpt
= (struct pte
*) nlst
[X_USRPT
].n_value
;
329 mpid_offset
= nlst
[X_MPID
].n_value
;
331 avenrun_offset
= nlst
[X_AVENRUN
].n_value
;
332 total_offset
= nlst
[X_TOTAL
].n_value
;
333 cp_time_offset
= nlst
[X_CP_TIME
].n_value
;
335 /* this is used in calculating WCPU -- calculate it ahead of time */
336 logcpu
= log(loaddouble(ccpu
));
338 /* allocate space for proc structure array and array of pointers */
339 bytes
= nproc
* sizeof(struct proc
);
340 pbase
= (struct proc
*)malloc(bytes
);
341 pref
= (struct proc
**)malloc(nproc
* sizeof(struct proc
*));
343 /* Just in case ... */
344 if (pbase
== (struct proc
*)NULL
|| pref
== (struct proc
**)NULL
)
346 fprintf(stderr
, "top: can't allocate sufficient memory\n");
350 /* get the page size with "getpagesize" and calculate pageshift from it */
351 pagesize
= getpagesize();
359 /* we only need the amount of log(2)1024 for our conversion */
360 pageshift
-= LOG1024
;
362 /* fill in the statics information */
363 statics
->procstate_names
= procstatenames
;
364 statics
->cpustate_names
= cpustatenames
;
365 statics
->memory_names
= memorynames
;
371 char *format_header(uname_field
)
373 register char *uname_field
;
378 ptr
= header
+ UNAME_START
;
379 while (*uname_field
!= '\0')
381 *ptr
++ = *uname_field
++;
390 struct system_info
*si
;
396 /* get the cp_time array */
397 (void) getkval(cp_time_offset
, (int *)cp_time
, sizeof(cp_time
),
400 /* get load average array */
401 (void) getkval(avenrun_offset
, (int *)avenrun
, sizeof(avenrun
),
405 /* get mpid -- process id of last process */
406 (void) getkval(mpid_offset
, &(si
->last_pid
), sizeof(si
->last_pid
),
412 /* convert load averages to doubles */
415 register double *infoloadp
;
416 register load_avg
*sysloadp
;
418 infoloadp
= si
->load_avg
;
420 for (i
= 0; i
< 3; i
++)
422 *infoloadp
++ = loaddouble(*sysloadp
++);
426 /* convert cp_time counts to percentages */
427 total
= percentages(CPUSTATES
, cpu_states
, cp_time
, cp_old
, cp_diff
);
429 /* sum memory statistics */
431 struct vmtotal total
;
433 /* get total -- systemwide main memory usage structure */
434 (void) getkval(total_offset
, (int *)(&total
), sizeof(total
),
436 /* convert memory stats to Kbytes */
437 memory_stats
[0] = -1;
438 memory_stats
[1] = pagetok(total
.t_arm
);
439 memory_stats
[2] = pagetok(total
.t_rm
);
440 memory_stats
[3] = -1;
441 memory_stats
[4] = pagetok(total
.t_avm
);
442 memory_stats
[5] = pagetok(total
.t_vm
);
443 memory_stats
[6] = pagetok(total
.t_free
);
446 /* set arrays and strings */
447 si
->cpustates
= cpu_states
;
448 si
->memory
= memory_stats
;
451 static struct handle handle
;
453 caddr_t
get_process_info(si
, sel
, i
)
455 struct system_info
*si
;
456 struct process_select
*sel
;
461 register int total_procs
;
462 register int active_procs
;
463 register struct proc
**prefp
;
464 register struct proc
*pp
;
466 /* these are copied out of sel for speed */
472 /* read all the proc structures in one fell swoop */
473 (void) getkval(proc
, (int *)pbase
, bytes
, "proc array");
475 /* get a pointer to the states summary array */
476 si
->procstates
= process_states
;
478 /* set up flags which define what we are going to select */
479 show_idle
= sel
->idle
;
480 show_system
= sel
->system
;
481 show_uid
= sel
->uid
!= -1;
482 show_command
= sel
->command
!= NULL
;
484 /* count up process states and get pointers to interesting procs */
487 memset((char *)process_states
, 0, sizeof(process_states
));
489 for (pp
= pbase
, i
= 0; i
< nproc
; pp
++, i
++)
492 * Place pointers to each valid proc structure in pref[].
493 * Process slots that are actually in use have a non-zero
494 * status field. Processes with SSYS set are system
495 * processes---these get ignored unless show_sysprocs is set.
497 if (pp
->p_stat
!= 0 &&
498 (show_system
|| ((pp
->p_flag
& SSYS
) == 0)))
501 process_states
[pp
->p_stat
]++;
502 if ((pp
->p_stat
!= SZOMB
) &&
503 (show_idle
|| (pp
->p_pctcpu
!= 0) || (pp
->p_stat
== SRUN
)) &&
504 (!show_uid
|| pp
->p_uid
== (uid_t
)sel
->uid
))
512 /* if requested, sort the "interesting" processes */
515 qsort((char *)pref
, active_procs
, sizeof(struct proc
*), proc_compare
);
518 /* remember active and total counts */
519 si
->p_total
= total_procs
;
520 si
->p_active
= pref_len
= active_procs
;
522 /* pass back a handle */
523 handle
.next_proc
= pref
;
524 handle
.remaining
= active_procs
;
525 return((caddr_t
)&handle
);
528 char fmt
[MAX_COLS
]; /* static area where result is built */
530 char *format_next_process(handle
, get_userid
)
533 char *(*get_userid
)();
536 register struct proc
*pp
;
537 register long cputime
;
543 /* find and remember the next proc structure */
544 hp
= (struct handle
*)handle
;
545 pp
= *(hp
->next_proc
++);
549 /* get the process's user struct and set cputime */
550 where
= getu(pp
, &u
);
553 (void) strcpy(u
.u_comm
, "<swapped>");
560 /* set u_comm for system processes */
561 if (u
.u_comm
[0] == '\0')
565 (void) strcpy(u
.u_comm
, "Swapper");
567 else if (pp
->p_pid
== 2)
569 (void) strcpy(u
.u_comm
, "Pager");
574 * Print swapped processes as <pname>
576 char buf
[sizeof(u
.u_comm
)];
577 (void) strncpy(buf
, u
.u_comm
, sizeof(u
.u_comm
));
579 (void) strncpy(&u
.u_comm
[1], buf
, sizeof(u
.u_comm
) - 2);
580 u
.u_comm
[sizeof(u
.u_comm
) - 2] = '\0';
581 (void) strncat(u
.u_comm
, ">", sizeof(u
.u_comm
) - 1);
582 u
.u_comm
[sizeof(u
.u_comm
) - 1] = '\0';
585 cputime
= u
.u_ru
.ru_utime
.tv_sec
+ u
.u_ru
.ru_stime
.tv_sec
;
588 /* calculate the base for cpu percentages */
589 pct
= pctdouble(pp
->p_pctcpu
);
591 /* format this entry */
595 (*get_userid
)(pp
->p_uid
),
598 format_k(pagetok(PROCSIZE(pp
))),
599 format_k(pagetok(pp
->p_rssize
)),
600 state_abbrev
[pp
->p_stat
],
601 format_time(cputime
),
602 100.0 * weighted_cpu(pct
, pp
),
604 printable(u
.u_comm
));
606 /* return the result */
611 * getu(p, u) - get the user structure for the process whose proc structure
612 * is pointed to by p. The user structure is put in the buffer pointed
613 * to by u. Return 0 if successful, -1 on failure (such as the process
614 * being swapped out).
617 #define USERSIZE sizeof(struct user)
621 register struct proc
*p
;
625 struct pte uptes
[UPAGES
];
626 register caddr_t upage
;
627 register struct pte
*pte
;
631 * Check if the process is currently loaded or swapped out. The way we
632 * get the u area is totally different for the two cases. For this
633 * application, we just don't bother if the process is swapped out.
635 if ((p
->p_flag
& SLOAD
) == 0) {
637 if (lseek(swap
, (long)dtob(p
->p_swaddr
), 0) == -1) {
638 perror("lseek(swap)");
641 if (read(swap
, (char *) u
, USERSIZE
) != USERSIZE
) {
642 perror("read(swap)");
652 * Process is currently in memory, we hope!
654 if (!getkval((unsigned long)p
->p_addr
, (int *)uptes
, sizeof(uptes
),
658 perror("getkval(uptes)");
660 /* we can't seem to get to it, so pretend it's swapped out */
665 for (nbytes
= USERSIZE
; nbytes
> 0; nbytes
-= NBPG
) {
666 (void) lseek(mem
, (long)(pte
++->pg_pfnum
* NBPG
), 0);
668 perror("lseek(mem)");
670 n
= MIN(nbytes
, NBPG
);
671 if (read(mem
, upage
, n
) != n
) {
675 /* we can't seem to get to it, so pretend it's swapped out */
684 * check_nlist(nlst) - checks the nlist to see if any symbols were not
685 * found. For every symbol that was not found, a one-line
686 * message is printed to stderr. The routine returns the
687 * number of symbols NOT found.
690 int check_nlist(nlst
)
692 register struct nlist
*nlst
;
697 /* check to see if we got ALL the symbols we requested */
698 /* this will write one line to stderr for every symbol not found */
701 while (nlst
->n_name
!= NULL
)
703 if (nlst
->n_type
== 0)
705 /* this one wasn't found */
706 fprintf(stderr
, "kernel: no symbol named `%s'\n", nlst
->n_name
);
717 * getkval(offset, ptr, size, refstr) - get a value out of the kernel.
718 * "offset" is the byte offset into the kernel for the desired value,
719 * "ptr" points to a buffer into which the value is retrieved,
720 * "size" is the size of the buffer (and the object to retrieve),
721 * "refstr" is a reference string used when printing error meessages,
722 * if "refstr" starts with a '!', then a failure on read will not
723 * be fatal (this may seem like a silly way to do things, but I
724 * really didn't want the overhead of another argument).
728 getkval(offset
, ptr
, size
, refstr
)
730 unsigned long offset
;
736 if (lseek(kmem
, (long)offset
, L_SET
) == -1) {
739 (void) fprintf(stderr
, "%s: lseek to %s: %s\n", KMEM
,
740 refstr
, strerror(errno
));
743 if (read(kmem
, (char *) ptr
, size
) == -1) {
747 (void) fprintf(stderr
, "%s: reading %s: %s\n", KMEM
,
748 refstr
, strerror(errno
));
755 /* comparison routine for qsort */
758 * proc_compare - comparison function for "qsort"
759 * Compares the resource consumption of two processes using five
760 * distinct keys. The keys (in descending order of importance) are:
761 * percent cpu, cpu ticks, state, resident set size, total virtual
762 * memory usage. The process states are ordered as follows (from least
763 * to most important): WAIT, zombie, sleep, stop, start, run. The
764 * array declaration below maps a process state index into a number
765 * that reflects this ordering.
768 static unsigned char sorted_state
[] =
772 1, /* ABANDONED (WAIT) */
779 proc_compare(pp1
, pp2
)
785 register struct proc
*p1
;
786 register struct proc
*p2
;
788 register pctcpu lresult
;
790 /* remove one level of indirection */
794 /* compare percent cpu (pctcpu) */
795 if ((lresult
= p2
->p_pctcpu
- p1
->p_pctcpu
) == 0)
797 /* use cpticks to break the tie */
798 if ((result
= p2
->p_cpticks
- p1
->p_cpticks
) == 0)
800 /* use process state to break the tie */
801 if ((result
= sorted_state
[p2
->p_stat
] -
802 sorted_state
[p1
->p_stat
]) == 0)
804 /* use priority to break the tie */
805 if ((result
= p2
->p_pri
- p1
->p_pri
) == 0)
807 /* use resident set size (rssize) to break the tie */
808 if ((result
= p2
->p_rssize
- p1
->p_rssize
) == 0)
810 /* use total memory to break the tie */
811 result
= PROCSIZE(p2
) - PROCSIZE(p1
);
819 result
= lresult
< 0 ? -1 : 1;
826 void (*signal(sig
, func
))()
830 struct sigvec osv
, sv
;
833 * XXX: we should block the signal we are playing with,
834 * in case we get interrupted in here.
836 if (sigvector(sig
, NULL
, &osv
) == -1)
839 sv
.sv_handler
= func
;
841 sv
.sv_flags
|= SV_BSDSIG
;
843 if (sigvector(sig
, &sv
, NULL
) == -1)
845 return osv
.sv_handler
;
848 int getpagesize() { return 1 << PGSHIFT
; }
850 int setpriority(a
, b
, c
) { errno
= ENOSYS
; return -1; }
853 * proc_owner(pid) - returns the uid that owns process "pid", or -1 if
854 * the process does not exist.
855 * It is EXTREMLY IMPORTANT that this function work correctly.
856 * If top runs setuid root (as in SVR4), then this function
857 * is the only thing that stands in the way of a serious
858 * security problem. It validates requests for the "kill"
859 * and "renice" commands.
868 register struct proc
**prefp
;
869 register struct proc
*pp
;
875 if ((pp
= *prefp
++)->p_pid
== (pid_t
)pid
)
877 return((int)pp
->p_uid
);