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 Sun running SunOS version 4.x
39 * This is the machine-dependent module for SunOS 4.x.
40 * This makes top work on the following systems:
43 * SunOS 4.0.2 (including 386i architecture)
47 * SunOS 4.1.2 (including MP architectures)
48 * SunOS 4.1.3 (including MP architectures)
49 * SunOS 4.1.3_U1 (including MP architectures)
50 * SunOS 4.1.4 (including MP architectures)
51 * Solbourne OS/MP PRIOR to 4.1A
55 * CFLAGS: -DHAVE_GETOPT -DORDER
57 * AUTHOR: William LeFebvre <wnl@groupsys.com>
58 * Solbourne support by David MacKenzie <djm@eng.umd.edu>
62 * #ifdef MULTIPROCESSOR means Sun MP.
63 * #ifdef solbourne is for Solbourne.
67 #include <sys/types.h>
68 #include <sys/signal.h>
70 /* make sure param.h gets loaded with KERNEL defined to get PZERO & NZERO */
72 #include <sys/param.h>
89 #include <sys/syscall.h>
92 /* Older versions of SunOS don't have a typedef for pid_t.
93 Hopefully this will catch all those cases without causing other problems.
95 #ifndef __sys_stdtypes_h
103 /* declarations for load_avg */
106 /* get_process_info passes back a handle. This is what it looks like: */
110 struct proc
**next_proc
; /* points to next valid proc pointer */
111 int remaining
; /* number of pointers remaining */
114 /* define what weighted cpu is. */
115 #define weighted_cpu(pct, pp) ((pp)->p_time == 0 ? 0.0 : \
116 ((pct) / (1.0 - exp((pp)->p_time * logcpu))))
118 /* what we consider to be process size: */
119 #define PROCSIZE(pp) ((pp)->p_tsize + (pp)->p_dsize + (pp)->p_ssize)
121 /* definitions for indices in the nlist array */
132 static struct nlist nlst
[] = {
134 { "avenrun" }, /* 0 */
140 { "cp_time" }, /* 6 */
142 { "epages" }, /* 8 */
144 { "_avenrun" }, /* 0 */
147 { "_nproc" }, /* 3 */
149 { "_total" }, /* 5 */
150 { "_cp_time" }, /* 6 */
151 { "_pages" }, /* 7 */
152 { "_epages" }, /* 8 */
153 #ifdef MULTIPROCESSOR
164 * These definitions control the format of the per-process area
167 static char header
[] =
168 " PID X PRI NICE SIZE RES STATE TIME WCPU CPU COMMAND";
169 /* 0123456 -- field to fill in starts at header+6 */
170 #define UNAME_START 6
172 #define Proc_format \
173 "%5d %-8.8s %3d %4d %5s %5s %-5s %-6s %5.2f%% %5.2f%% %s"
176 /* process state names for the "STATE" column of the display */
177 /* the extra nulls in the string "run" are for adding a slash and
178 the processor number when needed */
180 char *state_abbrev
[] =
182 "", "sleep", "WAIT", "run\0\0\0", "start", "zomb", "stop"
185 /* values that we stash away in _init and use in later routines */
187 static double logcpu
;
190 /* these are retrieved from the kernel in _init */
192 static unsigned long proc
;
194 static load_avg ccpu
;
195 static unsigned long pages
;
196 static unsigned long epages
;
199 /* these are offsets obtained via nlist and used in the get_ functions */
201 static unsigned long mpid_offset
;
202 static unsigned long avenrun_offset
;
203 static unsigned long total_offset
;
204 static unsigned long cp_time_offset
;
205 #ifdef MULTIPROCESSOR
206 static unsigned long xp_time_offset
;
209 /* these are for calculating cpu state percentages */
211 static long cp_time
[CPUSTATES
];
212 static long cp_old
[CPUSTATES
];
213 static long cp_diff
[CPUSTATES
];
214 #ifdef MULTIPROCESSOR
215 static long xp_time
[NCPU
][XPSTATES
];
216 /* for now we only accumulate spin time, but extending this to pick up
217 other stuff in xp_time is trivial. */
218 static long xp_old
[NCPU
];
221 /* these are for detailing the process states */
223 int process_states
[7];
224 char *procstatenames
[] = {
225 "", " sleeping, ", " ABANDONED, ", " running, ", " starting, ",
226 " zombie, ", " stopped, ",
230 /* these are for detailing the cpu states */
233 char *cpustatenames
[] = {
234 "user", "nice", "system", "idle",
235 #ifdef MULTIPROCESSOR
242 /* these are for detailing the memory statistics */
244 long memory_stats
[4];
245 char *memorynames
[] = {
246 "K available, ", "K in use, ", "K free, ", "K locked", NULL
249 /* these are names given to allowed sorting orders -- first is default */
251 {"cpu", "size", "res", NULL
};
253 /* forward definitions for comparison functions */
258 int (*proc_compares
[])() = {
265 /* these are for keeping track of the proc array */
269 static struct proc
*pbase
;
270 static struct proc
**pref
;
272 /* these are for getting the memory statistics */
274 static struct page
*physpage
;
277 static int pageshift
; /* log base 2 of the pagesize */
279 /* define pagetok in terms of pageshift */
281 #define pagetok(size) ((size) << pageshift)
283 /* useful externals */
285 extern char *sys_errlist
[];
290 machine_init(statics
)
292 struct statics
*statics
;
296 register int pagesize
;
298 /* initialize the kernel interface */
299 if ((kd
= kvm_open(NULL
, NULL
, NULL
, O_RDONLY
, "top")) == NULL
)
305 /* get the list of symbols we want to access in the kernel */
306 if ((i
= kvm_nlist(kd
, nlst
)) < 0)
308 fprintf(stderr
, "top: nlist failed\n");
312 #ifdef MULTIPROCESSOR
313 /* were ncpu and xp_time not found in the nlist? */
314 if (i
> 0 && nlst
[X_NCPU
].n_type
== 0 && nlst
[X_XP_TIME
].n_type
== 0)
316 /* we were compiled on an MP system but we are not running on one */
317 /* so we will pretend this didn't happen and set ncpu = 1 */
325 unsigned int status
, type
;
327 /* Get the number of CPUs on this system. */
328 syscall(SYS_getcpustatus
, &status
, &ncpu
, &type
);
332 /* make sure they were all found */
333 if (i
> 0 && check_nlist(nlst
) > 0)
338 /* get the symbol values out of kmem */
339 (void) getkval(nlst
[X_PROC
].n_value
, (int *)(&proc
), sizeof(proc
),
340 nlst
[X_PROC
].n_name
);
341 (void) getkval(nlst
[X_NPROC
].n_value
, &nproc
, sizeof(nproc
),
342 nlst
[X_NPROC
].n_name
);
343 (void) getkval(nlst
[X_CCPU
].n_value
, (int *)(&ccpu
), sizeof(ccpu
),
344 nlst
[X_CCPU
].n_name
);
345 (void) getkval(nlst
[X_PAGES
].n_value
, (int *)(&pages
), sizeof(pages
),
346 nlst
[X_PAGES
].n_name
);
347 (void) getkval(nlst
[X_EPAGES
].n_value
, (int *)(&epages
), sizeof(epages
),
348 nlst
[X_EPAGES
].n_name
);
349 #ifdef MULTIPROCESSOR
352 /* if ncpu > 0 then we are not really on an MP system */
353 (void) getkval(nlst
[X_NCPU
].n_value
, (int *)(&ncpu
), sizeof(ncpu
),
354 nlst
[X_NCPU
].n_name
);
358 /* stash away certain offsets for later use */
359 mpid_offset
= nlst
[X_MPID
].n_value
;
360 avenrun_offset
= nlst
[X_AVENRUN
].n_value
;
361 total_offset
= nlst
[X_TOTAL
].n_value
;
362 cp_time_offset
= nlst
[X_CP_TIME
].n_value
;
363 #ifdef MULTIPROCESSOR
364 xp_time_offset
= nlst
[X_XP_TIME
].n_value
;
367 /* this is used in calculating WCPU -- calculate it ahead of time */
368 logcpu
= log(loaddouble(ccpu
));
370 /* allocate space for proc structure array and array of pointers */
371 bytes
= nproc
* sizeof(struct proc
);
372 pbase
= (struct proc
*)malloc(bytes
);
373 pref
= (struct proc
**)malloc(nproc
* sizeof(struct proc
*));
375 /* Just in case ... */
376 if (pbase
== (struct proc
*)NULL
|| pref
== (struct proc
**)NULL
)
378 fprintf(stderr
, "top: can't allocate sufficient memory\n");
382 /* allocate a table to hold all the page structs */
383 bytesize
= epages
- pages
;
384 count
= bytesize
/ sizeof(struct page
);
385 physpage
= (struct page
*)malloc(epages
- pages
);
386 if (physpage
== NULL
)
388 fprintf(stderr
, "top: can't allocate sufficient memory\n");
392 /* get the page size with "getpagesize" and calculate pageshift from it */
393 pagesize
= getpagesize();
401 /* we only need the amount of log(2)1024 for our conversion */
402 pageshift
-= LOG1024
;
404 #if defined(MULTIPROCESSOR) || defined(solbourne)
405 /* add a slash to the "run" state abbreviation */
408 state_abbrev
[SRUN
][3] = '/';
412 /* fill in the statics information */
413 statics
->procstate_names
= procstatenames
;
414 statics
->cpustate_names
= cpustatenames
;
415 statics
->memory_names
= memorynames
;
416 statics
->order_names
= ordernames
;
422 char *format_header(uname_field
)
424 register char *uname_field
;
429 ptr
= header
+ UNAME_START
;
430 while (*uname_field
!= '\0')
432 *ptr
++ = *uname_field
++;
441 struct system_info
*si
;
446 #ifdef MULTIPROCESSOR
450 /* get the cp_time array */
451 (void) getkval(cp_time_offset
, (int *)cp_time
, sizeof(cp_time
),
454 #ifdef MULTIPROCESSOR
455 /* get the xp_time array as well */
458 (void) getkval(xp_time_offset
, (int *)xp_time
, sizeof(xp_time
),
463 /* get load average array */
464 (void) getkval(avenrun_offset
, (int *)avenrun
, sizeof(avenrun
),
467 /* get mpid -- process id of last process */
468 (void) getkval(mpid_offset
, &(si
->last_pid
), sizeof(si
->last_pid
),
471 /* get the array of physpage descriptors */
472 (void) getkval(pages
, (int *)physpage
, bytesize
, "array _page");
474 /* convert load averages to doubles */
477 register double *infoloadp
;
478 register load_avg
*sysloadp
;
480 infoloadp
= si
->load_avg
;
482 for (i
= 0; i
< 3; i
++)
484 *infoloadp
++ = loaddouble(*sysloadp
++);
488 /* convert cp_time counts to percentages */
489 total
= percentages(CPUSTATES
, cpu_states
, cp_time
, cp_old
, cp_diff
);
491 #ifdef MULTIPROCESSOR
492 /* calculate spin time from all processors */
498 register long change
;
500 /* collect differences for each processor and add them */
502 for (i
= 0; i
< ncpu
; i
++)
504 c
= xp_time
[i
][XP_SPIN
];
505 change
= c
- xp_old
[i
];
508 /* counter wrapped */
509 change
= (long)((unsigned long)c
-
510 (unsigned long)xp_old
[i
]);
517 * NOTE: I am assuming that the ticks found in xp_time are
518 * already included in the ticks accumulated in cp_time. To
519 * get an accurate reflection, therefore, we have to subtract
520 * the spin time from the system time and recompute those two
523 half_total
= total
/ 2l;
524 cp_diff
[CP_SYS
] -= sum
;
525 cpu_states
[CP_SYS
] = (int)((cp_diff
[CP_SYS
] * 1000 + half_total
) /
527 cpu_states
[XCP_SPIN
] = (int)((sum
* 1000 + half_total
) / total
);
531 /* sum memory statistics */
533 register struct page
*pp
;
539 /* bop thru the array counting page types */
541 inuse
= free
= locked
= 0;
542 for (cnt
= count
; --cnt
>= 0; pp
++)
546 else if (pp
->p_lock
|| pp
->p_keepcnt
> 0)
552 /* convert memory stats to Kbytes */
553 memory_stats
[0] = pagetok(inuse
+ free
);
554 memory_stats
[1] = pagetok(inuse
);
555 memory_stats
[2] = pagetok(free
);
556 memory_stats
[3] = pagetok(locked
);
559 /* set arrays and strings */
560 si
->cpustates
= cpu_states
;
561 si
->memory
= memory_stats
;
564 static struct handle handle
;
566 caddr_t
get_process_info(si
, sel
, compare_index
)
568 struct system_info
*si
;
569 struct process_select
*sel
;
574 register int total_procs
;
575 register int active_procs
;
576 register struct proc
**prefp
;
577 register struct proc
*pp
;
579 /* these are copied out of sel for speed */
585 /* read all the proc structures in one fell swoop */
586 (void) getkval(proc
, (int *)pbase
, bytes
, "proc array");
588 /* get a pointer to the states summary array */
589 si
->procstates
= process_states
;
591 /* set up flags which define what we are going to select */
592 show_idle
= sel
->idle
;
593 show_system
= sel
->system
;
594 show_uid
= sel
->uid
!= -1;
595 show_command
= sel
->command
!= NULL
;
597 /* count up process states and get pointers to interesting procs */
600 bzero((char *)process_states
, sizeof(process_states
));
602 for (pp
= pbase
, i
= 0; i
< nproc
; pp
++, i
++)
605 * Place pointers to each valid proc structure in pref[].
606 * Process slots that are actually in use have a non-zero
607 * status field. Processes with SSYS set are system
608 * processes---these get ignored unless show_sysprocs is set.
610 if (pp
->p_stat
!= 0 &&
611 (show_system
|| ((pp
->p_flag
& SSYS
) == 0)))
614 process_states
[pp
->p_stat
]++;
615 if ((pp
->p_stat
!= SZOMB
) &&
616 (show_idle
|| (pp
->p_pctcpu
!= 0) || (pp
->p_stat
== SRUN
)) &&
617 (!show_uid
|| pp
->p_uid
== (uid_t
)sel
->uid
))
625 /* if requested, sort the "interesting" processes */
626 qsort((char *)pref
, active_procs
, sizeof(struct proc
*),
627 proc_compares
[compare_index
]);
629 /* remember active and total counts */
630 si
->p_total
= total_procs
;
631 si
->p_active
= pref_len
= active_procs
;
633 /* pass back a handle */
634 handle
.next_proc
= pref
;
635 handle
.remaining
= active_procs
;
636 return((caddr_t
)&handle
);
639 char fmt
[MAX_COLS
]; /* static area where result is built */
641 char *format_next_process(handle
, get_userid
)
644 char *(*get_userid
)();
647 register struct proc
*pp
;
648 register long cputime
;
653 /* find and remember the next proc structure */
654 hp
= (struct handle
*)handle
;
655 pp
= *(hp
->next_proc
++);
658 /* get the process's user struct and set cputime */
659 if (getu(pp
, &u
) == -1)
661 (void) strcpy(u
.u_comm
, "<swapped>");
666 /* set u_comm for system processes */
667 if (u
.u_comm
[0] == '\0')
671 (void) strcpy(u
.u_comm
, "Swapper");
673 else if (pp
->p_pid
== 2)
675 (void) strcpy(u
.u_comm
, "Pager");
679 cputime
= u
.u_ru
.ru_utime
.tv_sec
+ u
.u_ru
.ru_stime
.tv_sec
;
682 /* calculate the base for cpu percentages */
683 pct
= pctdouble(pp
->p_pctcpu
);
685 #ifdef MULTIPROCESSOR
687 * If there is more than one cpu then add the processor number to
688 * the "run/" string. Note that this will only show up if the
689 * process is in the run state. Also note: when they
690 * start making Suns with more than 9 processors this will break
691 * since the string will then be more than 5 characters.
695 state_abbrev
[SRUN
][4] = (pp
->p_cpuid
& 0xf) + '0';
701 state_abbrev
[SRUN
][4] = (pp
->p_lastcpu
) + '0';
705 /* format this entry */
709 (*get_userid
)(pp
->p_uid
),
712 format_k(pagetok(PROCSIZE(pp
))),
713 format_k(pagetok(pp
->p_rssize
)),
714 state_abbrev
[pp
->p_stat
],
715 format_time(cputime
),
716 100.0 * weighted_cpu(pct
, pp
),
718 printable(u
.u_comm
));
720 /* return the result */
725 * getu(p, u) - get the user structure for the process whose proc structure
726 * is pointed to by p. The user structure is put in the buffer pointed
727 * to by u. Return 0 if successful, -1 on failure (such as the process
728 * being swapped out).
733 register struct proc
*p
;
737 register struct user
*lu
;
739 lu
= kvm_getu(kd
, p
);
752 * check_nlist(nlst) - checks the nlist to see if any symbols were not
753 * found. For every symbol that was not found, a one-line
754 * message is printed to stderr. The routine returns the
755 * number of symbols NOT found.
758 int check_nlist(nlst
)
760 register struct nlist
*nlst
;
765 /* check to see if we got ALL the symbols we requested */
766 /* this will write one line to stderr for every symbol not found */
769 while (nlst
->n_name
!= NULL
)
772 if (nlst
->n_value
== 0)
774 if (nlst
->n_type
== 0)
777 /* this one wasn't found */
778 fprintf(stderr
, "kernel: no symbol named `%s'\n", nlst
->n_name
);
789 * getkval(offset, ptr, size, refstr) - get a value out of the kernel.
790 * "offset" is the byte offset into the kernel for the desired value,
791 * "ptr" points to a buffer into which the value is retrieved,
792 * "size" is the size of the buffer (and the object to retrieve),
793 * "refstr" is a reference string used when printing error meessages,
794 * if "refstr" starts with a '!', then a failure on read will not
795 * be fatal (this may seem like a silly way to do things, but I
796 * really didn't want the overhead of another argument).
800 getkval(offset
, ptr
, size
, refstr
)
802 unsigned long offset
;
808 if (kvm_read(kd
, offset
, ptr
, size
) != size
)
816 fprintf(stderr
, "top: kvm_read for %s: %s\n",
817 refstr
, sys_errlist
[errno
]);
825 /* comparison routines for qsort */
828 * There are currently four possible comparison routines. main selects
829 * one of these by indexing in to the array proc_compares.
831 * Possible keys are defined as macros below. Currently these keys are
832 * defined: percent cpu, cpu ticks, process state, resident set size,
833 * total virtual memory usage. The process states are ordered as follows
834 * (from least to most important): WAIT, zombie, sleep, stop, start, run.
835 * The array declaration below maps a process state index into a number
836 * that reflects this ordering.
839 /* First, the possible comparison keys. These are defined in such a way
840 that they can be merely listed in the source code to define the actual
844 #define ORDERKEY_PCTCPU if (lresult = p2->p_pctcpu - p1->p_pctcpu,\
845 (result = lresult > 0 ? 1 : lresult < 0 ? -1 : 0) == 0)
846 #define ORDERKEY_CPTICKS if ((result = p2->p_cpticks - p1->p_cpticks) == 0)
847 #define ORDERKEY_STATE if ((result = sorted_state[p2->p_stat] - \
848 sorted_state[p1->p_stat]) == 0)
849 #define ORDERKEY_PRIO if ((result = p2->p_pri - p1->p_pri) == 0)
850 #define ORDERKEY_RSSIZE if ((result = p2->p_rssize - p1->p_rssize) == 0)
851 #define ORDERKEY_MEM if ((result = PROCSIZE(p2) - PROCSIZE(p1)) == 0)
853 /* Now the array that maps process state to a weight */
855 static unsigned char sorted_state
[] =
859 1, /* ABANDONED (WAIT) */
866 /* compare_cpu - the comparison function for sorting by cpu percentage */
868 compare_cpu(pp1
, pp2
)
874 register struct proc
*p1
;
875 register struct proc
*p2
;
877 register pctcpu lresult
;
879 /* remove one level of indirection */
894 /* compare_size - the comparison function for sorting by total memory usage */
896 compare_size(pp1
, pp2
)
902 register struct proc
*p1
;
903 register struct proc
*p2
;
905 register pctcpu lresult
;
907 /* remove one level of indirection */
922 /* compare_res - the comparison function for sorting by resident set size */
924 compare_res(pp1
, pp2
)
930 register struct proc
*p1
;
931 register struct proc
*p2
;
933 register pctcpu lresult
;
935 /* remove one level of indirection */
951 * proc_owner(pid) - returns the uid that owns process "pid", or -1 if
952 * the process does not exist.
953 * It is EXTREMLY IMPORTANT that this function work correctly.
954 * If top runs setuid root (as in SVR4), then this function
955 * is the only thing that stands in the way of a serious
956 * security problem. It validates requests for the "kill"
957 * and "renice" commands.
966 register struct proc
**prefp
;
967 register struct proc
*pp
;
973 if ((pp
= *prefp
++)->p_pid
== (pid_t
)pid
)
975 return((int)pp
->p_uid
);