1 | /* |
---|
2 | * top - a top users display for Unix |
---|
3 | * |
---|
4 | * SYNOPSIS: Any Sun running SunOS 5.x (Solaris 2.x) |
---|
5 | * |
---|
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 the following systems: |
---|
10 | * SunOS 5.0 (not tested) |
---|
11 | * SunOS 5.1 |
---|
12 | * SunOS 5.2 |
---|
13 | * SunOS 5.3 |
---|
14 | * SunOS 5.4 |
---|
15 | * SunOS 5.5 |
---|
16 | * SunOS 5.6 |
---|
17 | * SunOS 5.7 |
---|
18 | * |
---|
19 | * Tested on a SPARCclassic with SunOS 5.1, using gcc-2.3.3, and |
---|
20 | * SPARCsystem 600 with SunOS 5.2, using Sun C |
---|
21 | * |
---|
22 | * LIBS: -lelf -lkvm -lkstat |
---|
23 | * |
---|
24 | * CFLAGS: -DHAVE_GETOPT -DORDER -DHAVE_STRERROR |
---|
25 | * |
---|
26 | * |
---|
27 | * AUTHORS: Torsten Kasch <torsten@techfak.uni-bielefeld.de> |
---|
28 | * Robert Boucher <boucher@sofkin.ca> |
---|
29 | * CONTRIBUTORS: Marc Cohen <marc@aai.com> |
---|
30 | * Charles Hedrick <hedrick@geneva.rutgers.edu> |
---|
31 | * William L. Jones <jones@chpc> |
---|
32 | * Petri Kutvonen <kutvonen@cs.helsinki.fi> |
---|
33 | * Casper Dik <casper.dik@sun.com> |
---|
34 | * Tim Pugh <tpugh@oce.orst.edu> |
---|
35 | */ |
---|
36 | |
---|
37 | #define _KMEMUSER |
---|
38 | |
---|
39 | #if (OSREV == 551) |
---|
40 | #undef OSREV |
---|
41 | #define OSREV 55 |
---|
42 | #endif |
---|
43 | |
---|
44 | #define USE_NEW_PROC |
---|
45 | #if defined(USE_NEW_PROC) && OSREV >= 56 |
---|
46 | #define _STRUCTURED_PROC 1 |
---|
47 | #define prpsinfo psinfo |
---|
48 | #include <sys/procfs.h> |
---|
49 | #define pr_fill pr_nlwp |
---|
50 | /* These require an ANSI C compiler "Reisser cpp" doesn't like this */ |
---|
51 | #define pr_state pr_lwp.pr_state |
---|
52 | #define pr_oldpri pr_lwp.pr_oldpri |
---|
53 | #define pr_nice pr_lwp.pr_nice |
---|
54 | #define pr_pri pr_lwp.pr_pri |
---|
55 | #define pr_onpro pr_lwp.pr_onpro |
---|
56 | #define ZOMBIE(p) ((p)->pr_nlwp == 0) |
---|
57 | #define SIZE_K(p) ((p)->pr_size) |
---|
58 | #define RSS_K(p) ((p)->pr_rssize) |
---|
59 | #else |
---|
60 | #undef USE_NEW_PROC |
---|
61 | #define ZOMBIE(p) ((p)->pr_zomb) |
---|
62 | #define SIZE_K(p) ((p)->pr_bysize/1024) |
---|
63 | #define RSS_K(p) ((p)->pr_byrssize/1024) |
---|
64 | #define pr_onpro pr_filler[5] |
---|
65 | #endif |
---|
66 | |
---|
67 | #include "top.h" |
---|
68 | #include "machine.h" |
---|
69 | #include "utils.h" |
---|
70 | #include <stdio.h> |
---|
71 | #include <fcntl.h> |
---|
72 | #include <unistd.h> |
---|
73 | #include <stdlib.h> |
---|
74 | #include <errno.h> |
---|
75 | #include <dirent.h> |
---|
76 | #include <nlist.h> |
---|
77 | #include <string.h> |
---|
78 | #include <kvm.h> |
---|
79 | #include <sys/types.h> |
---|
80 | #include <sys/param.h> |
---|
81 | #include <sys/signal.h> |
---|
82 | #include <sys/fault.h> |
---|
83 | #include <sys/sysinfo.h> |
---|
84 | #include <sys/sysmacros.h> |
---|
85 | #include <sys/syscall.h> |
---|
86 | #include <sys/user.h> |
---|
87 | #include <sys/proc.h> |
---|
88 | #include <sys/procfs.h> |
---|
89 | #include <sys/vm.h> |
---|
90 | #include <sys/var.h> |
---|
91 | #include <sys/cpuvar.h> |
---|
92 | #include <sys/file.h> |
---|
93 | #include <sys/time.h> |
---|
94 | #include <sys/priocntl.h> |
---|
95 | #include <sys/tspriocntl.h> |
---|
96 | #include <sys/processor.h> |
---|
97 | #include <sys/swap.h> |
---|
98 | #include <vm/anon.h> |
---|
99 | #include <math.h> |
---|
100 | |
---|
101 | #if OSREV >= 53 |
---|
102 | #define USE_KSTAT |
---|
103 | #endif |
---|
104 | #ifdef USE_KSTAT |
---|
105 | #include <kstat.h> |
---|
106 | /* |
---|
107 | * Some kstats are fixed at 32 bits, these will be specified as ui32; some |
---|
108 | * are "natural" size (32 bit on 32 bit Solaris, 64 on 64 bit Solaris |
---|
109 | * we'll make those unsigned long) |
---|
110 | * Older Solaris doesn't define KSTAT_DATA_UINT32, those are always 32 bit. |
---|
111 | */ |
---|
112 | # ifndef KSTAT_DATA_UINT32 |
---|
113 | # define ui32 ul |
---|
114 | # endif |
---|
115 | #endif |
---|
116 | |
---|
117 | #ifdef SC_AINFO |
---|
118 | #undef USE_ANONINFO /* Use swapctl() instead */ |
---|
119 | #endif |
---|
120 | |
---|
121 | #define UNIX "/dev/ksyms" |
---|
122 | #define KMEM "/dev/kmem" |
---|
123 | #define PROCFS "/proc" |
---|
124 | #define CPUSTATES 5 |
---|
125 | #ifndef PRIO_MIN |
---|
126 | #define PRIO_MIN -20 |
---|
127 | #endif |
---|
128 | #ifndef PRIO_MAX |
---|
129 | #define PRIO_MAX 20 |
---|
130 | #endif |
---|
131 | |
---|
132 | #ifndef FSCALE |
---|
133 | #define FSHIFT 8 /* bits to right of fixed binary point */ |
---|
134 | #define FSCALE (1<<FSHIFT) |
---|
135 | #endif /* FSCALE */ |
---|
136 | |
---|
137 | #define loaddouble(la) ((double)(la) / FSCALE) |
---|
138 | #define dbl_align(x) (((unsigned long)(x)+(sizeof(double)-1)) & \ |
---|
139 | ~(sizeof(double)-1)) |
---|
140 | #if (OSREV >= 54) |
---|
141 | /* |
---|
142 | * snarfed from <sys/procfs.h>: |
---|
143 | * The following percent numbers are 16-bit binary |
---|
144 | * fractions [0 .. 1] with the binary point to the |
---|
145 | * right of the high-order bit (one == 0x8000) |
---|
146 | */ |
---|
147 | #define percent_cpu(pp) (((double)pp->pr_pctcpu)/0x8000*100) |
---|
148 | #define weighted_cpu(pp) (*(double *)dbl_align(pp->pr_filler)) |
---|
149 | #else |
---|
150 | #define percent_cpu(pp) (*(double *)dbl_align(&pp->pr_filler[0])) |
---|
151 | #define weighted_cpu(pp) (*(double *)dbl_align(&pp->pr_filler[2])) |
---|
152 | #endif |
---|
153 | |
---|
154 | /* definitions for indices in the nlist array */ |
---|
155 | #define X_V 0 |
---|
156 | #define X_MPID 1 |
---|
157 | #define X_ANONINFO 2 |
---|
158 | #define X_MAXMEM 3 |
---|
159 | #define X_SWAPFS_MINFREE 4 |
---|
160 | #define X_FREEMEM 5 |
---|
161 | #define X_AVAILRMEM 6 |
---|
162 | #define X_AVENRUN 7 |
---|
163 | #define X_CPU 8 |
---|
164 | #define X_NPROC 9 |
---|
165 | #define X_NCPUS 10 |
---|
166 | |
---|
167 | static struct nlist nlst[] = |
---|
168 | { |
---|
169 | {"v"}, /* 0 */ /* replaced by dynamic allocation */ |
---|
170 | {"mpid"}, /* 1 */ |
---|
171 | #if OSREV >= 56 |
---|
172 | /* this structure really has some extra fields, but the first three match */ |
---|
173 | {"k_anoninfo"}, /* 2 */ |
---|
174 | #else |
---|
175 | {"anoninfo"}, /* 2 */ |
---|
176 | #endif |
---|
177 | {"maxmem"}, /* 3 */ /* use sysconf */ |
---|
178 | {"swapfs_minfree"}, /* 4 */ /* used only w/ USE_ANONINFO */ |
---|
179 | {"freemem"}, /* 5 */ /* available from kstat >= 2.5 */ |
---|
180 | {"availrmem"}, /* 6 */ /* available from kstat >= 2.5 */ |
---|
181 | {"avenrun"}, /* 7 */ /* available from kstat */ |
---|
182 | {"cpu"}, /* 8 */ /* available from kstat */ |
---|
183 | {"nproc"}, /* 9 */ /* available from kstat */ |
---|
184 | {"ncpus"}, /* 10 */ /* available from kstat */ |
---|
185 | {0} |
---|
186 | }; |
---|
187 | |
---|
188 | static unsigned long avenrun_offset; |
---|
189 | static unsigned long mpid_offset; |
---|
190 | #ifdef USE_KSTAT |
---|
191 | #define NO_NPROC |
---|
192 | static kstat_ctl_t *kc = NULL; |
---|
193 | static kstat_t **cpu_ks; |
---|
194 | static cpu_stat_t *cpu_stat; |
---|
195 | #else |
---|
196 | static unsigned long *cpu_offset; |
---|
197 | #endif |
---|
198 | static unsigned long nproc_offset; |
---|
199 | static unsigned long freemem_offset; |
---|
200 | static unsigned long maxmem_offset; |
---|
201 | static unsigned long availrmem_offset; |
---|
202 | static unsigned long swapfs_minfree_offset; |
---|
203 | static unsigned long anoninfo_offset; |
---|
204 | static void reallocproc(int n); |
---|
205 | static int maxprocs; |
---|
206 | |
---|
207 | /* get_process_info passes back a handle. This is what it looks like: */ |
---|
208 | struct handle |
---|
209 | { |
---|
210 | struct prpsinfo **next_proc;/* points to next valid proc pointer */ |
---|
211 | int remaining; /* number of pointers remaining */ |
---|
212 | }; |
---|
213 | |
---|
214 | /* |
---|
215 | * Structure for keeping track of CPU times from last time around |
---|
216 | * the program. We keep these things in a hash table, which is |
---|
217 | * recreated at every cycle. |
---|
218 | */ |
---|
219 | struct oldproc |
---|
220 | { |
---|
221 | pid_t oldpid; |
---|
222 | double oldtime; |
---|
223 | double oldpct; |
---|
224 | }; |
---|
225 | int oldprocs; /* size of table */ |
---|
226 | #define HASH(x) ((x << 1) % oldprocs) |
---|
227 | |
---|
228 | /* |
---|
229 | * GCC assumes that all doubles are aligned. Unfortunately it |
---|
230 | * doesn't round up the structure size to be a multiple of 8. |
---|
231 | * Thus we'll get a coredump when going through array. The |
---|
232 | * following is a size rounded up to 8. |
---|
233 | */ |
---|
234 | #define PRPSINFOSIZE dbl_align(sizeof(struct prpsinfo)) |
---|
235 | |
---|
236 | /* |
---|
237 | * These definitions control the format of the per-process area |
---|
238 | */ |
---|
239 | #if OSREV >= 58 |
---|
240 | static char header[] = |
---|
241 | " PID X THR PRI NICE SIZE RES STATE TIME CPU COMMAND"; |
---|
242 | /* 0123456 -- field to fill in starts at header+6 */ |
---|
243 | #define UNAME_START 7 |
---|
244 | |
---|
245 | #define Proc_format \ |
---|
246 | "%6d %-8.8s %3d %3d %4d %5s %5s %-6s %6s %5.2f%% %s" |
---|
247 | #else |
---|
248 | static char header[] = |
---|
249 | " PID X THR PRI NICE SIZE RES STATE TIME CPU COMMAND"; |
---|
250 | /* 0123456 -- field to fill in starts at header+6 */ |
---|
251 | #define UNAME_START 6 |
---|
252 | |
---|
253 | #define Proc_format \ |
---|
254 | "%5d %-8.8s %3d %3d %4d %5s %5s %-6s %6s %5.2f%% %s" |
---|
255 | #endif |
---|
256 | |
---|
257 | /* process state names for the "STATE" column of the display */ |
---|
258 | char *state_abbrev[] = |
---|
259 | {"", "sleep", "run", "zombie", "stop", "start", "cpu", "swap"}; |
---|
260 | |
---|
261 | int process_states[8]; |
---|
262 | char *procstatenames[] = |
---|
263 | { |
---|
264 | "", " sleeping, ", " running, ", " zombie, ", " stopped, ", |
---|
265 | " starting, ", " on cpu, ", " swapped, ", |
---|
266 | NULL |
---|
267 | }; |
---|
268 | |
---|
269 | int cpu_states[CPUSTATES]; |
---|
270 | char *cpustatenames[] = |
---|
271 | {"idle", "user", "kernel", "iowait", "swap", NULL}; |
---|
272 | #define CPUSTATE_IOWAIT 3 |
---|
273 | #define CPUSTATE_SWAP 4 |
---|
274 | |
---|
275 | |
---|
276 | /* these are for detailing the memory statistics */ |
---|
277 | int memory_stats[5]; |
---|
278 | char *memorynames[] = |
---|
279 | {"K real, ", "K active, ", "K free, ", "K swap in use, ", "K swap free", NULL}; |
---|
280 | |
---|
281 | /* these are names given to allowed sorting orders -- first is default */ |
---|
282 | char *ordernames[] = |
---|
283 | {"cpu", "size", "res", "time", NULL}; |
---|
284 | |
---|
285 | /* forward definitions for comparison functions */ |
---|
286 | int compare_cpu(); |
---|
287 | int compare_size(); |
---|
288 | int compare_res(); |
---|
289 | int compare_time(); |
---|
290 | |
---|
291 | int (*proc_compares[])() = { |
---|
292 | compare_cpu, |
---|
293 | compare_size, |
---|
294 | compare_res, |
---|
295 | compare_time, |
---|
296 | NULL }; |
---|
297 | |
---|
298 | kvm_t *kd; |
---|
299 | static DIR *procdir; |
---|
300 | static int nproc; |
---|
301 | static int ncpus; |
---|
302 | |
---|
303 | /* these are for keeping track of the proc array */ |
---|
304 | static int bytes; |
---|
305 | static struct prpsinfo *pbase; |
---|
306 | static struct prpsinfo **pref; |
---|
307 | static struct oldproc *oldbase; |
---|
308 | |
---|
309 | /* pagetok function is really a pointer to an appropriate function */ |
---|
310 | static int pageshift; |
---|
311 | static int (*p_pagetok) (); |
---|
312 | #define pagetok(size) ((*p_pagetok)(size)) |
---|
313 | |
---|
314 | /* useful externals */ |
---|
315 | extern char *myname; |
---|
316 | extern int check_nlist (); |
---|
317 | extern int gettimeofday (); |
---|
318 | extern int getkval (); |
---|
319 | extern void perror (); |
---|
320 | extern void getptable (); |
---|
321 | extern void quit (); |
---|
322 | extern int nlist (); |
---|
323 | |
---|
324 | int pagetok_none(int size) |
---|
325 | |
---|
326 | { |
---|
327 | return(size); |
---|
328 | } |
---|
329 | |
---|
330 | int pagetok_left(int size) |
---|
331 | |
---|
332 | { |
---|
333 | return(size << pageshift); |
---|
334 | } |
---|
335 | |
---|
336 | int pagetok_right(int size) |
---|
337 | |
---|
338 | { |
---|
339 | return(size >> pageshift); |
---|
340 | } |
---|
341 | |
---|
342 | int |
---|
343 | machine_init (struct statics *statics) |
---|
344 | { |
---|
345 | static struct var v; |
---|
346 | struct oldproc *op, *endbase; |
---|
347 | int i; |
---|
348 | char *p; |
---|
349 | #ifndef USE_KSTAT |
---|
350 | int offset; |
---|
351 | #endif |
---|
352 | |
---|
353 | /* There's a buffer overflow bug in curses that can be exploited when |
---|
354 | we run as root. By making sure that TERMINFO is set to something |
---|
355 | this bug is avoided. This code thanks to Casper */ |
---|
356 | if ((p = getenv("TERMINFO")) == NULL || *p == '\0') |
---|
357 | { |
---|
358 | putenv("TERMINFO=/usr/share/lib/terminfo/"); |
---|
359 | } |
---|
360 | |
---|
361 | /* perform the kvm_open - suppress error here */ |
---|
362 | kd = kvm_open (NULL, NULL, NULL, O_RDONLY, NULL); |
---|
363 | |
---|
364 | /* |
---|
365 | * turn off super group/user privs - but beware; we might |
---|
366 | * want the privs back later and we still have a fd to |
---|
367 | * /dev/kmem open so we can't use setgid()/setuid() as that |
---|
368 | * would allow a debugger to attach to this process. CD |
---|
369 | */ |
---|
370 | setegid(getgid()); |
---|
371 | seteuid(getuid()); /* super user not needed for NEW_PROC */ |
---|
372 | |
---|
373 | /* fill in the statics information */ |
---|
374 | statics->procstate_names = procstatenames; |
---|
375 | statics->cpustate_names = cpustatenames; |
---|
376 | statics->memory_names = memorynames; |
---|
377 | statics->order_names = ordernames; |
---|
378 | |
---|
379 | /* |
---|
380 | * test kvm_open return value - print error message and exit |
---|
381 | * when we need to read kernel memory (older releases) |
---|
382 | */ |
---|
383 | if (kd == NULL) |
---|
384 | { |
---|
385 | #ifndef USE_KSTAT |
---|
386 | /* Print the failure message here */ |
---|
387 | (void) kvm_open (NULL, NULL, NULL, O_RDONLY, "top"); |
---|
388 | perror("kvm_open"); |
---|
389 | return (-1); |
---|
390 | #endif |
---|
391 | } |
---|
392 | if (kd) |
---|
393 | { |
---|
394 | if (kvm_nlist (kd, nlst) < 0) |
---|
395 | { |
---|
396 | perror ("kvm_nlist"); |
---|
397 | return (-1); |
---|
398 | } |
---|
399 | if (check_nlist (nlst) != 0) |
---|
400 | return (-1); |
---|
401 | } |
---|
402 | |
---|
403 | #ifndef NO_NPROC |
---|
404 | /* NPROC Tuning parameter for max number of processes */ |
---|
405 | (void) getkval (nlst[X_V].n_value, &v, sizeof (struct var), nlst[X_V].n_name); |
---|
406 | nproc = v.v_proc; |
---|
407 | |
---|
408 | reallocproc(nproc); |
---|
409 | #endif |
---|
410 | |
---|
411 | /* stash away certain offsets for later use */ |
---|
412 | mpid_offset = nlst[X_MPID].n_value; |
---|
413 | nproc_offset = nlst[X_NPROC].n_value; |
---|
414 | avenrun_offset = nlst[X_AVENRUN].n_value; |
---|
415 | anoninfo_offset = nlst[X_ANONINFO].n_value; |
---|
416 | freemem_offset = nlst[X_FREEMEM].n_value; |
---|
417 | maxmem_offset = nlst[X_MAXMEM].n_value; |
---|
418 | availrmem_offset = nlst[X_AVAILRMEM].n_value; |
---|
419 | swapfs_minfree_offset = nlst[X_SWAPFS_MINFREE].n_value; |
---|
420 | |
---|
421 | |
---|
422 | #ifndef USE_KSTAT |
---|
423 | (void) getkval (nlst[X_NCPUS].n_value, (int *) (&ncpus), |
---|
424 | sizeof (ncpus), "ncpus"); |
---|
425 | |
---|
426 | cpu_offset = (unsigned long *) malloc (ncpus * sizeof (unsigned long)); |
---|
427 | for (i = offset = 0; i < ncpus; offset += sizeof(unsigned long)) { |
---|
428 | (void) getkval (nlst[X_CPU].n_value + offset, |
---|
429 | &cpu_offset[i], sizeof (unsigned long), |
---|
430 | nlst[X_CPU].n_name ); |
---|
431 | if (cpu_offset[i] != 0) |
---|
432 | i++; |
---|
433 | } |
---|
434 | #endif |
---|
435 | |
---|
436 | /* calculate pageshift value */ |
---|
437 | i = sysconf(_SC_PAGESIZE); |
---|
438 | pageshift = 0; |
---|
439 | while ((i >>= 1) > 0) |
---|
440 | { |
---|
441 | pageshift++; |
---|
442 | } |
---|
443 | |
---|
444 | /* calculate an amount to shift to K values */ |
---|
445 | /* remember that log base 2 of 1024 is 10 (i.e.: 2^10 = 1024) */ |
---|
446 | pageshift -= 10; |
---|
447 | |
---|
448 | /* now determine which pageshift function is appropriate for the |
---|
449 | result (have to because x << y is undefined for y < 0) */ |
---|
450 | if (pageshift > 0) |
---|
451 | { |
---|
452 | /* this is the most likely */ |
---|
453 | p_pagetok = pagetok_left; |
---|
454 | } |
---|
455 | else if (pageshift == 0) |
---|
456 | { |
---|
457 | p_pagetok = pagetok_none; |
---|
458 | } |
---|
459 | else |
---|
460 | { |
---|
461 | p_pagetok = pagetok_right; |
---|
462 | pageshift = -pageshift; |
---|
463 | } |
---|
464 | |
---|
465 | if (!(procdir = opendir (PROCFS))) |
---|
466 | { |
---|
467 | (void) fprintf (stderr, "Unable to open %s\n", PROCFS); |
---|
468 | return (-1); |
---|
469 | } |
---|
470 | |
---|
471 | if (chdir (PROCFS)) |
---|
472 | { /* handy for later on when we're reading it */ |
---|
473 | (void) fprintf (stderr, "Unable to chdir to %s\n", PROCFS); |
---|
474 | return (-1); |
---|
475 | } |
---|
476 | |
---|
477 | /* all done! */ |
---|
478 | return (0); |
---|
479 | } |
---|
480 | |
---|
481 | char * |
---|
482 | format_header (register char *uname_field) |
---|
483 | { |
---|
484 | register char *ptr; |
---|
485 | |
---|
486 | ptr = header + UNAME_START; |
---|
487 | while (*uname_field != '\0') |
---|
488 | *ptr++ = *uname_field++; |
---|
489 | |
---|
490 | return (header); |
---|
491 | } |
---|
492 | |
---|
493 | #ifdef USE_KSTAT |
---|
494 | |
---|
495 | #define UPDKCID(nk,ok) \ |
---|
496 | if (nk == -1) { \ |
---|
497 | perror("kstat_read "); \ |
---|
498 | quit(1); \ |
---|
499 | } \ |
---|
500 | if (nk != ok)\ |
---|
501 | goto kcid_changed; |
---|
502 | |
---|
503 | int kupdate(int avenrun[3]) |
---|
504 | { |
---|
505 | kstat_t *ks; |
---|
506 | kid_t nkcid; |
---|
507 | int i; |
---|
508 | int changed = 0; |
---|
509 | static int ncpu = 0; |
---|
510 | static kid_t kcid = 0; |
---|
511 | kstat_named_t *kn; |
---|
512 | |
---|
513 | |
---|
514 | /* |
---|
515 | * 0. kstat_open |
---|
516 | */ |
---|
517 | |
---|
518 | if (!kc) |
---|
519 | { |
---|
520 | kc = kstat_open(); |
---|
521 | if (!kc) |
---|
522 | { |
---|
523 | perror("kstat_open "); |
---|
524 | quit(1); |
---|
525 | } |
---|
526 | changed = 1; |
---|
527 | kcid = kc->kc_chain_id; |
---|
528 | } |
---|
529 | |
---|
530 | /* keep doing it until no more changes */ |
---|
531 | kcid_changed: |
---|
532 | |
---|
533 | /* |
---|
534 | * 1. kstat_chain_update |
---|
535 | */ |
---|
536 | nkcid = kstat_chain_update(kc); |
---|
537 | if (nkcid) |
---|
538 | { |
---|
539 | /* UPDKCID will abort if nkcid is -1, so no need to check */ |
---|
540 | changed = 1; |
---|
541 | kcid = nkcid; |
---|
542 | } |
---|
543 | UPDKCID(nkcid,0); |
---|
544 | |
---|
545 | ks = kstat_lookup(kc, "unix", 0, "system_misc"); |
---|
546 | if (kstat_read(kc, ks, 0) == -1) { |
---|
547 | perror("kstat_read"); |
---|
548 | quit(1); |
---|
549 | } |
---|
550 | |
---|
551 | /* load average */ |
---|
552 | kn = kstat_data_lookup(ks, "avenrun_1min"); |
---|
553 | if (kn) |
---|
554 | avenrun[0] = kn->value.ui32; |
---|
555 | kn = kstat_data_lookup(ks, "avenrun_5min"); |
---|
556 | if (kn) |
---|
557 | avenrun[1] = kn->value.ui32; |
---|
558 | kn = kstat_data_lookup(ks, "avenrun_15min"); |
---|
559 | if (kn) |
---|
560 | avenrun[2] = kn->value.ui32; |
---|
561 | |
---|
562 | /* nproc */ |
---|
563 | kn = kstat_data_lookup(ks, "nproc"); |
---|
564 | if (kn) { |
---|
565 | nproc = kn->value.ui32; |
---|
566 | #ifdef NO_NPROC |
---|
567 | if (nproc > maxprocs) |
---|
568 | reallocproc(2 * nproc); |
---|
569 | #endif |
---|
570 | } |
---|
571 | |
---|
572 | if (changed) { |
---|
573 | |
---|
574 | /* |
---|
575 | * 2. get data addresses |
---|
576 | */ |
---|
577 | |
---|
578 | ncpu = 0; |
---|
579 | |
---|
580 | kn = kstat_data_lookup(ks, "ncpus"); |
---|
581 | if (kn && kn->value.ui32 > ncpus) { |
---|
582 | ncpus = kn->value.ui32; |
---|
583 | cpu_ks = (kstat_t **) realloc (cpu_ks, ncpus * sizeof (kstat_t *)); |
---|
584 | cpu_stat = (cpu_stat_t *) realloc (cpu_stat, |
---|
585 | ncpus * sizeof (cpu_stat_t)); |
---|
586 | } |
---|
587 | |
---|
588 | for (ks = kc->kc_chain; ks; |
---|
589 | ks = ks->ks_next) |
---|
590 | { |
---|
591 | if (strncmp(ks->ks_name, "cpu_stat", 8) == 0) |
---|
592 | { |
---|
593 | nkcid = kstat_read(kc, ks, NULL); |
---|
594 | /* if kcid changed, pointer might be invalid */ |
---|
595 | UPDKCID(nkcid, kcid); |
---|
596 | |
---|
597 | cpu_ks[ncpu] = ks; |
---|
598 | ncpu++; |
---|
599 | if (ncpu > ncpus) |
---|
600 | { |
---|
601 | fprintf(stderr, "kstat finds too many cpus: should be %d\n", |
---|
602 | ncpus); |
---|
603 | quit(1); |
---|
604 | } |
---|
605 | } |
---|
606 | } |
---|
607 | /* note that ncpu could be less than ncpus, but that's okay */ |
---|
608 | changed = 0; |
---|
609 | } |
---|
610 | |
---|
611 | /* |
---|
612 | * 3. get data |
---|
613 | */ |
---|
614 | |
---|
615 | for (i = 0; i < ncpu; i++) |
---|
616 | { |
---|
617 | nkcid = kstat_read(kc, cpu_ks[i], &cpu_stat[i]); |
---|
618 | /* if kcid changed, pointer might be invalid */ |
---|
619 | UPDKCID(nkcid, kcid); |
---|
620 | } |
---|
621 | |
---|
622 | /* return the number of cpus found */ |
---|
623 | return(ncpu); |
---|
624 | } |
---|
625 | |
---|
626 | #endif /* USE_KSTAT */ |
---|
627 | |
---|
628 | void |
---|
629 | get_system_info (struct system_info *si) |
---|
630 | { |
---|
631 | int avenrun[3]; |
---|
632 | static long freemem; |
---|
633 | static long maxmem; |
---|
634 | static long availrmem; |
---|
635 | static long swapfs_minfree; |
---|
636 | static int swap_total; |
---|
637 | static int swap_free; |
---|
638 | struct anoninfo anoninfo; |
---|
639 | static long cp_time[CPUSTATES]; |
---|
640 | static long cp_old[CPUSTATES]; |
---|
641 | static long cp_diff[CPUSTATES]; |
---|
642 | register int j, i; |
---|
643 | #ifdef USE_KSTAT |
---|
644 | kstat_t *ks; |
---|
645 | kstat_named_t *kn; |
---|
646 | int cpus_found; |
---|
647 | #else |
---|
648 | struct cpu cpu; |
---|
649 | #endif |
---|
650 | |
---|
651 | /* get the cp_time array */ |
---|
652 | for (j = 0; j < CPUSTATES; j++) |
---|
653 | cp_time[j] = 0L; |
---|
654 | |
---|
655 | #ifdef USE_KSTAT |
---|
656 | /* use kstat to update all processor information */ |
---|
657 | cpus_found = kupdate(avenrun); |
---|
658 | for (i = 0; i < cpus_found; i++) |
---|
659 | { |
---|
660 | /* sum counters up to, but not including, wait state counter */ |
---|
661 | for (j = 0; j < CPU_WAIT; j++) |
---|
662 | cp_time[j] += (long) cpu_stat[i].cpu_sysinfo.cpu[j]; |
---|
663 | |
---|
664 | /* add in wait state breakdown counters */ |
---|
665 | cp_time[CPUSTATE_IOWAIT] += (long) cpu_stat[i].cpu_sysinfo.wait[W_IO] + |
---|
666 | (long) cpu_stat[i].cpu_sysinfo.wait[W_PIO]; |
---|
667 | cp_time[CPUSTATE_SWAP] += (long) cpu_stat[i].cpu_sysinfo.wait[W_SWAP]; |
---|
668 | } |
---|
669 | /* avenrun */ |
---|
670 | |
---|
671 | #if OSREV >= 55 |
---|
672 | /* |
---|
673 | * The system_pages kstat is extremely expensive on systems with large |
---|
674 | * amounts of physical memory. But since we only need freemem/availrmem |
---|
675 | * and those are readily avaiable in the kernel cheaply, we read the |
---|
676 | * kernel if kvm_open succeeded. |
---|
677 | */ |
---|
678 | if (kd == NULL) { |
---|
679 | ks = kstat_lookup(kc, "unix", 0, "system_pages"); |
---|
680 | if (kstat_read(kc, ks, 0) == -1) { |
---|
681 | perror("kstat_read"); |
---|
682 | quit(1); |
---|
683 | } |
---|
684 | #ifdef USE_ANONINFO |
---|
685 | kn = kstat_data_lookup(ks, "availrmem"); |
---|
686 | if (kn) |
---|
687 | availrmem = kn->value.ul; |
---|
688 | #endif |
---|
689 | kn = kstat_data_lookup(ks, "freemem"); |
---|
690 | if (kn) |
---|
691 | freemem = kn->value.ul; |
---|
692 | } |
---|
693 | #endif /* OSREV >= 55 */ |
---|
694 | |
---|
695 | #else /* !USE_KSTAT */ |
---|
696 | |
---|
697 | for (i = 0; i < ncpus; i++) |
---|
698 | if (cpu_offset[i] != 0) |
---|
699 | { |
---|
700 | /* get struct cpu for this processor */ |
---|
701 | (void) getkval (cpu_offset[i], &cpu, sizeof (struct cpu), "cpu"); |
---|
702 | |
---|
703 | /* sum counters up to, but not including, wait state counter */ |
---|
704 | for (j = 0; j < CPU_WAIT; j++) |
---|
705 | cp_time[j] += (long) cpu.cpu_stat.cpu_sysinfo.cpu[j]; |
---|
706 | |
---|
707 | /* add in wait state breakdown counters */ |
---|
708 | cp_time[CPUSTATE_IOWAIT] += (long) cpu.cpu_stat.cpu_sysinfo.wait[W_IO] + |
---|
709 | (long) cpu.cpu_stat.cpu_sysinfo.wait[W_PIO]; |
---|
710 | cp_time[CPUSTATE_SWAP] += (long) cpu.cpu_stat.cpu_sysinfo.wait[W_SWAP]; |
---|
711 | } |
---|
712 | |
---|
713 | /* get load average array */ |
---|
714 | (void) getkval (avenrun_offset, (int *) avenrun, sizeof (avenrun), "avenrun"); |
---|
715 | |
---|
716 | |
---|
717 | #endif /* USE_KSTAT */ |
---|
718 | |
---|
719 | /* convert cp_time counts to percentages */ |
---|
720 | (void) percentages (CPUSTATES, cpu_states, cp_time, cp_old, cp_diff); |
---|
721 | |
---|
722 | /* get mpid -- process id of last process */ |
---|
723 | if (kd) |
---|
724 | (void) getkval(mpid_offset, &(si->last_pid), sizeof (si->last_pid), "mpid"); |
---|
725 | else |
---|
726 | si->last_pid = -1; |
---|
727 | |
---|
728 | /* convert load averages to doubles */ |
---|
729 | for (i = 0; i < 3; i++) |
---|
730 | si->load_avg[i] = loaddouble (avenrun[i]); |
---|
731 | |
---|
732 | /* get system wide main memory usage structure */ |
---|
733 | #if 1 |
---|
734 | maxmem = sysconf(_SC_PHYS_PAGES); |
---|
735 | #else |
---|
736 | (void) getkval (maxmem_offset, (int *) (&maxmem), sizeof (maxmem), "maxmem"); |
---|
737 | #endif |
---|
738 | |
---|
739 | #if defined(USE_KSTAT) && OSREV >= 55 |
---|
740 | if (kd != NULL) /* always get freemem from kvm if we can*/ |
---|
741 | #endif |
---|
742 | (void) getkval (freemem_offset, (int *) (&freemem), |
---|
743 | sizeof (freemem), "freemem"); |
---|
744 | |
---|
745 | memory_stats[0] = pagetok (maxmem); |
---|
746 | memory_stats[1] = 0; |
---|
747 | memory_stats[2] = pagetok (freemem); |
---|
748 | |
---|
749 | #ifdef USE_ANONINFO |
---|
750 | (void) getkval (anoninfo_offset, (int *) (&anoninfo), sizeof (anoninfo), "anoninfo"); |
---|
751 | #if defined(USE_KSTAT) && OSREV >= 55 |
---|
752 | if (kd != NULL) /* always get availrmem from kvm if we can*/ |
---|
753 | #endif |
---|
754 | (void) getkval (availrmem_offset, (int *) (&availrmem), |
---|
755 | sizeof (availrmem), "availrmem"); |
---|
756 | |
---|
757 | (void) getkval (swapfs_minfree_offset, (int *) (&swapfs_minfree), sizeof (swapfs_minfree), "swapfs_minfree"); |
---|
758 | memory_stats[3] = pagetok (anoninfo.ani_resv); |
---|
759 | memory_stats[4] = pagetok (MAX ((int) (anoninfo.ani_max - anoninfo.ani_resv), 0) + availrmem - swapfs_minfree); |
---|
760 | #else |
---|
761 | get_swapinfo(&swap_total, &swap_free); |
---|
762 | memory_stats[3] = pagetok(swap_total - swap_free); |
---|
763 | memory_stats[4] = pagetok(swap_free); |
---|
764 | #endif |
---|
765 | |
---|
766 | /* set arrays and strings */ |
---|
767 | si->cpustates = cpu_states; |
---|
768 | si->memory = memory_stats; |
---|
769 | } |
---|
770 | |
---|
771 | static struct handle handle; |
---|
772 | |
---|
773 | caddr_t |
---|
774 | get_process_info ( |
---|
775 | struct system_info *si, |
---|
776 | struct process_select *sel, |
---|
777 | int (*compare) ()) |
---|
778 | { |
---|
779 | register int i; |
---|
780 | register int total_procs; |
---|
781 | register int active_procs; |
---|
782 | register struct prpsinfo **prefp; |
---|
783 | register struct prpsinfo *pp; |
---|
784 | |
---|
785 | /* these are copied out of sel for speed */ |
---|
786 | int show_idle; |
---|
787 | int show_system; |
---|
788 | int show_uid; |
---|
789 | |
---|
790 | #ifndef USE_KSTAT |
---|
791 | /* Get current number of processes */ |
---|
792 | /* Got this when calling system info if using kstat */ |
---|
793 | (void) getkval (nproc_offset, (int *) (&nproc), sizeof (nproc), "nproc"); |
---|
794 | #endif |
---|
795 | |
---|
796 | /* read all the proc structures */ |
---|
797 | getptable (pbase); |
---|
798 | |
---|
799 | /* get a pointer to the states summary array */ |
---|
800 | si->procstates = process_states; |
---|
801 | |
---|
802 | /* set up flags which define what we are going to select */ |
---|
803 | show_idle = sel->idle; |
---|
804 | show_system = sel->system; |
---|
805 | show_uid = sel->uid != -1; |
---|
806 | |
---|
807 | /* count up process states and get pointers to interesting procs */ |
---|
808 | total_procs = 0; |
---|
809 | active_procs = 0; |
---|
810 | (void) memset (process_states, 0, sizeof (process_states)); |
---|
811 | prefp = pref; |
---|
812 | |
---|
813 | for (pp = pbase, i = 0; i < nproc; |
---|
814 | i++, pp = (struct prpsinfo *) ((char *) pp + PRPSINFOSIZE)) |
---|
815 | { |
---|
816 | /* |
---|
817 | * Place pointers to each valid proc structure in pref[]. |
---|
818 | * Process slots that are actually in use have a non-zero |
---|
819 | * status field. Processes with SSYS set are system |
---|
820 | * processes---these get ignored unless show_sysprocs is set. |
---|
821 | */ |
---|
822 | if (pp->pr_state != 0 && |
---|
823 | (show_system || ((pp->pr_flag & SSYS) == 0))) |
---|
824 | { |
---|
825 | total_procs++; |
---|
826 | process_states[pp->pr_state]++; |
---|
827 | if ((!ZOMBIE(pp)) && |
---|
828 | (show_idle || percent_cpu (pp) || (pp->pr_state == SRUN) || (pp->pr_state == SONPROC)) && |
---|
829 | (!show_uid || pp->pr_uid == (uid_t) sel->uid)) |
---|
830 | { |
---|
831 | *prefp++ = pp; |
---|
832 | active_procs++; |
---|
833 | } |
---|
834 | } |
---|
835 | } |
---|
836 | |
---|
837 | /* if requested, sort the "interesting" processes */ |
---|
838 | if (compare != NULL) |
---|
839 | qsort ((char *) pref, active_procs, sizeof (struct prpsinfo *), compare); |
---|
840 | |
---|
841 | /* remember active and total counts */ |
---|
842 | si->p_total = total_procs; |
---|
843 | si->p_active = active_procs; |
---|
844 | |
---|
845 | /* pass back a handle */ |
---|
846 | handle.next_proc = pref; |
---|
847 | handle.remaining = active_procs; |
---|
848 | return ((caddr_t) & handle); |
---|
849 | } |
---|
850 | |
---|
851 | char fmt[MAX_COLS]; /* static area where result is built */ |
---|
852 | |
---|
853 | char * |
---|
854 | format_next_process ( |
---|
855 | caddr_t handle, |
---|
856 | char *(*get_userid) ()) |
---|
857 | { |
---|
858 | register struct prpsinfo *pp; |
---|
859 | struct handle *hp; |
---|
860 | register long cputime; |
---|
861 | register double pctcpu; |
---|
862 | char sb[10]; |
---|
863 | |
---|
864 | /* find and remember the next proc structure */ |
---|
865 | hp = (struct handle *) handle; |
---|
866 | pp = *(hp->next_proc++); |
---|
867 | hp->remaining--; |
---|
868 | |
---|
869 | /* get the cpu usage and calculate the cpu percentages */ |
---|
870 | cputime = pp->pr_time.tv_sec; |
---|
871 | pctcpu = percent_cpu (pp); |
---|
872 | |
---|
873 | if (pp->pr_state == SONPROC && ncpus > 1) |
---|
874 | sprintf(sb,"cpu/%-2d", pp->pr_onpro); /* XXX large #s may overflow colums */ |
---|
875 | else |
---|
876 | *sb = '\0'; |
---|
877 | |
---|
878 | /* format this entry */ |
---|
879 | sprintf (fmt, |
---|
880 | Proc_format, |
---|
881 | pp->pr_pid, |
---|
882 | (*get_userid) (pp->pr_uid), |
---|
883 | (u_short)pp->pr_fill < 999 ? (u_short)pp->pr_fill : 999, |
---|
884 | pp->pr_pri, |
---|
885 | pp->pr_nice - NZERO, |
---|
886 | format_k(SIZE_K(pp)), |
---|
887 | format_k(RSS_K(pp)), |
---|
888 | *sb ? sb : state_abbrev[pp->pr_state], |
---|
889 | format_time(cputime), |
---|
890 | pctcpu, |
---|
891 | pp->pr_fname); |
---|
892 | |
---|
893 | /* return the result */ |
---|
894 | return (fmt); |
---|
895 | } |
---|
896 | |
---|
897 | /* |
---|
898 | * check_nlist(nlst) - checks the nlist to see if any symbols were not |
---|
899 | * found. For every symbol that was not found, a one-line |
---|
900 | * message is printed to stderr. The routine returns the |
---|
901 | * number of symbols NOT found. |
---|
902 | */ |
---|
903 | int |
---|
904 | check_nlist (register struct nlist *nlst) |
---|
905 | { |
---|
906 | register int i; |
---|
907 | |
---|
908 | /* check to see if we got ALL the symbols we requested */ |
---|
909 | /* this will write one line to stderr for every symbol not found */ |
---|
910 | |
---|
911 | i = 0; |
---|
912 | while (nlst->n_name != NULL) |
---|
913 | { |
---|
914 | if (nlst->n_type == 0) |
---|
915 | { |
---|
916 | /* this one wasn't found */ |
---|
917 | fprintf (stderr, "kernel: no symbol named `%s'\n", nlst->n_name); |
---|
918 | i = 1; |
---|
919 | } |
---|
920 | nlst++; |
---|
921 | } |
---|
922 | return (i); |
---|
923 | } |
---|
924 | |
---|
925 | |
---|
926 | /* |
---|
927 | * getkval(offset, ptr, size, refstr) - get a value out of the kernel. |
---|
928 | * "offset" is the byte offset into the kernel for the desired value, |
---|
929 | * "ptr" points to a buffer into which the value is retrieved, |
---|
930 | * "size" is the size of the buffer (and the object to retrieve), |
---|
931 | * "refstr" is a reference string used when printing error meessages, |
---|
932 | * if "refstr" starts with a '!', then a failure on read will not |
---|
933 | * be fatal (this may seem like a silly way to do things, but I |
---|
934 | * really didn't want the overhead of another argument). |
---|
935 | * |
---|
936 | */ |
---|
937 | int |
---|
938 | getkval (unsigned long offset, |
---|
939 | int *ptr, |
---|
940 | int size, |
---|
941 | char *refstr) |
---|
942 | { |
---|
943 | if (kvm_read (kd, offset, (char *) ptr, size) != size) |
---|
944 | { |
---|
945 | if (*refstr == '!') |
---|
946 | { |
---|
947 | return (0); |
---|
948 | } |
---|
949 | else |
---|
950 | { |
---|
951 | fprintf (stderr, "top: kvm_read for %s: %s\n", refstr, strerror(errno)); |
---|
952 | quit (23); |
---|
953 | } |
---|
954 | } |
---|
955 | return (1); |
---|
956 | |
---|
957 | } |
---|
958 | |
---|
959 | /* comparison routines for qsort */ |
---|
960 | |
---|
961 | /* |
---|
962 | * There are currently four possible comparison routines. main selects |
---|
963 | * one of these by indexing in to the array proc_compares. |
---|
964 | * |
---|
965 | * Possible keys are defined as macros below. Currently these keys are |
---|
966 | * defined: percent cpu, cpu ticks, process state, resident set size, |
---|
967 | * total virtual memory usage. The process states are ordered as follows |
---|
968 | * (from least to most important): WAIT, zombie, sleep, stop, start, run. |
---|
969 | * The array declaration below maps a process state index into a number |
---|
970 | * that reflects this ordering. |
---|
971 | */ |
---|
972 | |
---|
973 | /* First, the possible comparison keys. These are defined in such a way |
---|
974 | that they can be merely listed in the source code to define the actual |
---|
975 | desired ordering. |
---|
976 | */ |
---|
977 | |
---|
978 | #define ORDERKEY_PCTCPU if (dresult = percent_cpu (p2) - percent_cpu (p1),\ |
---|
979 | (result = dresult > 0.0 ? 1 : dresult < 0.0 ? -1 : 0) == 0) |
---|
980 | #define ORDERKEY_CPTICKS if ((result = p2->pr_time.tv_sec - p1->pr_time.tv_sec) == 0) |
---|
981 | #define ORDERKEY_STATE if ((result = (long) (sorted_state[p2->pr_state] - \ |
---|
982 | sorted_state[p1->pr_state])) == 0) |
---|
983 | #define ORDERKEY_PRIO if ((result = p2->pr_oldpri - p1->pr_oldpri) == 0) |
---|
984 | #define ORDERKEY_RSSIZE if ((result = p2->pr_rssize - p1->pr_rssize) == 0) |
---|
985 | #define ORDERKEY_MEM if ((result = (p2->pr_size - p1->pr_size)) == 0) |
---|
986 | |
---|
987 | /* Now the array that maps process state to a weight */ |
---|
988 | |
---|
989 | unsigned char sorted_state[] = |
---|
990 | { |
---|
991 | 0, /* not used */ |
---|
992 | 3, /* sleep */ |
---|
993 | 6, /* run */ |
---|
994 | 2, /* zombie */ |
---|
995 | 4, /* stop */ |
---|
996 | 5, /* start */ |
---|
997 | 7, /* run on a processor */ |
---|
998 | 1 /* being swapped (WAIT) */ |
---|
999 | }; |
---|
1000 | |
---|
1001 | |
---|
1002 | /* compare_cpu - the comparison function for sorting by cpu percentage */ |
---|
1003 | |
---|
1004 | int |
---|
1005 | compare_cpu ( |
---|
1006 | struct prpsinfo **pp1, |
---|
1007 | struct prpsinfo **pp2) |
---|
1008 | { |
---|
1009 | register struct prpsinfo *p1; |
---|
1010 | register struct prpsinfo *p2; |
---|
1011 | register long result; |
---|
1012 | double dresult; |
---|
1013 | |
---|
1014 | /* remove one level of indirection */ |
---|
1015 | p1 = *pp1; |
---|
1016 | p2 = *pp2; |
---|
1017 | |
---|
1018 | ORDERKEY_PCTCPU |
---|
1019 | ORDERKEY_CPTICKS |
---|
1020 | ORDERKEY_STATE |
---|
1021 | ORDERKEY_PRIO |
---|
1022 | ORDERKEY_RSSIZE |
---|
1023 | ORDERKEY_MEM |
---|
1024 | ; |
---|
1025 | |
---|
1026 | return (result); |
---|
1027 | } |
---|
1028 | |
---|
1029 | /* compare_size - the comparison function for sorting by total memory usage */ |
---|
1030 | |
---|
1031 | int |
---|
1032 | compare_size ( |
---|
1033 | struct prpsinfo **pp1, |
---|
1034 | struct prpsinfo **pp2) |
---|
1035 | { |
---|
1036 | register struct prpsinfo *p1; |
---|
1037 | register struct prpsinfo *p2; |
---|
1038 | register long result; |
---|
1039 | double dresult; |
---|
1040 | |
---|
1041 | /* remove one level of indirection */ |
---|
1042 | p1 = *pp1; |
---|
1043 | p2 = *pp2; |
---|
1044 | |
---|
1045 | ORDERKEY_MEM |
---|
1046 | ORDERKEY_RSSIZE |
---|
1047 | ORDERKEY_PCTCPU |
---|
1048 | ORDERKEY_CPTICKS |
---|
1049 | ORDERKEY_STATE |
---|
1050 | ORDERKEY_PRIO |
---|
1051 | ; |
---|
1052 | |
---|
1053 | return (result); |
---|
1054 | } |
---|
1055 | |
---|
1056 | /* compare_res - the comparison function for sorting by resident set size */ |
---|
1057 | |
---|
1058 | int |
---|
1059 | compare_res ( |
---|
1060 | struct prpsinfo **pp1, |
---|
1061 | struct prpsinfo **pp2) |
---|
1062 | { |
---|
1063 | register struct prpsinfo *p1; |
---|
1064 | register struct prpsinfo *p2; |
---|
1065 | register long result; |
---|
1066 | double dresult; |
---|
1067 | |
---|
1068 | /* remove one level of indirection */ |
---|
1069 | p1 = *pp1; |
---|
1070 | p2 = *pp2; |
---|
1071 | |
---|
1072 | ORDERKEY_RSSIZE |
---|
1073 | ORDERKEY_MEM |
---|
1074 | ORDERKEY_PCTCPU |
---|
1075 | ORDERKEY_CPTICKS |
---|
1076 | ORDERKEY_STATE |
---|
1077 | ORDERKEY_PRIO |
---|
1078 | ; |
---|
1079 | |
---|
1080 | return (result); |
---|
1081 | } |
---|
1082 | |
---|
1083 | /* compare_time - the comparison function for sorting by total cpu time */ |
---|
1084 | |
---|
1085 | int |
---|
1086 | compare_time ( |
---|
1087 | struct prpsinfo **pp1, |
---|
1088 | struct prpsinfo **pp2) |
---|
1089 | { |
---|
1090 | register struct prpsinfo *p1; |
---|
1091 | register struct prpsinfo *p2; |
---|
1092 | register long result; |
---|
1093 | double dresult; |
---|
1094 | |
---|
1095 | /* remove one level of indirection */ |
---|
1096 | p1 = *pp1; |
---|
1097 | p2 = *pp2; |
---|
1098 | |
---|
1099 | ORDERKEY_CPTICKS |
---|
1100 | ORDERKEY_PCTCPU |
---|
1101 | ORDERKEY_STATE |
---|
1102 | ORDERKEY_PRIO |
---|
1103 | ORDERKEY_MEM |
---|
1104 | ORDERKEY_RSSIZE |
---|
1105 | ; |
---|
1106 | |
---|
1107 | return (result); |
---|
1108 | } |
---|
1109 | |
---|
1110 | /* |
---|
1111 | get process table |
---|
1112 | V.4 only has a linked list of processes so we want to follow that |
---|
1113 | linked list, get all the process structures, and put them in our own |
---|
1114 | table |
---|
1115 | */ |
---|
1116 | void |
---|
1117 | getptable (struct prpsinfo *baseptr) |
---|
1118 | { |
---|
1119 | struct prpsinfo *currproc; /* pointer to current proc structure */ |
---|
1120 | #ifndef USE_NEW_PROC |
---|
1121 | struct prstatus prstatus; /* for additional information */ |
---|
1122 | #endif |
---|
1123 | int numprocs = 0; |
---|
1124 | int i; |
---|
1125 | struct dirent *direntp; |
---|
1126 | struct oldproc *op; |
---|
1127 | static struct timeval lasttime = |
---|
1128 | {0, 0}; |
---|
1129 | struct timeval thistime; |
---|
1130 | double timediff; |
---|
1131 | double alpha, beta; |
---|
1132 | struct oldproc *endbase; |
---|
1133 | |
---|
1134 | gettimeofday (&thistime, NULL); |
---|
1135 | /* |
---|
1136 | * To avoid divides, we keep times in nanoseconds. This is |
---|
1137 | * scaled by 1e7 rather than 1e9 so that when we divide we |
---|
1138 | * get percent. |
---|
1139 | */ |
---|
1140 | if (lasttime.tv_sec) |
---|
1141 | timediff = ((double) thistime.tv_sec * 1.0e7 + |
---|
1142 | ((double) thistime.tv_usec * 10.0)) - |
---|
1143 | ((double) lasttime.tv_sec * 1.0e7 + |
---|
1144 | ((double) lasttime.tv_usec * 10.0)); |
---|
1145 | else |
---|
1146 | timediff = 1.0e7; |
---|
1147 | |
---|
1148 | /* |
---|
1149 | * constants for exponential average. avg = alpha * new + beta * avg |
---|
1150 | * The goal is 50% decay in 30 sec. However if the sample period |
---|
1151 | * is greater than 30 sec, there's not a lot we can do. |
---|
1152 | */ |
---|
1153 | if (timediff < 30.0e7) |
---|
1154 | { |
---|
1155 | alpha = 0.5 * (timediff / 30.0e7); |
---|
1156 | beta = 1.0 - alpha; |
---|
1157 | } |
---|
1158 | else |
---|
1159 | { |
---|
1160 | alpha = 0.5; |
---|
1161 | beta = 0.5; |
---|
1162 | } |
---|
1163 | |
---|
1164 | endbase = oldbase + oldprocs; |
---|
1165 | currproc = baseptr; |
---|
1166 | |
---|
1167 | /* before reading /proc files, turn on root privs */ |
---|
1168 | /* (we don't care if this fails since it will be caught later) */ |
---|
1169 | #ifndef USE_NEW_PROC |
---|
1170 | seteuid(0); |
---|
1171 | #endif |
---|
1172 | |
---|
1173 | for (rewinddir (procdir); (direntp = readdir (procdir));) |
---|
1174 | { |
---|
1175 | int fd; |
---|
1176 | char buf[30]; |
---|
1177 | |
---|
1178 | if (direntp->d_name[0] == '.') |
---|
1179 | continue; |
---|
1180 | |
---|
1181 | #ifdef USE_NEW_PROC |
---|
1182 | snprintf(buf, sizeof(buf), "%s/psinfo", direntp->d_name); |
---|
1183 | |
---|
1184 | if ((fd = open (buf, O_RDONLY)) < 0) |
---|
1185 | continue; |
---|
1186 | |
---|
1187 | if (read(fd, currproc, sizeof(psinfo_t)) != sizeof(psinfo_t)) |
---|
1188 | { |
---|
1189 | (void) close (fd); |
---|
1190 | continue; |
---|
1191 | } |
---|
1192 | |
---|
1193 | #else |
---|
1194 | if ((fd = open (direntp->d_name, O_RDONLY)) < 0) |
---|
1195 | continue; |
---|
1196 | |
---|
1197 | if (ioctl (fd, PIOCPSINFO, currproc) < 0) |
---|
1198 | { |
---|
1199 | (void) close (fd); |
---|
1200 | continue; |
---|
1201 | } |
---|
1202 | |
---|
1203 | if (ioctl (fd, PIOCSTATUS, &prstatus) < 0) |
---|
1204 | { |
---|
1205 | /* not a show stopper -- just fill in the needed values */ |
---|
1206 | currproc->pr_fill = 0; |
---|
1207 | currproc->pr_onpro = 0; |
---|
1208 | } else { |
---|
1209 | /* copy over the values we need from prstatus */ |
---|
1210 | currproc->pr_fill = (short)prstatus.pr_nlwp; |
---|
1211 | currproc->pr_onpro = prstatus.pr_processor; |
---|
1212 | } |
---|
1213 | #endif |
---|
1214 | |
---|
1215 | /* |
---|
1216 | * SVr4 doesn't keep track of CPU% in the kernel, so we have |
---|
1217 | * to do our own. See if we've heard of this process before. |
---|
1218 | * If so, compute % based on CPU since last time. |
---|
1219 | * NOTE: Solaris 2.4 and higher do maintain CPU% in prpsinfo. |
---|
1220 | */ |
---|
1221 | op = oldbase + HASH (currproc->pr_pid); |
---|
1222 | while (1) |
---|
1223 | { |
---|
1224 | if (op->oldpid == -1) /* not there */ |
---|
1225 | break; |
---|
1226 | if (op->oldpid == currproc->pr_pid) |
---|
1227 | { /* found old data */ |
---|
1228 | #if (OSREV < 54) |
---|
1229 | percent_cpu (currproc) = |
---|
1230 | ((currproc->pr_time.tv_sec * 1.0e9 + |
---|
1231 | currproc->pr_time.tv_nsec) |
---|
1232 | - op->oldtime) / timediff; |
---|
1233 | #endif |
---|
1234 | weighted_cpu (currproc) = |
---|
1235 | op->oldpct * beta + percent_cpu (currproc) * alpha; |
---|
1236 | |
---|
1237 | break; |
---|
1238 | } |
---|
1239 | op++; /* try next entry in hash table */ |
---|
1240 | if (op == endbase) /* table wrapped around */ |
---|
1241 | op = oldbase; |
---|
1242 | } |
---|
1243 | |
---|
1244 | /* Otherwise, it's new, so use all of its CPU time */ |
---|
1245 | if (op->oldpid == -1) |
---|
1246 | { |
---|
1247 | #if (OSREV >= 54) |
---|
1248 | weighted_cpu (currproc) = |
---|
1249 | percent_cpu (currproc); |
---|
1250 | #else |
---|
1251 | if (lasttime.tv_sec) |
---|
1252 | { |
---|
1253 | percent_cpu (currproc) = |
---|
1254 | (currproc->pr_time.tv_sec * 1.0e9 + |
---|
1255 | currproc->pr_time.tv_nsec) / timediff; |
---|
1256 | weighted_cpu (currproc) = |
---|
1257 | percent_cpu (currproc); |
---|
1258 | } |
---|
1259 | else |
---|
1260 | { /* first screen -- no difference is possible */ |
---|
1261 | percent_cpu (currproc) = 0.0; |
---|
1262 | weighted_cpu (currproc) = 0.0; |
---|
1263 | } |
---|
1264 | #endif |
---|
1265 | } |
---|
1266 | |
---|
1267 | numprocs++; |
---|
1268 | currproc = (struct prpsinfo *) ((char *) currproc + PRPSINFOSIZE); |
---|
1269 | (void) close (fd); |
---|
1270 | #ifdef NO_NPROC |
---|
1271 | /* Atypical place for growth */ |
---|
1272 | if (numprocs >= maxprocs) { |
---|
1273 | reallocproc(2 * numprocs); |
---|
1274 | currproc = (struct prpsinfo *) |
---|
1275 | ((char *)baseptr + PRPSINFOSIZE * numprocs); |
---|
1276 | } |
---|
1277 | #endif |
---|
1278 | } |
---|
1279 | |
---|
1280 | #ifndef USE_NEW_PROC |
---|
1281 | /* turn off root privs */ |
---|
1282 | seteuid(getuid()); |
---|
1283 | #endif |
---|
1284 | |
---|
1285 | if (nproc != numprocs) |
---|
1286 | nproc = numprocs; |
---|
1287 | |
---|
1288 | /* |
---|
1289 | * Save current CPU time for next time around |
---|
1290 | * For the moment recreate the hash table each time, as the code |
---|
1291 | * is easier that way. |
---|
1292 | */ |
---|
1293 | oldprocs = 2 * nproc; |
---|
1294 | endbase = oldbase + oldprocs; |
---|
1295 | for (op = oldbase; op < endbase; op++) |
---|
1296 | op->oldpid = -1; |
---|
1297 | for (i = 0, currproc = baseptr; |
---|
1298 | i < nproc; |
---|
1299 | i++, currproc = (struct prpsinfo *) ((char *) currproc + PRPSINFOSIZE)) |
---|
1300 | { |
---|
1301 | /* find an empty spot */ |
---|
1302 | op = oldbase + HASH (currproc->pr_pid); |
---|
1303 | while (1) |
---|
1304 | { |
---|
1305 | if (op->oldpid == -1) |
---|
1306 | break; |
---|
1307 | op++; |
---|
1308 | if (op == endbase) |
---|
1309 | op = oldbase; |
---|
1310 | } |
---|
1311 | op->oldpid = currproc->pr_pid; |
---|
1312 | op->oldtime = (currproc->pr_time.tv_sec * 1.0e9 + |
---|
1313 | currproc->pr_time.tv_nsec); |
---|
1314 | op->oldpct = weighted_cpu (currproc); |
---|
1315 | } |
---|
1316 | lasttime = thistime; |
---|
1317 | } |
---|
1318 | |
---|
1319 | /* |
---|
1320 | * proc_owner(pid) - returns the uid that owns process "pid", or -1 if |
---|
1321 | * the process does not exist. |
---|
1322 | * It is EXTREMLY IMPORTANT that this function work correctly. |
---|
1323 | * If top runs setuid root (as in SVR4), then this function |
---|
1324 | * is the only thing that stands in the way of a serious |
---|
1325 | * security problem. It validates requests for the "kill" |
---|
1326 | * and "renice" commands. |
---|
1327 | */ |
---|
1328 | uid_t |
---|
1329 | proc_owner (pid_t pid) |
---|
1330 | { |
---|
1331 | register struct prpsinfo *p; |
---|
1332 | int i; |
---|
1333 | for (i = 0, p = pbase; i < nproc; |
---|
1334 | i++, p = (struct prpsinfo *) ((char *) p + PRPSINFOSIZE)) { |
---|
1335 | if (p->pr_pid == pid) |
---|
1336 | return (p->pr_uid); |
---|
1337 | } |
---|
1338 | return (-1); |
---|
1339 | } |
---|
1340 | |
---|
1341 | #if OSREV < 55 |
---|
1342 | int |
---|
1343 | setpriority (int dummy, int who, int niceval) |
---|
1344 | { |
---|
1345 | int scale; |
---|
1346 | int prio; |
---|
1347 | pcinfo_t pcinfo; |
---|
1348 | pcparms_t pcparms; |
---|
1349 | tsparms_t *tsparms; |
---|
1350 | |
---|
1351 | strcpy (pcinfo.pc_clname, "TS"); |
---|
1352 | if (priocntl (0, 0, PC_GETCID, (caddr_t) & pcinfo) == -1) |
---|
1353 | return (-1); |
---|
1354 | |
---|
1355 | prio = niceval; |
---|
1356 | if (prio > PRIO_MAX) |
---|
1357 | prio = PRIO_MAX; |
---|
1358 | else if (prio < PRIO_MIN) |
---|
1359 | prio = PRIO_MIN; |
---|
1360 | |
---|
1361 | tsparms = (tsparms_t *) pcparms.pc_clparms; |
---|
1362 | scale = ((tsinfo_t *) pcinfo.pc_clinfo)->ts_maxupri; |
---|
1363 | tsparms->ts_uprilim = tsparms->ts_upri = -(scale * prio) / 20; |
---|
1364 | pcparms.pc_cid = pcinfo.pc_cid; |
---|
1365 | |
---|
1366 | if (priocntl (P_PID, who, PC_SETPARMS, (caddr_t) & pcparms) == -1) |
---|
1367 | return (-1); |
---|
1368 | |
---|
1369 | return (0); |
---|
1370 | } |
---|
1371 | #endif |
---|
1372 | |
---|
1373 | #ifndef USE_ANONINFO |
---|
1374 | get_swapinfo(int *total, int *fr) |
---|
1375 | |
---|
1376 | { |
---|
1377 | #ifdef SC_AINFO |
---|
1378 | struct anoninfo anon; |
---|
1379 | |
---|
1380 | if (swapctl(SC_AINFO, &anon) == -1) { |
---|
1381 | *total = *fr = 0; |
---|
1382 | return; |
---|
1383 | } |
---|
1384 | *total = anon.ani_max; |
---|
1385 | *fr = anon.ani_max - anon.ani_resv; |
---|
1386 | #else |
---|
1387 | register int cnt, i; |
---|
1388 | register int t, f; |
---|
1389 | struct swaptable *swt; |
---|
1390 | struct swapent *ste; |
---|
1391 | static char path[256]; |
---|
1392 | |
---|
1393 | /* get total number of swap entries */ |
---|
1394 | cnt = swapctl(SC_GETNSWP, 0); |
---|
1395 | |
---|
1396 | /* allocate enough space to hold count + n swapents */ |
---|
1397 | swt = (struct swaptable *)malloc(sizeof(int) + |
---|
1398 | cnt * sizeof(struct swapent)); |
---|
1399 | if (swt == NULL) |
---|
1400 | { |
---|
1401 | *total = 0; |
---|
1402 | *fr = 0; |
---|
1403 | return; |
---|
1404 | } |
---|
1405 | swt->swt_n = cnt; |
---|
1406 | |
---|
1407 | /* fill in ste_path pointers: we don't care about the paths, so we point |
---|
1408 | them all to the same buffer */ |
---|
1409 | ste = &(swt->swt_ent[0]); |
---|
1410 | i = cnt; |
---|
1411 | while (--i >= 0) |
---|
1412 | { |
---|
1413 | ste++->ste_path = path; |
---|
1414 | } |
---|
1415 | |
---|
1416 | /* grab all swap info */ |
---|
1417 | swapctl(SC_LIST, swt); |
---|
1418 | |
---|
1419 | /* walk thru the structs and sum up the fields */ |
---|
1420 | t = f = 0; |
---|
1421 | ste = &(swt->swt_ent[0]); |
---|
1422 | i = cnt; |
---|
1423 | while (--i >= 0) |
---|
1424 | { |
---|
1425 | /* dont count slots being deleted */ |
---|
1426 | if (!(ste->ste_flags & ST_INDEL) && |
---|
1427 | !(ste->ste_flags & ST_DOINGDEL)) |
---|
1428 | { |
---|
1429 | t += ste->ste_pages; |
---|
1430 | f += ste->ste_free; |
---|
1431 | } |
---|
1432 | ste++; |
---|
1433 | } |
---|
1434 | |
---|
1435 | /* fill in the results */ |
---|
1436 | *total = t; |
---|
1437 | *fr = f; |
---|
1438 | free(swt); |
---|
1439 | #endif /* SC_AINFO */ |
---|
1440 | } |
---|
1441 | #endif /* USE_ANONINFO */ |
---|
1442 | |
---|
1443 | /* |
---|
1444 | * When we reach a proc limit, we need to realloc the stuff. |
---|
1445 | */ |
---|
1446 | static void reallocproc(int n) |
---|
1447 | { |
---|
1448 | int bytes; |
---|
1449 | struct oldproc *op, *endbase; |
---|
1450 | |
---|
1451 | if (n < maxprocs) |
---|
1452 | return; |
---|
1453 | |
---|
1454 | maxprocs = n; |
---|
1455 | |
---|
1456 | /* allocate space for proc structure array and array of pointers */ |
---|
1457 | bytes = maxprocs * PRPSINFOSIZE; |
---|
1458 | pbase = (struct prpsinfo *) realloc(pbase, bytes); |
---|
1459 | pref = (struct prpsinfo **) realloc(pref, |
---|
1460 | maxprocs * sizeof(struct prpsinfo *)); |
---|
1461 | oldbase = (struct oldproc *) realloc(oldbase, |
---|
1462 | 2 * maxprocs * sizeof(struct oldproc)); |
---|
1463 | |
---|
1464 | /* Just in case ... */ |
---|
1465 | if (pbase == (struct prpsinfo *) NULL || pref == (struct prpsinfo **) NULL |
---|
1466 | || oldbase == (struct oldproc *) NULL) |
---|
1467 | { |
---|
1468 | fprintf (stderr, "%s: can't allocate sufficient memory\n", myname); |
---|
1469 | quit(1); |
---|
1470 | } |
---|
1471 | |
---|
1472 | /* |
---|
1473 | * We're growing from 0 to some number, only then we need to |
---|
1474 | * init the oldproc stuff |
---|
1475 | */ |
---|
1476 | if (!oldprocs) { |
---|
1477 | oldprocs = 2 * maxprocs; |
---|
1478 | |
---|
1479 | endbase = oldbase + oldprocs; |
---|
1480 | for (op = oldbase; op < endbase; op++) |
---|
1481 | op->oldpid = -1; |
---|
1482 | } |
---|
1483 | } |
---|