Rename top(1) to mtop(1), import NetBSD top(1)
[minix3.git] / external / bsd / top / dist / machine / m_hpux8.c
blob83dc9c2bd021003947b4ca5caa1d8a5e8bd1f6c4
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 hp9000 running hpux version 8 (may work with 9)
38 * DESCRIPTION:
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
41 * following systems:
42 * hp9000s300
43 * hp9000s700
44 * hp9000s800
46 * LIBS:
48 * AUTHOR: Christos Zoulas <christos@ee.cornell.edu>
51 #include "config.h"
52 #include <sys/types.h>
53 #include <sys/signal.h>
54 #include <sys/param.h>
56 #include <stdio.h>
57 #include <nlist.h>
58 #include <math.h>
59 #include <sys/dir.h>
60 #include <sys/user.h>
61 #include <sys/proc.h>
62 #include <sys/dk.h>
63 #include <sys/vm.h>
64 #include <sys/file.h>
65 #include <sys/time.h>
66 #ifndef hpux
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
71 #else
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)
79 #endif
81 #include "top.h"
82 #include "machine.h"
83 #include "utils.h"
85 #define VMUNIX "/hp-ux"
86 #define KMEM "/dev/kmem"
87 #define MEM "/dev/mem"
88 #ifdef DOSWAP
89 #define SWAP "/dev/dmem"
90 #endif
92 /* get_process_info passes back a handle. This is what it looks like: */
94 struct handle
96 struct proc **next_proc; /* points to next valid proc pointer */
97 int remaining; /* number of pointers remaining */
100 /* declarations for load_avg */
101 #include "loadavg.h"
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 */
111 #define X_AVENRUN 0
112 #define X_CCPU 1
113 #define X_NPROC 2
114 #define X_PROC 3
115 #define X_TOTAL 4
116 #define X_CP_TIME 5
117 #define X_MPID 6
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.
128 #ifndef hp9000s300
129 #define X_HZ 7
130 #endif
133 static struct nlist nlst[] = {
134 { "_avenrun" }, /* 0 */
135 { "_ccpu" }, /* 1 */
136 { "_nproc" }, /* 2 */
137 { "_proc" }, /* 3 */
138 { "_total" }, /* 4 */
139 { "_cp_time" }, /* 5 */
140 { "_mpid" }, /* 6 */
141 #ifdef X_HZ
142 { "_hz" }, /* 7 */
143 #endif
144 { 0 }
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"
170 static int kmem;
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;
179 static int nproc;
180 static long hz;
181 static load_avg ccpu;
182 static int ncpu = 0;
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, ",
202 NULL
205 /* these are for detailing the cpu states */
207 int cpu_states[9];
208 char *cpustatenames[] = {
209 "usr", "nice", "sys", "idle", "", "", "", "intr", "ker",
210 NULL
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 */
223 static int bytes;
224 static int pref_len;
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 */
238 extern int errno;
239 extern char *sys_errlist[];
241 long lseek();
242 long time();
244 machine_init(statics)
246 struct statics *statics;
249 register int i = 0;
250 register int pagesize;
252 if ((kmem = open(KMEM, O_RDONLY)) == -1) {
253 perror(KMEM);
254 return(-1);
256 #ifdef hp9000s800
257 /* 800 names don't have leading underscores */
258 for (i = 0; nlst[i].n_name; nlst[i++].n_name++)
259 continue;
260 #endif
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");
267 return(-1);
270 /* make sure they were all found */
271 if (check_nlist(nlst) > 0)
273 return(-1);
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);
283 #ifdef X_HZ
284 (void) getkval(nlst[X_HZ].n_value, (int *)(&hz), sizeof(hz),
285 nlst[X_HZ].n_name);
286 #else
287 hz = HZ;
288 #endif
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");
309 return(-1);
312 /* get the page size with "getpagesize" and calculate pageshift from it */
313 pagesize = getpagesize();
314 pageshift = 0;
315 while (pagesize > 1)
317 pageshift++;
318 pagesize >>= 1;
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;
329 /* all done! */
330 return(0);
333 char *format_header(uname_field)
335 register char *uname_field;
338 register char *ptr;
340 ptr = header + UNAME_START;
341 while (*uname_field != '\0')
343 *ptr++ = *uname_field++;
346 return(header);
349 void
350 get_system_info(si)
352 struct system_info *si;
355 load_avg avenrun[3];
356 long total;
358 /* get the cp_time array */
359 (void) getkval(cp_time_offset, (int *)cp_time, sizeof(cp_time),
360 "_cp_time");
362 /* get load average array */
363 (void) getkval(avenrun_offset, (int *)avenrun, sizeof(avenrun),
364 "_avenrun");
366 /* get mpid -- process id of last process */
367 (void) getkval(mpid_offset, &(si->last_pid), sizeof(si->last_pid),
368 "_mpid");
370 /* convert load averages to doubles */
372 register int i;
373 register double *infoloadp;
374 register load_avg *sysloadp;
376 infoloadp = si->load_avg;
377 sysloadp = avenrun;
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),
393 "_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;
415 int i;
418 register int i;
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 */
425 int show_idle;
426 int show_system;
427 int show_uid;
428 int show_command;
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;
435 else
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 */
452 total_procs = 0;
453 active_procs = 0;
454 memset((char *)process_states, 0, sizeof(process_states));
455 prefp = pref;
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)))
467 total_procs++;
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))
473 *prefp++ = pp;
474 active_procs++;
479 /* if requested, sort the "interesting" processes */
480 if (compare != NULL)
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)
499 caddr_t handle;
500 char *(*get_userid)();
503 register struct proc *pp;
504 register long cputime;
505 register double pct;
506 int where;
507 struct user u;
508 struct handle *hp;
510 /* find and remember the next proc structure */
511 hp = (struct handle *)handle;
512 pp = *(hp->next_proc++);
513 hp->remaining--;
516 /* get the process's user struct and set cputime */
517 where = getu(pp, &u);
518 if (where == -1)
520 (void) strcpy(u.u_comm, "<swapped>");
521 cputime = 0;
523 else
527 /* set u_comm for system processes */
528 if (u.u_comm[0] == '\0')
530 if (pp->p_pid == 0)
532 (void) strcpy(u.u_comm, "Swapper");
534 else if (pp->p_pid == 2)
536 (void) strcpy(u.u_comm, "Pager");
539 if (where == 1) {
541 * Print swapped processes as <pname>
543 char buf[sizeof(u.u_comm)];
544 (void) strncpy(buf, u.u_comm, sizeof(u.u_comm));
545 u.u_comm[0] = '<';
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 */
559 sprintf(fmt,
560 Proc_format,
561 pp->p_pid,
562 (*get_userid)(pp->p_uid),
563 pp->p_pri - PZERO,
564 pp->p_nice - NZERO,
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),
570 100.0 * pct,
571 printable(u.u_comm));
573 /* return the result */
574 return(fmt);
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).
585 getu(p, u)
587 register struct proc *p;
588 struct user *u;
591 struct pst_status *ps;
592 char *s, *c;
593 int i;
595 if ((ps = (struct pst_status *) p->p_upreg) == NULL)
596 return -1;
598 memset(u, 0, sizeof(struct user));
599 c = ps->pst_cmd;
600 ps->pst_cmd[PST_CLEN - 1] = '\0'; /* paranoia */
601 s = strtok(ps->pst_cmd, "\t \n");
603 if (c = strrchr(s, '/'))
604 c++;
605 else
606 c = s;
607 if (*c == '-')
608 c++;
609 i = 0;
610 for (; i < MAXCOMLEN; i++) {
611 if (*c == '\0' || *c == ' ' || *c == '/')
612 break;
613 u->u_comm[i] = *c++;
615 #ifndef DOSWAP
616 return ((p->p_flag & SLOAD) == 0 ? 1 : 0);
617 #endif
618 return(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;
633 register int i;
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 */
638 i = 0;
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);
645 i = 1;
647 nlst++;
650 return(i);
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;
669 int *ptr;
670 int size;
671 char *refstr;
674 if (lseek(kmem, (long)offset, L_SET) == -1) {
675 if (*refstr == '!')
676 refstr++;
677 (void) fprintf(stderr, "%s: lseek to %s: %s\n", KMEM,
678 refstr, strerror(errno));
679 quit(23);
681 if (read(kmem, (char *) ptr, size) == -1) {
682 if (*refstr == '!')
683 return(0);
684 else {
685 (void) fprintf(stderr, "%s: reading %s: %s\n", KMEM,
686 refstr, strerror(errno));
687 quit(23);
690 return(1);
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[] =
708 0, /* not used */
709 3, /* sleep */
710 1, /* ABANDONED (WAIT) */
711 6, /* run */
712 5, /* start */
713 2, /* zombie */
714 4 /* stop */
717 proc_compare(pp1, pp2)
719 struct proc **pp1;
720 struct proc **pp2;
723 register struct proc *p1;
724 register struct proc *p2;
725 register int result;
726 register pctcpu lresult;
728 /* remove one level of indirection */
729 p1 = *pp1;
730 p2 = *pp2;
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);
755 else
757 result = lresult < 0 ? -1 : 1;
760 return(result);
764 void (*signal(sig, func))()
765 int sig;
766 void (*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)
775 return BADSIG;
776 sv = osv;
777 sv.sv_handler = func;
778 #ifdef SV_BSDSIG
779 sv.sv_flags |= SV_BSDSIG;
780 #endif
781 if (sigvector(sig, &sv, NULL) == -1)
782 return BADSIG;
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.
800 int proc_owner(pid)
802 int pid;
805 register int cnt;
806 register struct proc **prefp;
807 register struct proc *pp;
809 prefp = pref;
810 cnt = pref_len;
811 while (--cnt >= 0)
813 if ((pp = *prefp++)->p_pid == (pid_t)pid)
815 return((int)pp->p_uid);
818 return(-1);