Sync usage with man page.
[netbsd-mini2440.git] / external / bsd / top / dist / machine / m_sunos5.c
blobf47a5a48c2bd2908890064ef5a8038512c87e0cc
1 /*
2 * top - a top users display for Unix
4 * SYNOPSIS: Any Sun running SunOS 5.x (Solaris 2.x)
6 * DESCRIPTION:
7 * This is the machine-dependent module for SunOS 5.x (Solaris 2).
8 * There is some support for MP architectures.
9 * This makes top work on all revisions of SunOS 5 from 5.0
10 * through 5.9 (otherwise known as Solaris 9). It has not been
11 * tested on SunOS 5.10.
13 * AUTHORS: Torsten Kasch <torsten@techfak.uni-bielefeld.de>
14 * Robert Boucher <boucher@sofkin.ca>
15 * CONTRIBUTORS: Marc Cohen <marc@aai.com>
16 * Charles Hedrick <hedrick@geneva.rutgers.edu>
17 * William L. Jones <jones@chpc>
18 * Petri Kutvonen <kutvonen@cs.helsinki.fi>
19 * Casper Dik <casper.dik@sun.com>
20 * Tim Pugh <tpugh@oce.orst.edu>
23 #define _KMEMUSER
25 #include "os.h"
26 #include "utils.h"
27 #include "username.h"
28 #include "display.h"
30 #if (OSREV == 551)
31 #undef OSREV
32 #define OSREV 55
33 #endif
36 * Starting with SunOS 5.6 the data in /proc changed along with the
37 * means by which it is accessed. In this case we define USE_NEW_PROC.
38 * Note that with USE_NEW_PROC defined the structure named "prpsinfo"
39 * is redefined to be "psinfo". This will be confusing as you read
40 * the code.
43 #if OSREV >= 56
44 #define USE_NEW_PROC
45 #endif
47 #if defined(USE_NEW_PROC)
48 #define _STRUCTURED_PROC 1
49 #define prpsinfo psinfo
50 #include <sys/procfs.h>
51 #define pr_fill pr_nlwp
52 /* the "px" macros are used where the actual member could be in a substructure */
53 #define px_state pr_lwp.pr_state
54 #define px_nice pr_lwp.pr_nice
55 #define px_pri pr_lwp.pr_pri
56 #define px_onpro pr_lwp.pr_onpro
57 #define ZOMBIE(p) ((p)->pr_nlwp == 0)
58 #define SIZE_K(p) (long)((p)->pr_size)
59 #define RSS_K(p) (long)((p)->pr_rssize)
60 #else
61 #define px_state pr_state
62 #define px_oldpri pr_oldpri
63 #define px_nice pr_nice
64 #define px_pri pr_pri
65 #define px_onpro pr_filler[5]
66 #define ZOMBIE(p) ((p)->pr_zomb)
67 #define SIZE_K(p) (long)((p)->pr_bysize/1024)
68 #define RSS_K(p) (long)((p)->pr_byrssize/1024)
69 #endif
71 #include "top.h"
72 #include "machine.h"
73 #include <limits.h>
74 #include <stdio.h>
75 #include <fcntl.h>
76 #include <unistd.h>
77 #include <stdlib.h>
78 #include <errno.h>
79 #include <dirent.h>
80 #include <nlist.h>
81 #include <string.h>
82 #include <kvm.h>
83 #include <signal.h>
84 #include <sys/types.h>
85 #include <sys/param.h>
86 #include <sys/signal.h>
87 #include <sys/fault.h>
88 #include <sys/sysinfo.h>
89 #include <sys/sysmacros.h>
90 #include <sys/syscall.h>
91 #include <sys/user.h>
92 #include <sys/proc.h>
93 #include <sys/procfs.h>
94 #include <sys/vm.h>
95 #include <sys/var.h>
96 #include <sys/cpuvar.h>
97 #include <sys/file.h>
98 #include <sys/time.h>
99 #include <sys/priocntl.h>
100 #include <sys/tspriocntl.h>
101 #include <sys/processor.h>
102 #include <sys/resource.h>
103 #include <sys/swap.h>
104 #include <sys/stat.h>
105 #include <vm/anon.h>
106 #include <math.h>
107 #include <utmpx.h>
108 #include "utils.h"
109 #include "hash.h"
111 #if OSREV >= 53
112 #define USE_KSTAT
113 #endif
114 #ifdef USE_KSTAT
115 #include <kstat.h>
117 * Some kstats are fixed at 32 bits, these will be specified as ui32; some
118 * are "natural" size (32 bit on 32 bit Solaris, 64 on 64 bit Solaris
119 * we'll make those unsigned long)
120 * Older Solaris doesn't define KSTAT_DATA_UINT32, those are always 32 bit.
122 # ifndef KSTAT_DATA_UINT32
123 # define ui32 ul
124 # endif
125 #endif
127 #define UNIX "/dev/ksyms"
128 #define KMEM "/dev/kmem"
129 #define PROCFS "/proc"
130 #define CPUSTATES 5
131 #ifndef PRIO_MIN
132 #define PRIO_MIN -20
133 #endif
134 #ifndef PRIO_MAX
135 #define PRIO_MAX 20
136 #endif
138 #ifndef FSCALE
139 #define FSHIFT 8 /* bits to right of fixed binary point */
140 #define FSCALE (1<<FSHIFT)
141 #endif /* FSCALE */
143 #define loaddouble(la) ((double)(la) / FSCALE)
144 #define dbl_align(x) (((unsigned long)(x)+(sizeof(double)-1)) & \
145 ~(sizeof(double)-1))
148 * SunOS 5.4 and above track pctcpu in the proc structure as pr_pctcpu.
149 * These values are weighted over one minute whereas top output prefers
150 * a near-instantaneous measure of cpu utilization. So we choose to
151 * ignore pr_pctcpu: we calculate our own cpu percentage and store it in
152 * one of the spare slots in the prinfo structure.
155 #define percent_cpu(pp) (*(double *)dbl_align(&pp->pr_filler[0]))
157 /* definitions for indices in the nlist array */
158 #define X_V 0
159 #define X_MPID 1
160 #define X_ANONINFO 2
161 #define X_MAXMEM 3
162 #define X_FREEMEM 4
163 #define X_AVENRUN 5
164 #define X_CPU 6
165 #define X_NPROC 7
166 #define X_NCPUS 8
168 static struct nlist nlst[] =
170 {"v"}, /* 0 */ /* replaced by dynamic allocation */
171 {"mpid"}, /* 1 */
172 #if OSREV >= 56
173 /* this structure really has some extra fields, but the first three match */
174 {"k_anoninfo"}, /* 2 */
175 #else
176 {"anoninfo"}, /* 2 */
177 #endif
178 {"maxmem"}, /* 3 */ /* use sysconf */
179 {"freemem"}, /* 4 */ /* available from kstat >= 2.5 */
180 {"avenrun"}, /* 5 */ /* available from kstat */
181 {"cpu"}, /* 6 */ /* available from kstat */
182 {"nproc"}, /* 7 */ /* available from kstat */
183 {"ncpus"}, /* 8 */ /* available from kstat */
187 static unsigned long avenrun_offset;
188 static unsigned long mpid_offset;
189 #ifdef USE_KSTAT
190 static kstat_ctl_t *kc = NULL;
191 static kid_t kcid = 0;
192 #else
193 static unsigned long *cpu_offset;
194 #endif
195 static unsigned long nproc_offset;
196 static unsigned long freemem_offset;
197 static unsigned long maxmem_offset;
198 static unsigned long anoninfo_offset;
199 static int maxfiles = 256;
200 #define MAXFILES 2048
201 static int *display_fields;
202 static int show_threads = 0;
203 static int show_fullcmd;
205 /* get_process_info passes back a handle. This is what it looks like: */
206 struct handle
208 struct prpsinfo **next_proc;/* points to next valid proc pointer */
209 int remaining; /* number of pointers remaining */
213 * Structure for keeping track processes between updates.
214 * We keep these things in a hash table, which is updated at every cycle.
216 struct oldproc
218 pid_t pid;
219 id_t lwpid;
220 double oldtime;
221 double oldpct;
222 uid_t owner_uid;
223 int fd_psinfo;
224 int fd_lpsinfo;
225 int seen;
228 #define TIMESPEC_TO_DOUBLE(ts) ((ts).tv_sec * 1.0e9 + (ts).tv_nsec)
230 hash_table *prochash;
231 hash_table *threadhash;
234 * Structure for tracking per-cpu information
236 struct cpustats
238 unsigned int states[CPUSTATES];
239 uint_t pswitch;
240 uint_t trap;
241 uint_t intr;
242 uint_t syscall;
243 uint_t sysfork;
244 uint_t sysvfork;
245 uint_t pfault;
246 uint_t pgin;
247 uint_t pgout;
251 * GCC assumes that all doubles are aligned. Unfortunately it
252 * doesn't round up the structure size to be a multiple of 8.
253 * Thus we'll get a coredump when going through array. The
254 * following is a size rounded up to 8.
256 #define PRPSINFOSIZE dbl_align(sizeof(struct prpsinfo))
258 /* this defines one field (or column) in the process display */
260 struct proc_field {
261 char *name;
262 int width;
263 int rjust;
264 int min_screenwidth;
265 int (*format)(char *, int, struct prpsinfo *);
268 #define PROCSTATES 8
269 /* process state names for the "STATE" column of the display */
270 char *state_abbrev[] =
271 {"", "sleep", "run", "zombie", "stop", "start", "cpu", "swap"};
273 int process_states[PROCSTATES];
274 char *procstatenames[] =
276 "", " sleeping, ", " running, ", " zombie, ", " stopped, ",
277 " starting, ", " on cpu, ", " swapped, ",
278 NULL
281 int cpu_states[CPUSTATES];
282 char *cpustatenames[] =
283 {"idle", "user", "kernel", "iowait", "swap", NULL};
284 #define CPUSTATE_IOWAIT 3
285 #define CPUSTATE_SWAP 4
288 /* these are for detailing the memory statistics */
289 long memory_stats[5];
290 char *memorynames[] =
291 {"K phys mem, ", "K free mem, ", "K total swap, ", "K free swap", NULL};
292 #define MEMORY_TOTALMEM 0
293 #define MEMORY_FREEMEM 1
294 #define MEMORY_TOTALSWAP 2
295 #define MEMORY_FREESWAP 3
297 /* these are for detailing kernel statistics */
298 int kernel_stats[8];
299 char *kernelnames[] =
300 {" ctxsw, ", " trap, ", " intr, ", " syscall, ", " fork, ",
301 " flt, ", " pgin, ", " pgout, ", NULL};
302 #define KERNEL_CSWITCH 0
303 #define KERNEL_TRAP 1
304 #define KERNEL_INTR 2
305 #define KERNEL_SYSCALL 3
306 #define KERNEL_FORK 4
307 #define KERNEL_PFAULT 5
308 #define KERNEL_PGIN 6
309 #define KERNEL_PGOUT 7
311 /* these are names given to allowed sorting orders -- first is default */
312 char *ordernames[] =
313 {"cpu", "size", "res", "time", "pid", NULL};
315 /* forward definitions for comparison functions */
316 int compare_cpu();
317 int compare_size();
318 int compare_res();
319 int compare_time();
320 int compare_pid();
322 int (*proc_compares[])() = {
323 compare_cpu,
324 compare_size,
325 compare_res,
326 compare_time,
327 compare_pid,
328 NULL };
330 kvm_t *kd;
331 static DIR *procdir;
333 /* "cpucount" is used to store the value for the kernel variable "ncpus".
334 But since <sys/cpuvar.h> actually defines a variable "ncpus" we need
335 to use a different name here. --wnl */
336 static int cpucount;
338 /* pagetok function is really a pointer to an appropriate function */
339 static int pageshift;
340 static long (*p_pagetok) ();
341 #define pagetok(size) ((*p_pagetok)(size))
343 /* useful externals */
344 extern char *myname;
345 extern void perror ();
346 extern int getptable ();
347 extern void quit ();
349 /* process formatting functions and data */
352 fmt_pid(char *buf, int sz, struct prpsinfo *pp)
355 return snprintf(buf, sz, "%6d", (int)pp->pr_pid);
359 fmt_username(char *buf, int sz, struct prpsinfo *pp)
362 return snprintf(buf, sz, "%-8.8s", username(pp->pr_uid));
366 fmt_uid(char *buf, int sz, struct prpsinfo *pp)
369 return snprintf(buf, sz, "%6d", (int)pp->pr_uid);
373 fmt_nlwp(char *buf, int sz, struct prpsinfo *pp)
376 return snprintf(buf, sz, "%4d", pp->pr_fill < 999 ? pp->pr_fill: 999);
380 fmt_pri(char *buf, int sz, struct prpsinfo *pp)
383 return snprintf(buf, sz, "%3d", pp->px_pri);
387 fmt_nice(char *buf, int sz, struct prpsinfo *pp)
390 return snprintf(buf, sz, "%4d", pp->px_nice - NZERO);
394 fmt_size(char *buf, int sz, struct prpsinfo *pp)
397 return snprintf(buf, sz, "%5s", format_k(SIZE_K(pp)));
401 fmt_res(char *buf, int sz, struct prpsinfo *pp)
404 return snprintf(buf, sz, "%5s", format_k(RSS_K(pp)));
408 fmt_state(char *buf, int sz, struct prpsinfo *pp)
411 if (pp->px_state == SONPROC && cpucount > 1)
413 /* large #s may overflow colums */
414 if (pp->px_onpro < 100)
416 return snprintf(buf, sz, "cpu/%-2d", pp->px_onpro);
418 return snprintf(buf, sz, "cpu/**");
421 return snprintf(buf, sz, "%-6s", state_abbrev[(int)pp->px_state]);
425 fmt_time(char *buf, int sz, struct prpsinfo *pp)
428 return snprintf(buf, sz, "%6s", format_time(pp->pr_time.tv_sec));
432 fmt_cpu(char *buf, int sz, struct prpsinfo *pp)
435 return snprintf(buf, sz, "%5s%%",
436 format_percent(percent_cpu(pp) / cpucount));
440 fmt_command(char *buf, int sz, struct prpsinfo *pp)
443 return snprintf(buf, sz, "%s",
444 printable(show_fullcmd ? pp->pr_psargs : pp->pr_fname));
448 fmt_lwp(char *buf, int sz, struct prpsinfo *pp)
451 return snprintf(buf, sz, "%4d", ((int)pp->pr_lwp.pr_lwpid < 10000 ?
452 (int)pp->pr_lwp.pr_lwpid : 9999));
455 struct proc_field proc_field[] = {
456 { "PID", 6, 1, 0, fmt_pid },
457 { "USERNAME", 8, 0, 0, fmt_username },
458 #define FIELD_USERNAME 1
459 { "UID", 6, 1, 0, fmt_uid },
460 #define FIELD_UID 2
461 { "NLWP", 4, 1, 0, fmt_nlwp },
462 { "PRI", 3, 1, 0, fmt_pri },
463 { "NICE", 4, 1, 0, fmt_nice },
464 { "SIZE", 5, 1, 0, fmt_size },
465 { "RES", 5, 1, 0, fmt_res },
466 { "STATE", 6, 0, 0, fmt_state },
467 { "TIME", 6, 1, 0, fmt_time },
468 { "CPU", 6, 1, 0, fmt_cpu },
469 { "COMMAND", 7, 0, 0, fmt_command },
470 { "LWP", 4, 1, 0, fmt_lwp },
472 #define MAX_FIELDS 13
474 static int proc_display[MAX_FIELDS];
475 static int thr_display[MAX_FIELDS];
478 field_index(char *col)
481 struct proc_field *fp;
482 int i = 0;
484 fp = proc_field;
485 while (fp->name != NULL)
487 if (strcmp(col, fp->name) == 0)
489 return i;
491 fp++;
492 i++;
495 return -1;
498 void
499 field_subst(int *fp, int old, int new)
502 while (*fp != -1)
504 if (*fp == old)
506 *fp = new;
508 fp++;
512 /* p_pagetok points to one of the following, depending on which
513 direction data has to be shifted: */
515 long pagetok_none(long size)
518 return(size);
521 long pagetok_left(long size)
524 return(size << pageshift);
527 long pagetok_right(long size)
530 return(size >> pageshift);
534 * getkval(offset, ptr, size, refstr) - get a value out of the kernel.
535 * "offset" is the byte offset into the kernel for the desired value,
536 * "ptr" points to a buffer into which the value is retrieved,
537 * "size" is the size of the buffer (and the object to retrieve),
538 * "refstr" is a reference string used when printing error meessages,
539 * if "refstr" starts with a '!', then a failure on read will not
540 * be fatal (this may seem like a silly way to do things, but I
541 * really didn't want the overhead of another argument).
545 getkval (unsigned long offset,
546 int *ptr,
547 int size,
548 char *refstr)
550 dprintf("getkval(%08x, %08x, %d, %s)\n", offset, ptr, size, refstr);
552 if (kvm_read (kd, offset, (char *) ptr, size) != size)
554 dprintf("getkval: read failed\n");
555 if (*refstr == '!')
557 return (0);
559 else
561 fprintf (stderr, "top: kvm_read for %s: %s\n", refstr, strerror(errno));
562 quit (23);
566 dprintf("getkval read %d (%08x)\n", *ptr);
568 return (1);
572 /* procs structure memory management */
574 static struct prpsinfo **allprocs = NULL;
575 static struct prpsinfo **nextproc = NULL;
576 static int maxprocs = 0;
577 static int idxprocs = 0;
580 * void procs_prealloc(int cnt)
582 * Preallocate "cnt" procs structures. If "cnt" is less than or equal
583 * to procs_max() then this function has no effect.
586 void
587 procs_prealloc(int max)
590 int cnt;
591 struct prpsinfo *new;
592 struct prpsinfo **pp;
594 cnt = max - maxprocs;
595 if (cnt > 0)
597 dprintf("procs_prealloc: need %d, deficit %d\n", max, cnt);
598 allprocs = (struct prpsinfo **)
599 realloc((void *)allprocs, max * sizeof(struct prpsinfo *));
600 pp = nextproc = allprocs + idxprocs;
601 new = (struct prpsinfo *)malloc(cnt * PRPSINFOSIZE);
602 dprintf("procs_prealloc: idxprocs %d, allprocs %08x, nextproc %08x, new %08x\n",
603 idxprocs, allprocs, nextproc, new);
604 while (--cnt >= 0)
606 *pp++ = new;
607 new = (struct prpsinfo *) ((char *)new + PRPSINFOSIZE);
609 dprintf("procs_prealloc: done filling at %08x\n", new);
610 maxprocs = max;
615 * struct prpsinfo *procs_next()
617 * Return the next available procs structure, allocating a new one
618 * if needed.
621 struct prpsinfo *
622 procs_next()
625 if (idxprocs >= maxprocs)
627 /* allocate some more */
628 procs_prealloc(maxprocs + 128);
630 idxprocs++;
631 return *nextproc++;
634 struct prpsinfo *
635 procs_dup(struct prpsinfo *p)
638 struct prpsinfo *n;
640 n = procs_next();
641 memcpy(n, p, PRPSINFOSIZE);
642 return n;
646 * struct prpsinfo *procs_start()
648 * Return the first procs structure.
651 struct prpsinfo *
652 procs_start()
655 idxprocs = 0;
656 nextproc = allprocs;
657 return procs_next();
661 * int procs_max()
663 * Return the maximum number of procs structures currently allocated.
667 procs_max()
670 return maxprocs;
674 * check_nlist(nlst) - checks the nlist to see if any symbols were not
675 * found. For every symbol that was not found, a one-line
676 * message is printed to stderr. The routine returns the
677 * number of symbols NOT found.
680 check_nlist (register struct nlist *nlst)
682 register int i;
684 /* check to see if we got ALL the symbols we requested */
685 /* this will write one line to stderr for every symbol not found */
687 i = 0;
688 while (nlst->n_name != NULL)
690 if (nlst->n_type == 0)
692 /* this one wasn't found */
693 fprintf (stderr, "kernel: no symbol named `%s'\n", nlst->n_name);
694 i = 1;
696 nlst++;
698 return (i);
702 char *
703 format_header (register char *uname_field)
705 return ("");
708 #ifdef USE_KSTAT
710 long
711 kstat_data_value_l(kstat_named_t *kn)
714 #ifdef KSTAT_DATA_UINT32
715 switch(kn->data_type)
717 case KSTAT_DATA_INT32:
718 return ((long)(kn->value.i32));
719 case KSTAT_DATA_UINT32:
720 return ((long)(kn->value.ui32));
721 case KSTAT_DATA_INT64:
722 return ((long)(kn->value.i64));
723 case KSTAT_DATA_UINT64:
724 return ((long)(kn->value.ui64));
726 return 0;
727 #else
728 return ((long)(kn->value.ui32));
729 #endif
733 kstat_safe_retrieve(kstat_t **ksp,
734 char *module, int instance, char *name, void *buf)
737 kstat_t *ks;
738 kid_t new_kcid;
739 int changed;
741 dprintf("kstat_safe_retrieve(%08x -> %08x, %s, %d, %s, %08x)\n",
742 ksp, *ksp, module, instance, name, buf);
744 ks = *ksp;
745 do {
746 changed = 0;
747 /* if we dont already have the kstat, retrieve it */
748 if (ks == NULL)
750 if ((ks = kstat_lookup(kc, module, instance, name)) == NULL)
752 return (-1);
754 *ksp = ks;
757 /* attempt to read it */
758 new_kcid = kstat_read(kc, ks, buf);
759 /* chance for an infinite loop here if kstat_read keeps
760 returning -1 */
762 /* if the chain changed, update it */
763 if (new_kcid != kcid)
765 dprintf("kstat_safe_retrieve: chain changed to %d...updating\n",
766 new_kcid);
767 changed = 1;
768 kcid = kstat_chain_update(kc);
770 } while (changed);
772 return (0);
776 * int kstat_safe_namematch(int num, kstat_t *ksp, char *name, void *buf)
778 * Safe scan of kstat chain for names starting with "name". Matches
779 * are copied in to "ksp", and kstat_read is called on each match using
780 * "buf" as a buffer of length "size". The actual number of records
781 * found is returned. Up to "num" kstats are copied in to "ksp", but
782 * no more. If any kstat_read indicates that the chain has changed, then
783 * the whole process is restarted.
787 kstat_safe_namematch(int num, kstat_t **ksparg, char *name, void *buf, int size)
790 kstat_t *ks;
791 kstat_t **ksp;
792 kid_t new_kcid;
793 int namelen;
794 int count;
795 int changed;
796 char *cbuf;
798 dprintf("kstat_safe_namematch(%d, %08x, %s, %08x, %d)\n",
799 num, ksparg, name, buf, size);
801 namelen = strlen(name);
803 do {
804 /* initialize before the scan */
805 cbuf = (char *)buf;
806 ksp = ksparg;
807 count = 0;
808 changed = 0;
810 /* scan the chain for matching kstats */
811 for (ks = kc->kc_chain; ks != NULL; ks = ks->ks_next)
813 if (strncmp(ks->ks_name, name, namelen) == 0)
815 /* this kstat matches: save it if there is room */
816 if (count++ < num)
818 /* read the kstat */
819 new_kcid = kstat_read(kc, ks, cbuf);
821 /* if the chain changed, update it */
822 if (new_kcid != kcid)
824 dprintf("kstat_safe_namematch: chain changed to %d...updating\n",
825 new_kcid);
826 changed = 1;
827 kcid = kstat_chain_update(kc);
829 /* there's no sense in continuing the scan */
830 /* so break out of the for loop */
831 break;
834 /* move to the next buffers */
835 cbuf += size;
836 *ksp++ = ks;
840 } while(changed);
842 dprintf("kstat_safe_namematch returns %d\n", count);
844 return count;
847 static kstat_t *ks_system_misc = NULL;
849 #endif /* USE_KSTAT */
853 get_avenrun(int avenrun[3])
856 #ifdef USE_KSTAT
857 int status;
858 kstat_named_t *kn;
860 dprintf("get_avenrun(%08x)\n", avenrun);
862 if ((status = kstat_safe_retrieve(&ks_system_misc,
863 "unix", 0, "system_misc", NULL)) == 0)
865 if ((kn = kstat_data_lookup(ks_system_misc, "avenrun_1min")) != NULL)
867 avenrun[0] = kn->value.ui32;
869 if ((kn = kstat_data_lookup(ks_system_misc, "avenrun_5min")) != NULL)
871 avenrun[1] = kn->value.ui32;
873 if ((kn = kstat_data_lookup(ks_system_misc, "avenrun_15min")) != NULL)
875 avenrun[2] = kn->value.ui32;
878 dprintf("get_avenrun returns %d\n", status);
879 return (status);
881 #else /* !USE_KSTAT */
883 (void) getkval (avenrun_offset, (int *) avenrun, sizeof (int [3]), "avenrun");
885 return 0;
887 #endif /* USE_KSTAT */
891 get_ncpus()
894 #ifdef USE_KSTAT
895 kstat_named_t *kn;
896 int ret = -1;
898 if ((kn = kstat_data_lookup(ks_system_misc, "ncpus")) != NULL)
900 ret = (int)(kn->value.ui32);
903 return ret;
904 #else
905 int ret;
907 (void) getkval(nlst[X_NCPUS].n_value, (int *)(&ret), sizeof(ret), "ncpus");
908 return ret;
909 #endif
913 get_nproc()
916 #ifdef USE_KSTAT
917 kstat_named_t *kn;
918 int ret = -1;
920 if ((kn = kstat_data_lookup(ks_system_misc, "nproc")) != NULL)
922 ret = (int)(kn->value.ui32);
924 #else
925 int ret;
927 (void) getkval (nproc_offset, (int *) (&ret), sizeof (ret), "nproc");
928 #endif
930 dprintf("get_nproc returns %d\n", ret);
931 return ret;
934 struct cpustats *
935 get_cpustats(int *cnt, struct cpustats *cpustats)
938 #ifdef USE_KSTAT
939 static kstat_t **cpu_ks = NULL;
940 static cpu_stat_t *cpu_stat = NULL;
941 static unsigned int nelems = 0;
942 cpu_stat_t *cpu_stat_p;
943 int i, cpu_num;
944 struct cpustats *cpustats_p;
946 dprintf("get_cpustats(%d -> %d, %08x)\n", cnt, *cnt, cpustats);
948 while (nelems > 0 ?
949 (cpu_num = kstat_safe_namematch(nelems,
950 cpu_ks,
951 "cpu_stat",
952 cpu_stat,
953 sizeof(cpu_stat_t))) > nelems :
954 (cpu_num = get_ncpus()) > 0)
956 /* reallocate the arrays */
957 dprintf("realloc from %d to %d\n", nelems, cpu_num);
958 nelems = cpu_num;
959 if (cpu_ks != NULL)
961 free(cpu_ks);
963 cpu_ks = (kstat_t **)calloc(nelems, sizeof(kstat_t *));
964 if (cpu_stat != NULL)
966 free(cpu_stat);
968 cpu_stat = (cpu_stat_t *)malloc(nelems * sizeof(cpu_stat_t));
971 /* do we have more cpus than our caller? */
972 if (cpu_num > *cnt)
974 /* yes, so realloc their array, too */
975 dprintf("realloc array from %d to %d\n", *cnt, cpu_num);
976 *cnt = cpu_num;
977 cpustats = (struct cpustats *)realloc(cpustats,
978 cpu_num * sizeof(struct cpustats));
981 cpu_stat_p = cpu_stat;
982 cpustats_p = cpustats;
983 for (i = 0; i < cpu_num; i++)
985 dprintf("cpu %d %08x: idle %u, user %u, syscall %u\n", i, cpu_stat_p,
986 cpu_stat_p->cpu_sysinfo.cpu[0],
987 cpu_stat_p->cpu_sysinfo.cpu[1],
988 cpu_stat_p->cpu_sysinfo.syscall);
990 cpustats_p->states[CPU_IDLE] = cpu_stat_p->cpu_sysinfo.cpu[CPU_IDLE];
991 cpustats_p->states[CPU_USER] = cpu_stat_p->cpu_sysinfo.cpu[CPU_USER];
992 cpustats_p->states[CPU_KERNEL] = cpu_stat_p->cpu_sysinfo.cpu[CPU_KERNEL];
993 cpustats_p->states[CPUSTATE_IOWAIT] = cpu_stat_p->cpu_sysinfo.wait[W_IO] +
994 cpu_stat_p->cpu_sysinfo.wait[W_PIO];
995 cpustats_p->states[CPUSTATE_SWAP] = cpu_stat_p->cpu_sysinfo.wait[W_SWAP];
996 cpustats_p->pswitch = cpu_stat_p->cpu_sysinfo.pswitch;
997 cpustats_p->trap = cpu_stat_p->cpu_sysinfo.trap;
998 cpustats_p->intr = cpu_stat_p->cpu_sysinfo.intr;
999 cpustats_p->syscall = cpu_stat_p->cpu_sysinfo.syscall;
1000 cpustats_p->sysfork = cpu_stat_p->cpu_sysinfo.sysfork;
1001 cpustats_p->sysvfork = cpu_stat_p->cpu_sysinfo.sysvfork;
1002 cpustats_p->pfault = cpu_stat_p->cpu_vminfo.hat_fault +
1003 cpu_stat_p->cpu_vminfo.as_fault;
1004 cpustats_p->pgin = cpu_stat_p->cpu_vminfo.pgin;
1005 cpustats_p->pgout = cpu_stat_p->cpu_vminfo.pgout;
1006 cpustats_p++;
1007 cpu_stat_p++;
1010 cpucount = cpu_num;
1012 dprintf("get_cpustats sees %d cpus and returns %08x\n", cpucount, cpustats);
1014 return (cpustats);
1015 #else /* !USE_KSTAT */
1016 int i;
1017 struct cpu cpu;
1018 unsigned int (*cp_stats_p)[CPUSTATES];
1020 /* do we have more cpus than our caller? */
1021 if (cpucount > *cnt)
1023 /* yes, so realloc their array, too */
1024 dprintf("realloc array from %d to %d\n", *cnt, cpucount);
1025 *cnt = cpucount;
1026 cp_stats = (unsigned int (*)[CPUSTATES])realloc(cp_stats,
1027 cpucount * sizeof(unsigned int) * CPUSTATES);
1030 cp_stats_p = cp_stats;
1031 for (i = 0; i < cpucount; i++)
1033 if (cpu_offset[i] != 0)
1035 /* get struct cpu for this processor */
1036 (void) getkval (cpu_offset[i], (int *)(&cpu), sizeof (struct cpu), "cpu");
1038 (*cp_stats_p)[CPU_IDLE] = cpu.cpu_stat.cpu_sysinfo.cpu[CPU_IDLE];
1039 (*cp_stats_p)[CPU_USER] = cpu.cpu_stat.cpu_sysinfo.cpu[CPU_USER];
1040 (*cp_stats_p)[CPU_KERNEL] = cpu.cpu_stat.cpu_sysinfo.cpu[CPU_KERNEL];
1041 (*cp_stats_p)[CPUSTATE_IOWAIT] = cpu.cpu_stat.cpu_sysinfo.wait[W_IO] +
1042 cpu.cpu_stat.cpu_sysinfo.wait[W_PIO];
1043 (*cp_stats_p)[CPUSTATE_SWAP] = cpu.cpu_stat.cpu_sysinfo.wait[W_SWAP];
1044 cp_stats_p++;
1048 return (cp_stats);
1049 #endif /* USE_KSTAT */
1053 * void get_meminfo(long *total, long *fr)
1055 * Get information about the system's physical memory. Pass back values
1056 * for total available and amount of memory that is free (in kilobytes).
1057 * It returns 0 on success and -1 on any kind of failure.
1061 get_meminfo(long *total, long *fr)
1064 long freemem;
1065 static kstat_t *ks = NULL;
1066 kstat_named_t *kn;
1068 /* total comes from sysconf */
1069 *total = pagetok(sysconf(_SC_PHYS_PAGES));
1071 /* free comes from the kernel's freemem or from kstat */
1072 /* prefer kmem for this because kstat unix:0:system_pages
1073 can be slow on systems with lots of memory */
1074 if (kd)
1076 (void) getkval(freemem_offset, (int *)(&freemem), sizeof(freemem),
1077 "freemem");
1079 else
1081 #ifdef USE_KSTAT
1082 /* only need to grab kstat chain once */
1083 if (ks == NULL)
1085 ks = kstat_lookup(kc, "unix", 0, "system_pages");
1088 if (ks != NULL &&
1089 kstat_read(kc, ks, 0) != -1 &&
1090 (kn = kstat_data_lookup(ks, "freemem")) != NULL)
1092 freemem = kstat_data_value_l(kn);
1094 else
1096 freemem = -1;
1098 #else
1099 freemem = -1;
1100 #endif
1103 *fr = freemem == -1 ? -1 : pagetok(freemem);
1105 return (0);
1109 * void get_swapinfo(long *total, long *fr)
1111 * Get information about the system's swap. Pass back values for
1112 * total swap available and amount of swap that is free (in kilobytes).
1113 * It returns 0 on success and -1 on any kind of failure.
1117 get_swapinfo(long *total, long *fr)
1120 register int cnt, i;
1121 register long t, f;
1122 struct swaptable *swt;
1123 struct swapent *ste;
1124 static char path[256];
1126 /* preset values to 0 just in case we have to return early */
1127 *total = 0;
1128 *fr = 0;
1130 /* get total number of swap entries */
1131 if ((cnt = swapctl(SC_GETNSWP, 0)) == -1)
1133 return (-1);
1136 /* allocate enough space to hold count + n swapents */
1137 swt = (struct swaptable *)malloc(sizeof(int) +
1138 cnt * sizeof(struct swapent));
1139 if (swt == NULL)
1141 return (-1);
1143 swt->swt_n = cnt;
1145 /* fill in ste_path pointers: we don't care about the paths, so we point
1146 them all to the same buffer */
1147 ste = &(swt->swt_ent[0]);
1148 i = cnt;
1149 while (--i >= 0)
1151 ste++->ste_path = path;
1154 /* grab all swap info */
1155 if (swapctl(SC_LIST, swt) == -1)
1157 return (-1);
1160 /* walk thru the structs and sum up the fields */
1161 t = f = 0;
1162 ste = &(swt->swt_ent[0]);
1163 i = cnt;
1164 while (--i >= 0)
1166 /* dont count slots being deleted */
1167 if (!(ste->ste_flags & ST_INDEL) &&
1168 !(ste->ste_flags & ST_DOINGDEL))
1170 t += ste->ste_pages;
1171 f += ste->ste_free;
1173 ste++;
1176 /* fill in the results */
1177 *total = pagetok(t);
1178 *fr = pagetok(f);
1179 free(swt);
1181 /* good to go */
1182 return (0);
1186 machine_init (struct statics *statics)
1188 struct utmpx ut;
1189 struct utmpx *up;
1190 struct rlimit rlim;
1191 int i;
1192 char *p;
1193 int *ip;
1194 int nproc;
1195 #ifndef USE_KSTAT
1196 int offset;
1197 #endif
1199 /* There's a buffer overflow bug in curses that can be exploited when
1200 we run as root. By making sure that TERMINFO is set to something
1201 this bug is avoided. This code thanks to Casper */
1202 if ((p = getenv("TERMINFO")) == NULL || *p == '\0')
1204 putenv("TERMINFO=/usr/share/lib/terminfo/");
1207 /* perform the kvm_open - suppress error here */
1208 if ((kd = kvm_open (NULL, NULL, NULL, O_RDONLY, NULL)) == NULL)
1210 /* save the error message: we may need it later */
1211 p = strerror(errno);
1213 dprintf("kvm_open: fd %d\n", kd);
1216 * turn off super group/user privs - but beware; we might
1217 * want the privs back later and we still have a fd to
1218 * /dev/kmem open so we can't use setgid()/setuid() as that
1219 * would allow a debugger to attach to this process. CD
1221 setegid(getgid());
1222 seteuid(getuid()); /* super user not needed for NEW_PROC */
1224 #ifdef USE_KSTAT
1225 /* open kstat */
1226 if ((kc = kstat_open()) == NULL)
1228 fprintf(stderr, "Unable to open kstat.\n");
1229 return(-1);
1231 kcid = kc->kc_chain_id;
1232 dprintf("kstat_open: chain %d\n", kcid);
1233 #endif
1235 /* fill in the statics information */
1236 statics->procstate_names = procstatenames;
1237 statics->cpustate_names = cpustatenames;
1238 statics->memory_names = memorynames;
1239 statics->kernel_names = kernelnames;
1240 statics->order_names = ordernames;
1241 statics->flags.fullcmds = 1;
1242 statics->flags.warmup = 1;
1243 statics->flags.threads = 1;
1245 /* get boot time */
1246 ut.ut_type = BOOT_TIME;
1247 if ((up = getutxid(&ut)) != NULL)
1249 statics->boottime = up->ut_tv.tv_sec;
1251 endutxent();
1253 /* if the kvm_open succeeded, get the nlist */
1254 if (kd)
1256 if (kvm_nlist (kd, nlst) < 0)
1258 perror ("kvm_nlist");
1259 return (-1);
1261 if (check_nlist (nlst) != 0)
1262 return (-1);
1264 #ifndef USE_KSTAT
1265 /* if KSTAT is not available to us and we can't open /dev/kmem,
1266 this is a serious problem.
1268 else
1270 /* Print the error message here */
1271 (void) fprintf(stderr, "kvm_open: %s\n", p);
1272 return (-1);
1274 #endif
1276 /* stash away certain offsets for later use */
1277 mpid_offset = nlst[X_MPID].n_value;
1278 nproc_offset = nlst[X_NPROC].n_value;
1279 avenrun_offset = nlst[X_AVENRUN].n_value;
1280 anoninfo_offset = nlst[X_ANONINFO].n_value;
1281 freemem_offset = nlst[X_FREEMEM].n_value;
1282 maxmem_offset = nlst[X_MAXMEM].n_value;
1284 #ifndef USE_KSTAT
1285 (void) getkval (nlst[X_NCPUS].n_value, (int *) (&cpucount),
1286 sizeof (cpucount), "ncpus");
1288 cpu_offset = (unsigned long *) malloc (cpucount * sizeof (unsigned long));
1289 for (i = offset = 0; i < cpucount; offset += sizeof(unsigned long)) {
1290 (void) getkval (nlst[X_CPU].n_value + offset,
1291 (int *)(&cpu_offset[i]), sizeof (unsigned long),
1292 nlst[X_CPU].n_name );
1293 if (cpu_offset[i] != 0)
1294 i++;
1296 #endif
1298 /* we need to get the current nproc */
1299 #ifdef USE_KSTAT
1300 /* get_nproc assumes that the chain has already been retrieved,
1301 so we need to do that here */
1302 kstat_safe_retrieve(&ks_system_misc, "unix", 0, "system_misc", NULL);
1303 #endif
1304 nproc = get_nproc();
1305 dprintf("machine_init: nproc=%d\n", nproc);
1307 /* hash table for procs and threads sized based on current nproc*/
1308 prochash = hash_create(nproc > 100 ? nproc * 2 + 1 : 521);
1309 threadhash = hash_create(nproc > 100 ? nproc * 4 + 1 : 2053);
1311 /* calculate pageshift value */
1312 i = sysconf(_SC_PAGESIZE);
1313 pageshift = 0;
1314 while ((i >>= 1) > 0)
1316 pageshift++;
1319 /* calculate an amount to shift to K values */
1320 /* remember that log base 2 of 1024 is 10 (i.e.: 2^10 = 1024) */
1321 pageshift -= 10;
1323 /* now determine which pageshift function is appropriate for the
1324 result (have to because x << y is undefined for y < 0) */
1325 if (pageshift > 0)
1327 /* this is the most likely */
1328 p_pagetok = pagetok_left;
1330 else if (pageshift == 0)
1332 p_pagetok = pagetok_none;
1334 else
1336 p_pagetok = pagetok_right;
1337 pageshift = -pageshift;
1340 /* we cache open files to improve performance, so we need to up
1341 the NOFILE limit */
1342 if (getrlimit(RLIMIT_NOFILE, &rlim) == 0)
1344 /* set a new soft limit */
1345 maxfiles = (int)(rlim.rlim_max < MAXFILES ? rlim.rlim_max : MAXFILES);
1346 rlim.rlim_cur = (rlim_t)maxfiles;
1347 (void)setrlimit(RLIMIT_NOFILE, &rlim);
1349 /* now leave some wiggle room above the maximum */
1350 maxfiles -= 20;
1353 /* set up the display indices */
1354 ip = proc_display;
1355 *ip++ = field_index("PID");
1356 *ip++ = field_index("USERNAME");
1357 *ip++ = field_index("NLWP");
1358 *ip++ = field_index("PRI");
1359 *ip++ = field_index("NICE");
1360 *ip++ = field_index("SIZE");
1361 *ip++ = field_index("RES");
1362 *ip++ = field_index("STATE");
1363 *ip++ = field_index("TIME");
1364 *ip++ = field_index("CPU");
1365 *ip++ = field_index("COMMAND");
1366 *ip = -1;
1367 ip = thr_display;
1368 *ip++ = field_index("PID");
1369 *ip++ = field_index("LWP");
1370 *ip++ = field_index("USERNAME");
1371 *ip++ = field_index("PRI");
1372 *ip++ = field_index("NICE");
1373 *ip++ = field_index("SIZE");
1374 *ip++ = field_index("RES");
1375 *ip++ = field_index("STATE");
1376 *ip++ = field_index("TIME");
1377 *ip++ = field_index("CPU");
1378 *ip++ = field_index("COMMAND");
1379 *ip = -1;
1381 if (!(procdir = opendir (PROCFS)))
1383 (void) fprintf (stderr, "Unable to open %s\n", PROCFS);
1384 return (-1);
1387 if (chdir (PROCFS))
1388 { /* handy for later on when we're reading it */
1389 (void) fprintf (stderr, "Unable to chdir to %s\n", PROCFS);
1390 return (-1);
1393 /* all done! */
1394 return (0);
1397 void
1398 get_system_info (struct system_info *si)
1400 int avenrun[3];
1402 static long cp_time[CPUSTATES];
1403 static long cp_old[CPUSTATES];
1404 static long cp_diff[CPUSTATES];
1405 static struct cpustats *cpustats = NULL;
1406 static struct cpustats sum_current;
1407 static struct cpustats sum_old;
1408 static int cpus = 0;
1409 register int j, i;
1411 /* remember the old values and zero out the current */
1412 memcpy(&sum_old, &sum_current, sizeof(sum_current));
1413 memset(&sum_current, 0, sizeof(sum_current));
1415 /* get important information */
1416 get_avenrun(avenrun);
1418 /* get the cpu statistics arrays */
1419 cpustats = get_cpustats(&cpus, cpustats);
1421 /* zero the cp_time array */
1422 memset(cp_time, 0, sizeof(cp_time));
1424 /* sum stats in to a single array and a single structure */
1425 for (i = 0; i < cpus; i++)
1427 for (j = 0; j < CPUSTATES; j++)
1429 cp_time[j] += cpustats[i].states[j];
1431 sum_current.pswitch += cpustats[i].pswitch;
1432 sum_current.trap += cpustats[i].trap;
1433 sum_current.intr += cpustats[i].intr;
1434 sum_current.syscall += cpustats[i].syscall;
1435 sum_current.sysfork += cpustats[i].sysfork;
1436 sum_current.sysvfork += cpustats[i].sysvfork;
1437 sum_current.pfault += cpustats[i].pfault;
1438 sum_current.pgin += cpustats[i].pgin;
1439 sum_current.pgout += cpustats[i].pgout;
1442 /* convert cp_time counts to percentages */
1443 (void) percentages (CPUSTATES, cpu_states, cp_time, cp_old, cp_diff);
1445 /* get mpid -- process id of last process */
1446 if (kd)
1447 (void) getkval(mpid_offset, &(si->last_pid), sizeof (si->last_pid), "mpid");
1448 else
1449 si->last_pid = -1;
1451 /* convert load averages to doubles */
1452 for (i = 0; i < 3; i++)
1453 si->load_avg[i] = loaddouble (avenrun[i]);
1455 /* get physical memory data */
1456 if (get_meminfo(&(memory_stats[MEMORY_TOTALMEM]),
1457 &(memory_stats[MEMORY_FREEMEM])) == -1)
1459 memory_stats[MEMORY_TOTALMEM] = memory_stats[MEMORY_FREEMEM] = -1;
1462 /* get swap data */
1463 if (get_swapinfo(&(memory_stats[MEMORY_TOTALSWAP]),
1464 &(memory_stats[MEMORY_FREESWAP])) == -1)
1466 memory_stats[MEMORY_TOTALSWAP] = memory_stats[MEMORY_FREESWAP] = -1;
1469 /* get kernel data */
1470 kernel_stats[KERNEL_CSWITCH] = diff_per_second(sum_current.pswitch, sum_old.pswitch);
1471 kernel_stats[KERNEL_TRAP] = diff_per_second(sum_current.trap, sum_old.trap);
1472 kernel_stats[KERNEL_INTR] = diff_per_second(sum_current.intr, sum_old.intr);
1473 kernel_stats[KERNEL_SYSCALL] = diff_per_second(sum_current.syscall, sum_old.syscall);
1474 kernel_stats[KERNEL_FORK] = diff_per_second(sum_current.sysfork + sum_current.sysvfork,
1475 sum_old.sysfork + sum_old.sysvfork);
1476 kernel_stats[KERNEL_PFAULT] = diff_per_second(sum_current.pfault, sum_old.pfault);
1477 kernel_stats[KERNEL_PGIN] = pagetok(diff_per_second(sum_current.pgin, sum_old.pgin));
1478 kernel_stats[KERNEL_PGOUT] = pagetok(diff_per_second(sum_current.pgout, sum_old.pgout));
1481 /* set arrays and strings */
1482 si->cpustates = cpu_states;
1483 si->memory = memory_stats;
1484 si->kernel = kernel_stats;
1486 dprintf("get_system_info returns\n");
1489 static struct handle handle;
1491 caddr_t
1492 get_process_info (
1493 struct system_info *si,
1494 struct process_select *sel,
1495 int compare_index)
1497 register int i;
1498 register int total_procs;
1499 register int active_procs;
1500 register struct prpsinfo **prefp;
1501 register struct prpsinfo *pp;
1502 int nproc;
1503 int state;
1505 /* these are copied out of sel for speed */
1506 int show_idle;
1507 int show_system;
1508 int show_uid;
1509 char *show_command;
1511 /* these persist across calls */
1512 static struct prpsinfo **pref = NULL;
1513 static int pref_size = 0;
1515 /* set up flags which define what we are going to select */
1516 show_idle = sel->idle;
1517 show_system = sel->system;
1518 show_uid = sel->uid != -1;
1519 show_fullcmd = sel->fullcmd;
1520 show_command = sel->command;
1521 show_threads = sel->threads;
1523 /* allocate enough space for twice our current needs */
1524 nproc = get_nproc();
1525 if (nproc > procs_max())
1527 procs_prealloc(2 * nproc);
1530 /* read all the proc structures */
1531 nproc = getptable();
1533 /* allocate pref[] */
1534 if (pref_size < nproc)
1536 if (pref != NULL)
1538 free(pref);
1540 pref = (struct prpsinfo **)malloc(nproc * sizeof(struct prpsinfo *));
1541 dprintf("get_process_info: allocated %d prinfo pointers at %08x\n",
1542 nproc, pref);
1543 pref_size = nproc;
1546 /* get a pointer to the states summary array */
1547 si->procstates = process_states;
1549 /* count up process states and get pointers to interesting procs */
1550 total_procs = 0;
1551 active_procs = 0;
1552 (void) memset (process_states, 0, sizeof (process_states));
1553 prefp = pref;
1555 for (pp = procs_start(), i = 0; i < nproc;
1556 i++, pp = procs_next())
1558 dprintf("looking at #%d: %d.%d\n", i,
1559 pp->pr_pid, pp->pr_lwp.pr_lwpid);
1561 * Place pointers to each valid proc structure in pref[].
1562 * Process slots that are actually in use have a non-zero
1563 * status field. Processes with SSYS set are system
1564 * processes---these get ignored unless show_sysprocs is set.
1566 if (pp->px_state != 0 &&
1567 (show_system || ((pp->pr_flag & SSYS) == 0)))
1569 total_procs++;
1570 state = (int)pp->px_state;
1571 if (state > 0 && state < PROCSTATES)
1573 process_states[state]++;
1575 else
1577 dprintf("process %d.%d: state out of bounds %d\n",
1578 pp->pr_pid, pp->pr_lwp.pr_lwpid, state);
1581 if ((!ZOMBIE(pp)) &&
1582 (show_idle || percent_cpu (pp) || (pp->px_state == SRUN) || (pp->px_state == SONPROC)) &&
1583 (!show_uid || pp->pr_uid == (uid_t) sel->uid) &&
1584 (show_command == NULL ||
1585 strstr(pp->pr_fname, show_command) != NULL))
1587 *prefp++ = pp;
1588 active_procs++;
1593 dprintf("total_procs %d, active_procs %d\n", total_procs, active_procs);
1595 /* if requested, sort the "interesting" processes */
1596 qsort ((char *) pref, active_procs, sizeof (struct prpsinfo *),
1597 proc_compares[compare_index]);
1599 /* remember active and total counts */
1600 si->p_total = total_procs;
1601 si->p_active = active_procs;
1603 /* pass back a handle */
1604 handle.next_proc = pref;
1605 handle.remaining = active_procs;
1606 return ((caddr_t) & handle);
1609 static char p_header[MAX_COLS];
1611 char *
1612 format_process_header(struct process_select *sel, caddr_t handle, int count)
1615 int cols;
1616 char *p;
1617 int *fi;
1618 struct proc_field *fp;
1620 /* check for null handle */
1621 if (handle == NULL)
1623 return("");
1626 /* remember how many columns there are on the display */
1627 cols = display_columns();
1629 /* mode & threads dictate format */
1630 fi = display_fields = sel->threads ? thr_display : proc_display;
1632 /* set username field correctly */
1633 if (!sel->usernames)
1635 /* display uids */
1636 field_subst(fi, FIELD_USERNAME, FIELD_UID);
1638 else
1640 /* display usernames */
1641 field_subst(fi, FIELD_UID, FIELD_USERNAME);
1644 /* walk thru fields and construct header */
1645 /* are we worried about overflow??? */
1646 p = p_header;
1647 while (*fi != -1)
1649 fp = &(proc_field[*fi++]);
1650 if (fp->min_screenwidth <= cols)
1652 p += sprintf(p, fp->rjust ? "%*s" : "%-*s", fp->width, fp->name);
1653 *p++ = ' ';
1656 *--p = '\0';
1658 return p_header;
1661 static char fmt[MAX_COLS]; /* static area where result is built */
1663 char *
1664 format_next_process(caddr_t handle, char *(*get_userid)(int))
1667 struct prpsinfo *pp;
1668 struct handle *hp;
1669 struct proc_field *fp;
1670 int *fi;
1671 int i;
1672 int cols;
1673 char *p;
1674 int len;
1675 int x;
1677 /* find and remember the next proc structure */
1678 hp = (struct handle *)handle;
1679 pp = *(hp->next_proc++);
1680 hp->remaining--;
1682 /* grab format descriptor */
1683 fi = display_fields;
1685 /* screen width is a consideration, too */
1686 cols = display_columns();
1688 /* build output by field */
1689 p = fmt;
1690 len = MAX_COLS;
1691 while ((i = *fi++) != -1)
1693 fp = &(proc_field[i]);
1694 if (len > 0 && fp->min_screenwidth <= cols)
1696 x = (*(fp->format))(p, len, pp);
1697 if (x >= len)
1699 dprintf("format_next_process: formatter overflow: x %d, len %d, p %08x => %08x, fmt %08x - %08x\n",
1700 x, len, p, p + len, fmt, fmt + sizeof(fmt));
1701 p += len;
1702 len = 0;
1704 else
1706 p += x;
1707 *p++ = ' ';
1708 len -= x + 1;
1712 *--p = '\0';
1714 /* return the result */
1715 return(fmt);
1718 /* comparison routines for qsort */
1721 * There are currently four possible comparison routines. main selects
1722 * one of these by indexing in to the array proc_compares.
1724 * Possible keys are defined as macros below. Currently these keys are
1725 * defined: percent cpu, cpu ticks, process state, resident set size,
1726 * total virtual memory usage. The process states are ordered as follows
1727 * (from least to most important): WAIT, zombie, sleep, stop, start, run.
1728 * The array declaration below maps a process state index into a number
1729 * that reflects this ordering.
1732 /* First, the possible comparison keys. These are defined in such a way
1733 that they can be merely listed in the source code to define the actual
1734 desired ordering.
1737 #define ORDERKEY_PCTCPU if (dresult = percent_cpu (p2) - percent_cpu (p1),\
1738 (result = dresult > 0.0 ? 1 : dresult < 0.0 ? -1 : 0) == 0)
1739 #define ORDERKEY_CPTICKS if ((result = p2->pr_time.tv_sec - p1->pr_time.tv_sec) == 0)
1740 #define ORDERKEY_STATE if ((result = (long) (sorted_state[(int)p2->px_state] - \
1741 sorted_state[(int)p1->px_state])) == 0)
1742 #define ORDERKEY_PRIO if ((result = p2->px_pri - p1->px_pri) == 0)
1743 #define ORDERKEY_RSSIZE if ((result = p2->pr_rssize - p1->pr_rssize) == 0)
1744 #define ORDERKEY_MEM if ((result = (p2->pr_size - p1->pr_size)) == 0)
1745 #define ORDERKEY_LWP if ((result = (p1->pr_lwp.pr_lwpid - p2->pr_lwp.pr_lwpid)) == 0)
1746 #define ORDERKEY_PID if ((result = (p1->pr_pid - p2->pr_pid)) == 0)
1748 /* Now the array that maps process state to a weight */
1750 unsigned char sorted_state[] =
1752 0, /* not used */
1753 3, /* sleep */
1754 6, /* run */
1755 2, /* zombie */
1756 4, /* stop */
1757 5, /* start */
1758 7, /* run on a processor */
1759 1 /* being swapped (WAIT) */
1763 /* compare_cpu - the comparison function for sorting by cpu percentage */
1766 compare_cpu (struct prpsinfo **pp1, struct prpsinfo **pp2)
1769 register struct prpsinfo *p1;
1770 register struct prpsinfo *p2;
1771 register long result;
1772 double dresult;
1774 /* remove one level of indirection */
1775 p1 = *pp1;
1776 p2 = *pp2;
1778 ORDERKEY_PCTCPU
1779 ORDERKEY_CPTICKS
1780 ORDERKEY_STATE
1781 ORDERKEY_PRIO
1782 ORDERKEY_RSSIZE
1783 ORDERKEY_MEM
1784 ORDERKEY_PID
1785 ORDERKEY_LWP
1788 return (result);
1791 /* compare_size - the comparison function for sorting by total memory usage */
1794 compare_size (struct prpsinfo **pp1, struct prpsinfo **pp2)
1797 register struct prpsinfo *p1;
1798 register struct prpsinfo *p2;
1799 register long result;
1800 double dresult;
1802 /* remove one level of indirection */
1803 p1 = *pp1;
1804 p2 = *pp2;
1806 ORDERKEY_MEM
1807 ORDERKEY_RSSIZE
1808 ORDERKEY_PCTCPU
1809 ORDERKEY_CPTICKS
1810 ORDERKEY_STATE
1811 ORDERKEY_PRIO
1812 ORDERKEY_PID
1813 ORDERKEY_LWP
1816 return (result);
1819 /* compare_res - the comparison function for sorting by resident set size */
1822 compare_res (struct prpsinfo **pp1, struct prpsinfo **pp2)
1825 register struct prpsinfo *p1;
1826 register struct prpsinfo *p2;
1827 register long result;
1828 double dresult;
1830 /* remove one level of indirection */
1831 p1 = *pp1;
1832 p2 = *pp2;
1834 ORDERKEY_RSSIZE
1835 ORDERKEY_MEM
1836 ORDERKEY_PCTCPU
1837 ORDERKEY_CPTICKS
1838 ORDERKEY_STATE
1839 ORDERKEY_PRIO
1840 ORDERKEY_PID
1841 ORDERKEY_LWP
1844 return (result);
1847 /* compare_time - the comparison function for sorting by total cpu time */
1850 compare_time (struct prpsinfo **pp1, struct prpsinfo **pp2)
1853 register struct prpsinfo *p1;
1854 register struct prpsinfo *p2;
1855 register long result;
1856 double dresult;
1858 /* remove one level of indirection */
1859 p1 = *pp1;
1860 p2 = *pp2;
1862 ORDERKEY_CPTICKS
1863 ORDERKEY_PCTCPU
1864 ORDERKEY_STATE
1865 ORDERKEY_PRIO
1866 ORDERKEY_MEM
1867 ORDERKEY_RSSIZE
1868 ORDERKEY_PID
1869 ORDERKEY_LWP
1872 return (result);
1875 /* compare_pid - the comparison function for sorting by process id */
1878 compare_pid (struct prpsinfo **pp1, struct prpsinfo **pp2)
1881 register struct prpsinfo *p1;
1882 register struct prpsinfo *p2;
1883 register long result;
1885 /* remove one level of indirection */
1886 p1 = *pp1;
1887 p2 = *pp2;
1889 ORDERKEY_PID
1890 ORDERKEY_LWP
1893 return (result);
1896 /* get process table */
1898 getptable (struct prpsinfo *baseptr)
1900 struct prpsinfo *currproc; /* pointer to current proc structure */
1901 #ifndef USE_NEW_PROC
1902 struct prstatus prstatus; /* for additional information */
1903 #endif
1904 int numprocs = 0;
1905 struct dirent *direntp;
1906 struct oldproc *op;
1907 hash_pos pos;
1908 hash_item_pid *hi;
1909 hash_item_pidthr *hip;
1910 prheader_t *prp;
1911 lwpsinfo_t *lwpp;
1912 pidthr_t pidthr;
1913 static struct timeval lasttime =
1914 {0, 0};
1915 struct timeval thistime;
1916 struct stat st;
1917 double timediff;
1919 gettimeofday (&thistime, NULL);
1921 * To avoid divides, we keep times in nanoseconds. This is
1922 * scaled by 1e7 rather than 1e9 so that when we divide we
1923 * get percent.
1925 if (lasttime.tv_sec)
1926 timediff = ((double) thistime.tv_sec * 1.0e7 +
1927 ((double) thistime.tv_usec * 10.0)) -
1928 ((double) lasttime.tv_sec * 1.0e7 +
1929 ((double) lasttime.tv_usec * 10.0));
1930 else
1931 timediff = 1.0e7;
1933 /* get our first procs pointer */
1934 currproc = procs_start();
1936 /* before reading /proc files, turn on root privs */
1937 /* (we don't care if this fails since it will be caught later) */
1938 #ifndef USE_NEW_PROC
1939 seteuid(0);
1940 #endif
1942 for (rewinddir (procdir); (direntp = readdir (procdir));)
1944 int fd;
1945 int pid;
1946 char buf[40];
1948 /* skip dot files */
1949 if (direntp->d_name[0] == '.')
1950 continue;
1952 /* convert pid to a number (and make sure its valid) */
1953 pid = atoi(direntp->d_name);
1954 if (pid <= 0)
1955 continue;
1957 /* fetch the old proc data */
1958 op = (struct oldproc *)hash_lookup_pid(prochash, pid);
1959 if (op == NULL)
1961 /* new proc: create an entry for it */
1962 op = (struct oldproc *)malloc(sizeof(struct oldproc));
1963 hash_add_pid(prochash, pid, (void *)op);
1964 op->pid = pid;
1965 op->fd_psinfo = -1;
1966 op->fd_lpsinfo = -1;
1967 op->oldtime = 0.0;
1970 /* do we have a cached file? */
1971 fd = op->fd_psinfo;
1972 if (fd == -1)
1974 /* no: open the psinfo file */
1975 snprintf(buf, sizeof(buf), "%s/psinfo", direntp->d_name);
1976 if ((fd = open(buf, O_RDONLY)) < 0)
1978 /* cleanup??? */
1979 continue;
1983 /* read data from the file */
1984 #ifdef USE_NEW_PROC
1985 if (pread(fd, currproc, sizeof(psinfo_t), 0) != sizeof(psinfo_t))
1987 (void) close (fd);
1988 op->fd_psinfo = -1;
1989 continue;
1991 #else
1992 if (ioctl(fd, PIOCPSINFO, currproc) < 0)
1994 (void) close(fd);
1995 op->fd_psinfo = -1;
1996 continue;
1999 if (ioctl(fd, PIOCSTATUS, &prstatus) < 0)
2001 /* not a show stopper -- just fill in the needed values */
2002 currproc->pr_fill = 0;
2003 currproc->px_onpro = 0;
2005 else
2007 /* copy over the values we need from prstatus */
2008 currproc->pr_fill = (short)prstatus.pr_nlwp;
2009 currproc->px_onpro = prstatus.pr_processor;
2011 #endif
2014 * We track our own cpu% usage.
2015 * We compute it based on CPU since the last update by calculating
2016 * the difference in cumulative cpu time and dividing by the amount
2017 * of time we measured between updates (timediff).
2018 * NOTE: Solaris 2.4 and higher do maintain CPU% in psinfo,
2019 * but it does not produce the kind of results we really want,
2020 * so we don't use it even though its there.
2022 if (lasttime.tv_sec > 0)
2024 percent_cpu(currproc) =
2025 (TIMESPEC_TO_DOUBLE(currproc->pr_time) - op->oldtime) / timediff;
2027 else
2029 /* first screen -- no difference is possible */
2030 percent_cpu(currproc) = 0.0;
2033 /* save data for next time */
2034 op->pid = currproc->pr_pid;
2035 op->oldtime = TIMESPEC_TO_DOUBLE(currproc->pr_time);
2036 op->owner_uid = currproc->pr_uid;
2037 op->seen = 1;
2039 /* cache the file descriptor if we can */
2040 if (fd < maxfiles)
2042 op->fd_psinfo = fd;
2044 else
2046 (void) close(fd);
2049 #ifdef USE_NEW_PROC
2050 /* collect up the threads */
2051 /* use cached lps file if it's there */
2052 fd = op->fd_lpsinfo;
2053 if (fd == -1)
2055 snprintf(buf, sizeof(buf), "%s/lpsinfo", direntp->d_name);
2056 fd = open(buf, O_RDONLY);
2059 /* make sure we have a valid descriptor and the file's current size */
2060 if (fd >= 0 && fstat(fd, &st) != -1)
2062 char *p;
2063 int i;
2065 /* read the whole file */
2066 p = malloc(st.st_size);
2067 (void)pread(fd, p, st.st_size, 0);
2069 /* cache the file descriptor if we can */
2070 if (fd < maxfiles)
2072 op->fd_lpsinfo = fd;
2074 else
2076 (void)close(fd);
2079 /* the file starts with a struct prheader */
2080 prp = (prheader_t *)p;
2081 p += sizeof(prheader_t);
2083 /* there are prp->pr_nent entries in the file */
2084 for (i = 0; i < prp->pr_nent; i++)
2086 /* process this entry */
2087 lwpp = (lwpsinfo_t *)p;
2088 p += prp->pr_entsize;
2090 /* fetch the old thread data */
2091 /* this hash is indexed by both pid and lwpid */
2092 pidthr.k_pid = currproc->pr_pid;
2093 pidthr.k_thr = lwpp->pr_lwpid;
2094 dprintf("getptable: processing %d.%d\n",
2095 pidthr.k_pid, pidthr.k_thr);
2096 op = (struct oldproc *)hash_lookup_pidthr(threadhash, pidthr);
2097 if (op == NULL)
2099 /* new thread: create an entry for it */
2100 op = (struct oldproc *)malloc(sizeof(struct oldproc));
2101 hash_add_pidthr(threadhash, pidthr, (void *)op);
2102 op->pid = pid;
2103 op->lwpid = lwpp->pr_lwpid;
2104 op->oldtime = 0.0;
2105 dprintf("getptable: %d.%d: new thread\n",
2106 pidthr.k_pid, pidthr.k_thr);
2109 /* are we showing individual threads? */
2110 if (show_threads)
2112 /* yes: if this is the first thread we reuse the proc
2113 entry we have, otherwise we create a new one by
2114 duping the current one */
2115 if (i > 0)
2117 currproc = procs_dup(currproc);
2118 numprocs++;
2121 /* yes: copy over thread-specific data */
2122 currproc->pr_time = lwpp->pr_time;
2123 currproc->px_state = lwpp->pr_state;
2124 currproc->px_pri = lwpp->pr_pri;
2125 currproc->px_onpro = lwpp->pr_onpro;
2126 currproc->pr_lwp.pr_lwpid = lwpp->pr_lwpid;
2128 /* calculate percent cpu for just this thread */
2129 if (lasttime.tv_sec > 0)
2131 percent_cpu(currproc) =
2132 (TIMESPEC_TO_DOUBLE(lwpp->pr_time) - op->oldtime) /
2133 timediff;
2135 else
2137 /* first screen -- no difference is possible */
2138 percent_cpu(currproc) = 0.0;
2141 dprintf("getptable: %d.%d: time %.0f, state %d, pctcpu %.2f\n",
2142 currproc->pr_pid, lwpp->pr_lwpid,
2143 TIMESPEC_TO_DOUBLE(currproc->pr_time),
2144 currproc->px_state, percent_cpu(currproc));
2147 /* save data for next time */
2148 op->oldtime = TIMESPEC_TO_DOUBLE(lwpp->pr_time);
2149 op->seen = 1;
2151 free(p);
2153 #endif
2155 /* move to next */
2156 numprocs++;
2157 currproc = procs_next();
2160 #ifndef USE_NEW_PROC
2161 /* turn off root privs */
2162 seteuid(getuid());
2163 #endif
2165 dprintf("getptable saw %d procs\n", numprocs);
2167 /* scan the hash tables and remove dead entries */
2168 hi = hash_first_pid(prochash, &pos);
2169 while (hi != NULL)
2171 op = (struct oldproc *)(hi->value);
2172 if (op->seen)
2174 op->seen = 0;
2176 else
2178 dprintf("removing %d from prochash\n", op->pid);
2179 if (op->fd_psinfo >= 0)
2181 (void)close(op->fd_psinfo);
2183 if (op->fd_lpsinfo >= 0)
2185 (void)close(op->fd_lpsinfo);
2187 hash_remove_pos_pid(&pos);
2188 free(op);
2190 hi = hash_next_pid(&pos);
2193 hip = hash_first_pidthr(threadhash, &pos);
2194 while (hip != NULL)
2196 op = (struct oldproc *)(hip->value);
2197 if (op->seen)
2199 op->seen = 0;
2201 else
2203 dprintf("removing %d from threadhash\n", op->pid);
2204 hash_remove_pos_pidthr(&pos);
2205 free(op);
2207 hip = hash_next_pidthr(&pos);
2210 lasttime = thistime;
2212 return numprocs;
2216 * proc_owner(pid) - returns the uid that owns process "pid", or -1 if
2217 * the process does not exist.
2218 * It is EXTREMLY IMPORTANT that this function work correctly.
2219 * If top runs setuid root (as in SVR4), then this function
2220 * is the only thing that stands in the way of a serious
2221 * security problem. It validates requests for the "kill"
2222 * and "renice" commands.
2225 proc_owner (int pid)
2227 struct oldproc *op;
2229 /* we keep this information in the hash table */
2230 op = (struct oldproc *)hash_lookup_pid(prochash, (pid_t)pid);
2231 if (op != NULL)
2233 return((int)(op->owner_uid));
2235 return(-1);
2238 /* older revisions don't supply a setpriority */
2239 #if (OSREV < 55)
2241 setpriority (int dummy, int who, int niceval)
2243 int scale;
2244 int prio;
2245 pcinfo_t pcinfo;
2246 pcparms_t pcparms;
2247 tsparms_t *tsparms;
2249 strcpy (pcinfo.pc_clname, "TS");
2250 if (priocntl (0, 0, PC_GETCID, (caddr_t) & pcinfo) == -1)
2251 return (-1);
2253 prio = niceval;
2254 if (prio > PRIO_MAX)
2255 prio = PRIO_MAX;
2256 else if (prio < PRIO_MIN)
2257 prio = PRIO_MIN;
2259 tsparms = (tsparms_t *) pcparms.pc_clparms;
2260 scale = ((tsinfo_t *) pcinfo.pc_clinfo)->ts_maxupri;
2261 tsparms->ts_uprilim = tsparms->ts_upri = -(scale * prio) / 20;
2262 pcparms.pc_cid = pcinfo.pc_cid;
2264 if (priocntl (P_PID, who, PC_SETPARMS, (caddr_t) & pcparms) == -1)
2265 return (-1);
2267 return (0);
2269 #endif