2 * top - a top users display for Unix
4 * SYNOPSIS: Any Sun running SunOS 5.x (Solaris 2.x)
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>
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
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)
61 #define px_state pr_state
62 #define px_oldpri pr_oldpri
63 #define px_nice pr_nice
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)
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>
93 #include <sys/procfs.h>
96 #include <sys/cpuvar.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>
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
127 #define UNIX "/dev/ksyms"
128 #define KMEM "/dev/kmem"
129 #define PROCFS "/proc"
139 #define FSHIFT 8 /* bits to right of fixed binary point */
140 #define FSCALE (1<<FSHIFT)
143 #define loaddouble(la) ((double)(la) / FSCALE)
144 #define dbl_align(x) (((unsigned long)(x)+(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 */
168 static struct nlist nlst
[] =
170 {"v"}, /* 0 */ /* replaced by dynamic allocation */
173 /* this structure really has some extra fields, but the first three match */
174 {"k_anoninfo"}, /* 2 */
176 {"anoninfo"}, /* 2 */
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
;
190 static kstat_ctl_t
*kc
= NULL
;
191 static kid_t kcid
= 0;
193 static unsigned long *cpu_offset
;
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: */
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.
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
238 unsigned int states
[CPUSTATES
];
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 */
265 int (*format
)(char *, int, struct prpsinfo
*);
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, ",
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 */
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 */
313 {"cpu", "size", "res", "time", "pid", NULL
};
315 /* forward definitions for comparison functions */
322 int (*proc_compares
[])() = {
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 */
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 */
345 extern void perror ();
346 extern int getptable ();
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
},
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
;
485 while (fp
->name
!= NULL
)
487 if (strcmp(col
, fp
->name
) == 0)
499 field_subst(int *fp
, int old
, int new)
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
)
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
,
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");
561 fprintf (stderr
, "top: kvm_read for %s: %s\n", refstr
, strerror(errno
));
566 dprintf("getkval read %d (%08x)\n", *ptr
);
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.
587 procs_prealloc(int max
)
591 struct prpsinfo
*new;
592 struct prpsinfo
**pp
;
594 cnt
= max
- maxprocs
;
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);
607 new = (struct prpsinfo
*) ((char *)new + PRPSINFOSIZE
);
609 dprintf("procs_prealloc: done filling at %08x\n", new);
615 * struct prpsinfo *procs_next()
617 * Return the next available procs structure, allocating a new one
625 if (idxprocs
>= maxprocs
)
627 /* allocate some more */
628 procs_prealloc(maxprocs
+ 128);
635 procs_dup(struct prpsinfo
*p
)
641 memcpy(n
, p
, PRPSINFOSIZE
);
646 * struct prpsinfo *procs_start()
648 * Return the first procs structure.
663 * Return the maximum number of procs structures currently allocated.
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
)
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 */
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
);
703 format_header (register char *uname_field
)
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
));
728 return ((long)(kn
->value
.ui32
));
733 kstat_safe_retrieve(kstat_t
**ksp
,
734 char *module
, int instance
, char *name
, void *buf
)
741 dprintf("kstat_safe_retrieve(%08x -> %08x, %s, %d, %s, %08x)\n",
742 ksp
, *ksp
, module
, instance
, name
, buf
);
747 /* if we dont already have the kstat, retrieve it */
750 if ((ks
= kstat_lookup(kc
, module
, instance
, name
)) == NULL
)
757 /* attempt to read it */
758 new_kcid
= kstat_read(kc
, ks
, buf
);
759 /* chance for an infinite loop here if kstat_read keeps
762 /* if the chain changed, update it */
763 if (new_kcid
!= kcid
)
765 dprintf("kstat_safe_retrieve: chain changed to %d...updating\n",
768 kcid
= kstat_chain_update(kc
);
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
)
798 dprintf("kstat_safe_namematch(%d, %08x, %s, %08x, %d)\n",
799 num
, ksparg
, name
, buf
, size
);
801 namelen
= strlen(name
);
804 /* initialize before the scan */
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 */
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",
827 kcid
= kstat_chain_update(kc
);
829 /* there's no sense in continuing the scan */
830 /* so break out of the for loop */
834 /* move to the next buffers */
842 dprintf("kstat_safe_namematch returns %d\n", count
);
847 static kstat_t
*ks_system_misc
= NULL
;
849 #endif /* USE_KSTAT */
853 get_avenrun(int avenrun
[3])
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
);
881 #else /* !USE_KSTAT */
883 (void) getkval (avenrun_offset
, (int *) avenrun
, sizeof (int [3]), "avenrun");
887 #endif /* USE_KSTAT */
898 if ((kn
= kstat_data_lookup(ks_system_misc
, "ncpus")) != NULL
)
900 ret
= (int)(kn
->value
.ui32
);
907 (void) getkval(nlst
[X_NCPUS
].n_value
, (int *)(&ret
), sizeof(ret
), "ncpus");
920 if ((kn
= kstat_data_lookup(ks_system_misc
, "nproc")) != NULL
)
922 ret
= (int)(kn
->value
.ui32
);
927 (void) getkval (nproc_offset
, (int *) (&ret
), sizeof (ret
), "nproc");
930 dprintf("get_nproc returns %d\n", ret
);
935 get_cpustats(int *cnt
, struct cpustats
*cpustats
)
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
;
944 struct cpustats
*cpustats_p
;
946 dprintf("get_cpustats(%d -> %d, %08x)\n", cnt
, *cnt
, cpustats
);
949 (cpu_num
= kstat_safe_namematch(nelems
,
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
);
963 cpu_ks
= (kstat_t
**)calloc(nelems
, sizeof(kstat_t
*));
964 if (cpu_stat
!= NULL
)
968 cpu_stat
= (cpu_stat_t
*)malloc(nelems
* sizeof(cpu_stat_t
));
971 /* do we have more cpus than our caller? */
974 /* yes, so realloc their array, too */
975 dprintf("realloc array from %d to %d\n", *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
;
1012 dprintf("get_cpustats sees %d cpus and returns %08x\n", cpucount
, cpustats
);
1015 #else /* !USE_KSTAT */
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
);
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
];
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
)
1065 static kstat_t
*ks
= NULL
;
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 */
1076 (void) getkval(freemem_offset
, (int *)(&freemem
), sizeof(freemem
),
1082 /* only need to grab kstat chain once */
1085 ks
= kstat_lookup(kc
, "unix", 0, "system_pages");
1089 kstat_read(kc
, ks
, 0) != -1 &&
1090 (kn
= kstat_data_lookup(ks
, "freemem")) != NULL
)
1092 freemem
= kstat_data_value_l(kn
);
1103 *fr
= freemem
== -1 ? -1 : pagetok(freemem
);
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
;
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 */
1130 /* get total number of swap entries */
1131 if ((cnt
= swapctl(SC_GETNSWP
, 0)) == -1)
1136 /* allocate enough space to hold count + n swapents */
1137 swt
= (struct swaptable
*)malloc(sizeof(int) +
1138 cnt
* sizeof(struct swapent
));
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]);
1151 ste
++->ste_path
= path
;
1154 /* grab all swap info */
1155 if (swapctl(SC_LIST
, swt
) == -1)
1160 /* walk thru the structs and sum up the fields */
1162 ste
= &(swt
->swt_ent
[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
;
1176 /* fill in the results */
1177 *total
= pagetok(t
);
1186 machine_init (struct statics
*statics
)
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
1222 seteuid(getuid()); /* super user not needed for NEW_PROC */
1226 if ((kc
= kstat_open()) == NULL
)
1228 fprintf(stderr
, "Unable to open kstat.\n");
1231 kcid
= kc
->kc_chain_id
;
1232 dprintf("kstat_open: chain %d\n", kcid
);
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;
1246 ut
.ut_type
= BOOT_TIME
;
1247 if ((up
= getutxid(&ut
)) != NULL
)
1249 statics
->boottime
= up
->ut_tv
.tv_sec
;
1253 /* if the kvm_open succeeded, get the nlist */
1256 if (kvm_nlist (kd
, nlst
) < 0)
1258 perror ("kvm_nlist");
1261 if (check_nlist (nlst
) != 0)
1265 /* if KSTAT is not available to us and we can't open /dev/kmem,
1266 this is a serious problem.
1270 /* Print the error message here */
1271 (void) fprintf(stderr
, "kvm_open: %s\n", p
);
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
;
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)
1298 /* we need to get the current nproc */
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
);
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
);
1314 while ((i
>>= 1) > 0)
1319 /* calculate an amount to shift to K values */
1320 /* remember that log base 2 of 1024 is 10 (i.e.: 2^10 = 1024) */
1323 /* now determine which pageshift function is appropriate for the
1324 result (have to because x << y is undefined for y < 0) */
1327 /* this is the most likely */
1328 p_pagetok
= pagetok_left
;
1330 else if (pageshift
== 0)
1332 p_pagetok
= pagetok_none
;
1336 p_pagetok
= pagetok_right
;
1337 pageshift
= -pageshift
;
1340 /* we cache open files to improve performance, so we need to up
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 */
1353 /* set up the display indices */
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");
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");
1381 if (!(procdir
= opendir (PROCFS
)))
1383 (void) fprintf (stderr
, "Unable to open %s\n", PROCFS
);
1388 { /* handy for later on when we're reading it */
1389 (void) fprintf (stderr
, "Unable to chdir to %s\n", PROCFS
);
1398 get_system_info (struct system_info
*si
)
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;
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 */
1447 (void) getkval(mpid_offset
, &(si
->last_pid
), sizeof (si
->last_pid
), "mpid");
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;
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
;
1493 struct system_info
*si
,
1494 struct process_select
*sel
,
1498 register int total_procs
;
1499 register int active_procs
;
1500 register struct prpsinfo
**prefp
;
1501 register struct prpsinfo
*pp
;
1505 /* these are copied out of sel for speed */
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
)
1540 pref
= (struct prpsinfo
**)malloc(nproc
* sizeof(struct prpsinfo
*));
1541 dprintf("get_process_info: allocated %d prinfo pointers at %08x\n",
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 */
1552 (void) memset (process_states
, 0, sizeof (process_states
));
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)))
1570 state
= (int)pp
->px_state
;
1571 if (state
> 0 && state
< PROCSTATES
)
1573 process_states
[state
]++;
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
))
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
];
1612 format_process_header(struct process_select
*sel
, caddr_t handle
, int count
)
1618 struct proc_field
*fp
;
1620 /* check for null handle */
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
)
1636 field_subst(fi
, FIELD_USERNAME
, FIELD_UID
);
1640 /* display usernames */
1641 field_subst(fi
, FIELD_UID
, FIELD_USERNAME
);
1644 /* walk thru fields and construct header */
1645 /* are we worried about overflow??? */
1649 fp
= &(proc_field
[*fi
++]);
1650 if (fp
->min_screenwidth
<= cols
)
1652 p
+= sprintf(p
, fp
->rjust
? "%*s" : "%-*s", fp
->width
, fp
->name
);
1661 static char fmt
[MAX_COLS
]; /* static area where result is built */
1664 format_next_process(caddr_t handle
, char *(*get_userid
)(int))
1667 struct prpsinfo
*pp
;
1669 struct proc_field
*fp
;
1677 /* find and remember the next proc structure */
1678 hp
= (struct handle
*)handle
;
1679 pp
= *(hp
->next_proc
++);
1682 /* grab format descriptor */
1683 fi
= display_fields
;
1685 /* screen width is a consideration, too */
1686 cols
= display_columns();
1688 /* build output by field */
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
);
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
));
1714 /* return the result */
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
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
[] =
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
;
1774 /* remove one level of indirection */
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
;
1802 /* remove one level of indirection */
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
;
1830 /* remove one level of indirection */
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
;
1858 /* remove one level of indirection */
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 */
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 */
1905 struct dirent
*direntp
;
1909 hash_item_pidthr
*hip
;
1913 static struct timeval lasttime
=
1915 struct timeval thistime
;
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
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));
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
1942 for (rewinddir (procdir
); (direntp
= readdir (procdir
));)
1948 /* skip dot files */
1949 if (direntp
->d_name
[0] == '.')
1952 /* convert pid to a number (and make sure its valid) */
1953 pid
= atoi(direntp
->d_name
);
1957 /* fetch the old proc data */
1958 op
= (struct oldproc
*)hash_lookup_pid(prochash
, pid
);
1961 /* new proc: create an entry for it */
1962 op
= (struct oldproc
*)malloc(sizeof(struct oldproc
));
1963 hash_add_pid(prochash
, pid
, (void *)op
);
1966 op
->fd_lpsinfo
= -1;
1970 /* do we have a cached file? */
1974 /* no: open the psinfo file */
1975 snprintf(buf
, sizeof(buf
), "%s/psinfo", direntp
->d_name
);
1976 if ((fd
= open(buf
, O_RDONLY
)) < 0)
1983 /* read data from the file */
1985 if (pread(fd
, currproc
, sizeof(psinfo_t
), 0) != sizeof(psinfo_t
))
1992 if (ioctl(fd
, PIOCPSINFO
, currproc
) < 0)
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;
2007 /* copy over the values we need from prstatus */
2008 currproc
->pr_fill
= (short)prstatus
.pr_nlwp
;
2009 currproc
->px_onpro
= prstatus
.pr_processor
;
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
;
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
;
2039 /* cache the file descriptor if we can */
2050 /* collect up the threads */
2051 /* use cached lps file if it's there */
2052 fd
= op
->fd_lpsinfo
;
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)
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 */
2072 op
->fd_lpsinfo
= 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
);
2099 /* new thread: create an entry for it */
2100 op
= (struct oldproc
*)malloc(sizeof(struct oldproc
));
2101 hash_add_pidthr(threadhash
, pidthr
, (void *)op
);
2103 op
->lwpid
= lwpp
->pr_lwpid
;
2105 dprintf("getptable: %d.%d: new thread\n",
2106 pidthr
.k_pid
, pidthr
.k_thr
);
2109 /* are we showing individual 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 */
2117 currproc
= procs_dup(currproc
);
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
) /
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
);
2157 currproc
= procs_next();
2160 #ifndef USE_NEW_PROC
2161 /* turn off root privs */
2165 dprintf("getptable saw %d procs\n", numprocs
);
2167 /* scan the hash tables and remove dead entries */
2168 hi
= hash_first_pid(prochash
, &pos
);
2171 op
= (struct oldproc
*)(hi
->value
);
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
);
2190 hi
= hash_next_pid(&pos
);
2193 hip
= hash_first_pidthr(threadhash
, &pos
);
2196 op
= (struct oldproc
*)(hip
->value
);
2203 dprintf("removing %d from threadhash\n", op
->pid
);
2204 hash_remove_pos_pidthr(&pos
);
2207 hip
= hash_next_pidthr(&pos
);
2210 lasttime
= thistime
;
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
)
2229 /* we keep this information in the hash table */
2230 op
= (struct oldproc
*)hash_lookup_pid(prochash
, (pid_t
)pid
);
2233 return((int)(op
->owner_uid
));
2238 /* older revisions don't supply a setpriority */
2241 setpriority (int dummy
, int who
, int niceval
)
2249 strcpy (pcinfo
.pc_clname
, "TS");
2250 if (priocntl (0, 0, PC_GETCID
, (caddr_t
) & pcinfo
) == -1)
2254 if (prio
> PRIO_MAX
)
2256 else if (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)