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: SCO UNIX OpenServer5
39 * This is the machine-dependent module for SCO OpenServer5.
40 * Originally written for BSD4.3 system by Christos Zoulas.
41 * Modified to m_sco.c (3.2v4.2) by Gregory Shilin <shilin@onyx.co.il>
42 * Modified to m_sco5.c (3.2v5.*) by Mike Hopkirk <hops@sco.com>
46 * CFLAGS: -DHAVE_GETOPT -DORDER
48 * AUTHOR: Mike Hopkirk (hops@sco.com)
49 * hops 10-Jul-98 - added sort fields
50 * 17-Jul-98 - add philiph's chopped cmd string support
51 * (define NO_COMMAND_ARGS to enable )
52 * 09-Dec-98 - provide RSS calculation
53 * 15-Mar-2000 - Fix broken lines and cleanup sysinfo access w macros
56 #include <sys/types.h>
57 #include <sys/param.h>
69 #include <sys/region.h>
72 #include <sys/sysinfo.h>
73 #include <sys/systm.h>
74 #include <sys/sysmacros.h>
76 #include <sys/sysi86.h>
84 typedef unsigned long ulong;
85 typedef unsigned int uint;
86 typedef unsigned short ushort;
88 typedef unsigned char uchar
;
90 #define VMUNIX "/unix"
91 #define KMEM "/dev/kmem"
92 #define MEM "/dev/mem"
94 #define SI_ACTIVE(p) p->p_active
95 #define SI_TOTAL(p) p->p_total
97 /* get_process_info passes back a handle. This is what it looks like: */
99 struct proc
**next_proc
; /* points to next valid proc pointer */
100 int remaining
; /* number of pointers remaining */
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 #define bytetok(bytes) ((bytes) >> 10)
109 /* what we consider to be process size: */
110 #define PROCSIZE(up) bytetok(ctob((up)->u_tsize + (up)->u_dsize+(up)->u_ssize))
112 /* definitions for indices in the nlist array */
113 #define X_V 0 /* System configuration information */
114 #define X_PROC 1 /* process tables */
115 #define X_FREEMEM 2 /* current free memory */
116 #define X_AVAILRMEM 3 /* available resident (not swappable) mem in pages
118 #define X_AVAILSMEM 4 /* available swappable memory in pages */
119 #define X_MAXMEM 5 /* maximum available free memory in clicks */
120 #define X_PHYSMEM 6 /* physical memory in clicks */
121 #define X_NSWAP 7 /* size of swap space in blocks */
122 #define X_HZ 8 /* ticks/second of the clock */
123 #define X_MPID 9 /* last process id */
124 #define X_SYSINFO 10 /* system information (cpu states) */
127 static struct nlist nlst
[] = {
130 { "freemem" }, /* 2 */
131 { "availrmem" }, /* 3 */
132 { "availsmem" }, /* 4 */
133 { "maxmem" }, /* 5 */
134 { "physmem" }, /* 6 */
138 { "sysinfo" }, /* 10 */
139 { "cur_cpu" }, /* 11 */
144 * These definitions control the format of the per-process area
147 static char header
[] =
148 " PID X PRI NICE SIZE RES STATE TIME COMMAND";
149 /* 0123456 -- field to fill in starts at header+6 */
150 #define UNAME_START 6
152 #define Proc_format \
153 "%5d %-8.8s %3d %4d %5s %5dK %-5s %6s %.28s"
155 static int kmem
, mem
;
157 static double logcpu
;
159 /* these are retrieved from the kernel in _init */
163 static load_avg cur_cpu
;
165 /* these are for detailing the process states */
166 int process_states
[8];
167 char *procstatenames
[] = {
168 "", " sleeping, ", " running, ", " zombie, ", " stopped, ",
169 " created, ", " onproc, ", " xswapped, ",
173 /* process state names for the "STATE" column of the display */
174 char *state_abbrev
[] = {
175 "", "sleep", "run", "zomb", "stop", "create", "onpr", "swap"
178 /* these are for calculating cpu state percentages */
179 #define CPUSTATES 5 /* definition from struct sysinfo */
180 static time_t cp_time
[CPUSTATES
];
181 static time_t cp_old
[CPUSTATES
];
182 static time_t cp_diff
[CPUSTATES
];
184 /* these are for detailing the cpu states */
185 int cpu_states
[CPUSTATES
];
186 char *cpustatenames
[] = {
187 "idle", "user", "system", "wait", "sxbrk",
191 /* these are for detailing the memory statistics */
192 unsigned long memory_stats
[6];
193 char *memorynames
[] = {
194 "K phys, ", "K max, ", "K free, ", "K lck, ", "K unlck, ",
198 /* these are for keeping track of the proc array */
201 static struct proc
*pbase
;
202 static struct proc
**pref
;
204 /* forward definitions for comparison functions */
210 int (*proc_compares
[])() = {
211 proc_compare
, /* state, pri, time, size */
212 compare_cpu
, /* cpu, time, state, pri, size */
213 compare_size
, /* size, cpu, time, state pri */
214 compare_time
, /* time, cpu, state, pri, size */
215 /* compare_res, /* res, cpu, time, state pri */
218 /* these are names given to allowed sorting orders -- first is default */
219 char *ordernames
[]={"state", "cpu", "size", "time", NULL
}; /*hops*/
221 /* useful externals */
223 extern char *sys_errlist
[];
229 machine_init(struct statics
*statics
)
234 if ((kmem
= open(KMEM
, O_RDONLY
)) == -1) {
238 if ((mem
= open(MEM
, O_RDONLY
)) == -1) {
243 /* get the list of symbols we want to access in the kernel */
244 if (nlist(VMUNIX
, nlst
) == -1) {
245 fprintf(stderr
, "top: nlist failed\n");
248 /* make sure they were all found */
250 if (check_nlist(nlst) > 0)
254 proca
= nlst
[X_PROC
].n_value
;
256 /* get the symbol values out of kmem */
257 (void) getkval(nlst
[X_CUR_CPU
].n_value
, (int *)(&cur_cpu
), sizeof(cur_cpu
),
258 nlst
[X_CUR_CPU
].n_name
);
259 (void) getkval(nlst
[X_HZ
].n_value
, (int *)(&Hz
), sizeof(Hz
),
261 (void) getkval(nlst
[X_V
].n_value
, (int *)(&v
), sizeof(v
),
264 /* this is used in calculating WCPU -- calculate it ahead of time */
265 logcpu
= log(fabs(loaddouble(cur_cpu
)));
267 /* allocate space for proc structure array and array of pointers */
268 bytes
= v
.v_proc
* sizeof(struct proc
);
269 pbase
= (struct proc
*)malloc(bytes
);
270 pref
= (struct proc
**)malloc(v
.v_proc
* sizeof(struct proc
*));
271 if (pbase
== (struct proc
*)NULL
|| pref
== (struct proc
**)NULL
) {
272 fprintf(stderr
, "top: cannot allocate sufficient memory\n");
276 /* fill in the statics information */
277 statics
->procstate_names
= procstatenames
;
278 statics
->cpustate_names
= cpustatenames
;
279 statics
->memory_names
= memorynames
;
280 statics
->order_names
= ordernames
; /* hops */
286 format_header(register char *uname_field
)
291 ptr
= header
+ UNAME_START
;
292 while (*uname_field
!= '\0')
294 *ptr
++ = *uname_field
++;
301 /* philiph - get run ave fm /dev/table info */
303 tab_avenrun(double runave
[])
305 FILE *fp
= fopen("/dev/table/avenrun", "r");
317 if (fread(rawave
, sizeof(short), 3, fp
) !=3 )
327 runave
[i
] = (double) (rawave
[i
] / 256.0);
336 get_pregion(void *ptr
)
338 static struct pregion preg
;
339 long addr
= (long)ptr
;
341 (void) getkval(addr
, (struct pregion
*)(&preg
),
342 sizeof(struct pregion
), "pregion" );
347 get_region(void *ptr
)
349 static struct region reg
;
350 long addr
= (long)ptr
;
352 (void) getkval( addr
, (struct region
*)(®
),
353 sizeof(struct region
), "region" );
357 static unsigned char shareable
[RT_VM86
+ 1]; /* 1 if shareable */
360 * sum private referenced pages,
361 * treat shared pages depending on value of TREAT_SHARABLE_PAGES macro
362 * undefined : ignore (don't account for - default)
363 * 1: divide among # of references
364 * 2: accumulate as if private
366 /* #define TREAT_SHAREABLE_PAGES 1 */
368 proc_residentsize(struct proc
*pp
)
376 /* init shareable region array */
377 if (shareable
[RT_STEXT
] == 0 )
378 shareable
[RT_STEXT
] = shareable
[RT_SHMEM
] = shareable
[RT_MAPFILE
] = 1
386 for( ; prp
&& (prp
= get_pregion((void *)(prp
))) &&
387 prp
->p_reg
&& (rp
= get_region((void*)(prp
->p_reg
)));
390 if (shareable
[rp
->r_type
] ) /* account for shared pgs separately
393 stot
+= (rp
->r_nvalid
/ rp
->r_refcnt
);
394 s1tot
+= rp
->r_nvalid
;
397 rtot
+= rp
->r_nvalid
;
400 #if defined(TREAT_SHAREABLE_PAGES) && TREAT_SHAREABLE_PAGES == 1
401 rtot
+= stot
; /* accumulate and spread over users */
404 #if defined(TREAT_SHAREABLE_PAGES) && TREAT_SHAREABLE_PAGES == 1
405 rtot
+= s1tot
; /* accumulate as if private */
408 return rtot
* NBPP
/1024; ;
412 get_system_info(struct system_info
*si
)
417 /* get process id of the last process */
418 (void) getkval(nlst
[X_MPID
].n_value
, &(si
->last_pid
),
419 sizeof(si
->last_pid
),
420 nlst
[X_MPID
].n_name
);
421 /* get the cp_time array */
422 (void) getkval(nlst
[X_SYSINFO
].n_value
, (int *)cp_time
, sizeof(cp_time
),
423 nlst
[X_SYSINFO
].n_name
);
425 /* convert cp_time counts to persentages */
426 total
= percentages(CPUSTATES
, cpu_states
, cp_time
, cp_old
, cp_diff
);
428 /* sum memory statistics */
429 (void) getkval(nlst
[X_PHYSMEM
].n_value
, &memory_stats
[0],
430 sizeof(memory_stats
[0]), nlst
[X_PHYSMEM
].n_name
);
431 (void) getkval(nlst
[X_MAXMEM
].n_value
, &memory_stats
[1],
432 sizeof(memory_stats
[1]), nlst
[X_MAXMEM
].n_name
);
433 (void) getkval(nlst
[X_FREEMEM
].n_value
, &memory_stats
[2],
434 sizeof(memory_stats
[2]), nlst
[X_FREEMEM
].n_name
);
435 (void) getkval(nlst
[X_AVAILRMEM
].n_value
, &memory_stats
[3],
436 sizeof(memory_stats
[3]), nlst
[X_AVAILRMEM
].n_name
);
437 (void) getkval(nlst
[X_AVAILSMEM
].n_value
, &memory_stats
[4],
438 sizeof(memory_stats
[4]), nlst
[X_AVAILSMEM
].n_name
);
439 (void) getkval(nlst
[X_NSWAP
].n_value
, &memory_stats
[5],
440 sizeof(memory_stats
[5]), nlst
[X_NSWAP
].n_name
);
441 memory_stats
[0] = bytetok(ctob(memory_stats
[0])); /* clicks -> bytes
443 memory_stats
[1] = bytetok(ctob(memory_stats
[1])); /* clicks -> bytes
445 memory_stats
[2] = bytetok(ctob(memory_stats
[2])); /* clicks -> bytes
447 memory_stats
[3] = bytetok(memory_stats
[3] * NBPP
); /* # bytes per page
449 memory_stats
[4] = bytetok(memory_stats
[4] * NBPP
); /* # bytes per page
451 memory_stats
[5] = bytetok(memory_stats
[5] * NBPSCTR
);/* # bytes per sector
454 /* set arrays and strings */
455 /* Note: we keep memory_stats as an unsigned long to avoid sign
456 extension problems when shifting in bytetok. But the module
457 interface requires an array of signed longs. So we just cast
458 the pointer here and hope for the best. --wnl
460 si
->cpustates
= cpu_states
;
461 si
->memory
= (long *)memory_stats
;
463 tab_avenrun(si
->load_avg
); /* philiph */
466 static struct handle handle
;
469 get_process_info(struct system_info
*si
,
470 struct process_select
*sel
,
475 register int total_procs
;
476 register int active_procs
;
477 register struct proc
**prefp
;
478 register struct proc
*pp
;
480 /* set up flags of what we are going to select */
481 /* these are copied out of sel for simplicity */
482 int show_idle
= sel
->idle
;
483 int show_system
= sel
->system
;
484 int show_uid
= sel
->uid
!= -1;
485 int show_command
= sel
->command
!= NULL
;
487 /* read all the proc structures in one fell swoop */
488 (void) getkval(proca
, (int *)pbase
, bytes
, "proc array");
490 /* get a pointer to the states summary array */
491 si
->procstates
= process_states
;
493 /* count up process states and get pointers to interesting procs */
494 total_procs
= active_procs
= 0;
495 memset((char *)process_states
, 0, sizeof(process_states
));
497 for (pp
= pbase
, i
= 0; i
< v
.v_proc
; pp
++, i
++) {
499 * Place pointers to each valid proc structure in pref[].
500 * Process slots that are actually in use have a non-zero
501 * status field. Processes with SSYS set are system processes --
502 * these are ignored unless show_system is set.
504 if (pp
->p_stat
&& (show_system
|| ((pp
->p_flag
& SSYS
) == 0))) {
506 process_states
[pp
->p_stat
]++;
507 if ((pp
->p_stat
!= SZOMB
) &&
508 (show_idle
|| (pp
->p_stat
== SRUN
) || (pp
->p_stat
== SONPROC
)) &&
509 (!show_uid
|| pp
->p_uid
== (ushort
)sel
->uid
)) {
516 /* if requested, sort the "interesting" processes */
517 qsort((char *)pref
, active_procs
, sizeof(struct proc
*), proc_compares
[idx
]);
519 /* remember active and total counts */
520 SI_TOTAL(si
) = total_procs
;
521 SI_ACTIVE(si
) = pref_len
= active_procs
;
523 /* pass back a handle */
524 handle
.next_proc
= pref
;
525 handle
.remaining
= active_procs
;
526 return((caddr_t
)&handle
);
529 char fmt
[128]; /* static area where result is built */
532 format_next_process(caddr_t handle
, char *(*get_userid
)())
535 register struct proc
*pp
;
536 register time_t cputime
;
545 /* find and remember the next proc structure */
546 hp
= (struct handle
*)handle
;
547 pp
= *(hp
->next_proc
++);
550 /* get the process's user struct and set cputime */
551 if ((where
= sysi86(RDUBLK
, pp
->p_pid
, &u
, sizeof(struct user
))) != -1)
552 where
= (pp
->p_flag
& SLOAD
) ? 0 : 1;
554 strcpy(command
, "<swapped>");
557 /* set u_comm for system processes */
558 if (u
.u_comm
[0] == '\0') {
560 strcpy(command
, "Swapper");
561 else if (pp
->p_pid
== 2)
562 strcpy(command
, "Pager");
563 else if (pp
->p_pid
== 3)
564 strcpy(command
, "Sync'er");
565 } else if (where
== 1) {
566 /* print swapped processes as <pname> */
569 u
.u_psargs
[28 - 3] = '\0';
570 strcpy(command
, "<");
571 strcat(command
, strtok(u
.u_psargs
, " "));
572 strcat(command
, ">");
573 while (s1
= (char *)strtok(NULL
, " "))
576 sprintf(command
, "%s", u
.u_psargs
);
578 cputime
= u
.u_utime
+ u
.u_stime
;
579 /* cputime = pp->p_utime + pp->p_stime; */
581 /* calculate the base for cpu percentages */
582 pct
= pctdouble(pp
->p_cpu
);
585 * psargs gives the absolute path of the process... strip it to only the
586 * command - [Changes by D. Currie & M. Muldner Aitt NS Canada]
588 process
= printable(command
);
592 process2
= strrchr(process
,'/');
600 /* format this entry */
604 (*get_userid
)(pp
->p_uid
),
607 format_k(PROCSIZE(&u
)), /* same as pp->p_size * 4 */
608 proc_residentsize(pp
),
609 state_abbrev
[pp
->p_stat
],
610 format_time(cputime
/ Hz
),
611 printable(process
) );
617 * Checks the nlist to see if any symbols were not found.
618 * For every symbol that was not found, a one-line message
619 * is printed to stderr. The routine returns the number of
620 * symbols NOT founded.
623 int check_nlist(register struct nlist
*nlst
)
628 while (nlst
->n_name
) {
629 if (nlst
->n_type
== 0) {
630 fprintf(stderr
, "kernel: no symbol named `%s'\n", nlst
->n_name
);
639 * getkval(offset, ptr, size, refstr) - get a value out of the kernel.
640 * "offset" is the byte offset into the kernel for the desired value,
641 * "ptr" points to a buffer into which the value is retrieved,
642 * "size" is the size of the buffer (and the object to retrieve),
643 * "refstr" is a reference string used when printing error meessages,
644 * if "refstr" starts with a '!', then a failure on read will not
645 * be fatal (this may seem like a silly way to do things, but I
646 * really didn't want the overhead of another argument).
651 getkval(unsigned long offset
, int *ptr
, int size
, char *refstr
)
654 if (lseek(kmem
, (long)offset
, SEEK_SET
) == -1) {
657 fprintf(stderr
, "%s: lseek to %s: %s\n", KMEM
,
658 refstr
, errmsg(errno
));
661 if (read(kmem
, (char *)ptr
, size
) == -1) {
664 fprintf(stderr
, "%s: reading %s: %s\n", KMEM
,
665 refstr
, errmsg(errno
));
671 /* comparison routine for qsort */
672 /* NOTE: this is specific to the BSD proc structure, but it should
673 give you a good place to start. */
676 * proc_compare - comparison function for "qsort"
677 * Compares the resource consumption of two processes using five
678 * distinct keys. The keys (in descending order of importance) are:
679 * percent cpu, cpu ticks, state, resident set size, total virtual
680 * memory usage. The process states are ordered as follows (from least
681 * to most important): WAIT, zombie, sleep, stop, start, run. The
682 * array declaration below maps a process state index into a number
683 * that reflects this ordering.
686 static unsigned char sorted_state
[] =
699 proc_compare(struct proc
**pp1
, struct proc
**pp2
)
702 register struct proc
*p1
;
703 register struct proc
*p2
;
705 register ulong lresult
;
707 /* remove one level of indirection */
711 /* use process state to break the tie */
712 if ((result
= sorted_state
[p2
->p_stat
] -
713 sorted_state
[p1
->p_stat
]) == 0)
715 /* use priority to break the tie */
716 if ((result
= p2
->p_pri
- p1
->p_pri
) == 0)
718 /* use time to break the tie */
719 if ((result
= (p2
->p_utime
+ p2
->p_stime
) -
720 (p1
->p_utime
+ p1
->p_stime
)) == 0)
722 /* use resident set size (rssize) to break the tie */
723 if ((result
= p2
->p_size
- p1
->p_size
) == 0)
734 /* returns uid of owner of process pid */
740 register struct proc
**prefp
;
741 register struct proc
*pp
;
746 if ((pp
= *prefp
++)->p_pid
== (short)pid
)
747 return ((int)pp
->p_uid
);
753 int setpriority(int dummy
, int who
, int nicewal
)
760 /* sigblock is not POSIX conformant */
761 sigset_t
sigblock (sigset_t mask
)
766 sigprocmask(SIG_BLOCK
, &mask
, &oset
);
770 /* sigsetmask is not POSIX conformant */
771 sigsetmask(sigset_t mask
)
776 sigprocmask(SIG_SETMASK
, &mask
, &oset
);
781 /* ---------------- hops - comparison/ordering support ---------------- */
783 #define ORDERKEY_PCTCPU if (dresult = pctdouble(p2->p_cpu) - pctdouble(p1->p_cpu),\
784 (result = dresult > 0.0 ? 1 : dresult < 0.0 ? -1 : 0) == 0)
785 #define ORDERKEY_MEMSIZE if ((result = (p2->p_size - p1->p_size)) == 0)
786 #define ORDERKEY_CPTIME if ((result = (long)(p2->p_utime + p2->p_stime) -\
787 (long)(p1->p_utime + p1->p_stime)) == 0)
789 #define ORDERKEY_STATE if ((result = (sorted_state[p2->p_stat] - \
790 sorted_state[p1->p_stat])) == 0)
791 #define ORDERKEY_PRIO if ((result = p2->p_pri - p1->p_pri) == 0)
795 compare_cpu ( struct proc
**pp1
, struct proc
**pp2
)
797 register struct proc
*p1
;
798 register struct proc
*p2
;
802 /* remove one level of indirection */
818 /* compare_size - the comparison function for sorting by process size */
820 compare_size ( struct proc
**pp1
, struct proc
**pp2
)
822 register struct proc
*p1
;
823 register struct proc
*p2
;
827 /* remove one level of indirection */
842 /* compare_res - the comparison function for sorting by resident set size */
843 /* TODO: add shadow proc struct updating usr + sys times and RSS for use
844 * in comparison rtns, implement compare_res rtn as per compare_size()
847 /* compare_time - the comparison function for sorting by total cpu time */
848 /* This is giving wrong results since its using the proc structure vals not
849 * the u struct vals we display above
850 * TODO: add shadow proc struct updating usr + sys times and RSS for use
854 compare_time ( struct proc
**pp1
, struct proc
**pp2
)
856 register struct proc
*p1
;
857 register struct proc
*p2
;
861 /* remove one level of indirection */