1 /*
2 * Copyright (c) 1984 through 2008, William LeFebvre
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 *
8 * * Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 *
11 * * Redistributions in binary form must reproduce the above
12 * copyright notice, this list of conditions and the following disclaimer
13 * in the documentation and/or other materials provided with the
14 * distribution.
15 *
16 * * Neither the name of William LeFebvre nor the names of other
17 * contributors may be used to endorse or promote products derived from
18 * this software without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
23 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
24 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
26 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 */
32
33 /*
34 * top - a top users display for Unix
35 *
36 * SYNOPSIS: For Intel based System V Release 5 (Unixware7)
37 *
38 * DESCRIPTION:
39 * System V release 5 for i[3456]86
40 * Works for:
41 * i586-sco-sysv5uw7 i386 SCO UNIX_SVR5 (UnixWare 7)
42 *
43 * LIBS: -lelf -lmas
44 *
45 * CFLAGS: -DHAVE_GETOPT -DORDER
46 *
47 * AUTHORS: Mike Hopkirk <hops@sco.com>
48 * David Cutter <dpc@grail.com>
49 * Andrew Herbert <andrew@werple.apana.org.au>
50 * Robert Boucher <boucher@sofkin.ca>
51 */
52
53 /* build config
54 * SHOW_NICE - process nice fields don't seem to be being updated so changed
55 * default to display # of threads in use instead.
56 * define this to display nice fields (values always 0)
57 * #define SHOW_NICE 1
58 */
59
60 #define _KMEMUSER
61 #define prpsinfo psinfo
62 #include <sys/procfs.h>
63
64 #define pr_state pr_lwp.pr_state
65 #define pr_nice pr_lwp.pr_nice
66 #define pr_pri pr_lwp.pr_pri
67 #define pr_onpro pr_lwp.pr_onpro
68 #define ZOMBIE(p) ((p)->pr_nlwp == 0)
69 #define SIZE_K(p) pagetok((p)->pr_size)
70 #define RSS_K(p) pagetok((p)->pr_rssize)
71
72
73 #include <stdio.h>
74 #include <fcntl.h>
75 #include <unistd.h>
76 #include <stdlib.h>
77 #include <errno.h>
78 #include <dirent.h>
79 #include <nlist.h>
80 #include <string.h>
81 #include <sys/types.h>
82 #include <sys/param.h>
83 #include <sys/proc.h>
84 #include <sys/sysmacros.h>
85 #include <vm/anon.h>
86 #include <sys/priocntl.h>
87 #include <sys/tspriocntl.h>
88 #include <sys/var.h>
89
90 #include "top.h"
91 #include "machine.h"
92 #include "utils.h"
93
94 #define UNIX "/stand/unix"
95 #define KMEM "/dev/kmem"
96 #define PROCFS "/proc"
97 #define CPUSTATES 5
98
99 #ifndef PRIO_MAX
100 #define PRIO_MAX 20
101 #endif
102 #ifndef PRIO_MIN
103 #define PRIO_MIN -20
104 #endif
105
106 #ifndef FSCALE
107 #define FSHIFT 8 /* bits to right of fixed binary point */
108 #define FSCALE (1<<FSHIFT)
109 #endif
110
111 #define loaddouble(x) ((double)x/FSCALE)
112 #define pagetok(size) ((size) * pagesz) >> LOG1024
113
114 /* definitions for the index in the nlist array */
115 #define X_AVENRUN 0
116 #define X_V 1
117 #define X_MPID 2
118
119 static struct nlist nlst[] =
120 {
121 {"avenrun"}, /* 0 */
122 {"v"}, /* 1 */
123 {"nextpid"}, /* 2 */
124 {NULL}
125 };
126
127 static unsigned long avenrun_offset;
128 static unsigned long mpid_offset;
129
130 static unsigned int pagesz;
131
132 static void reallocproc(int n);
133 static int maxprocs;
134
135 /* get_process_info passes back a handle. This is what it looks like: */
136
137 struct handle
138 {
139 struct prpsinfo **next_proc;/* points to next valid proc pointer */
140 int remaining; /* number of pointers remaining */
141 };
142
143 /*
144 * These definitions control the format of the per-process area
145 */
146
147 static char header[] =
148 #ifdef SHOW_NICE
149 " PID X PRI NICE SIZE RES STATE TIME CPU COMMAND";
150 #else
151 " PID X PRI THR SIZE RES STATE TIME CPU COMMAND";
152 #endif
153 /* 0123456 -- field to fill in starts at header+6 */
154 #define UNAME_START 6
155 #define Proc_format \
156 "%5d %-8.8s %3d %4d %5s %5s %-5s %6s %8.4f%% %.16s"
157
158 char *state_abbrev[] =
159 {"oncpu", "run", "sleep", "stop", "idle", "zombie"};
160
161 #define sZOMB 5
162 int process_states[8];
163 char *procstatenames[] =
164 {
165 " on cpu, ", " running, ", " sleeping, ", " stopped, ",
166 " idling ", " zombie, ",
167 NULL
168 };
169
170 int cpu_states[CPUSTATES];
171 char *cpustatenames[] =
172 {"idle", "user", "kernel", "wait", NULL};
173
174
175 /* these are for detailing the memory statistics */
176 long memory_stats[5];
177 char *memorynames[] =
178 {"K phys, ", "K used, ", "K free, ", "K swapUsed, ", "K swapFree", NULL};
179
180 /* these are names given to allowed sorting orders -- first is default */
181 char *ordernames[] =
182 {"state", "cpu", "size", "res", "time", "pid", "uid", "rpid", "ruid", NULL};
183
184 /* forward definitions for comparison functions */
185 int proc_compare();
186 int compare_cpu();
187 int compare_size();
188 int compare_res();
189 int compare_time();
190 int compare_pid();
191 int compare_uid();
192 int compare_rpid();
193 int compare_ruid();
194
195 int (*proc_compares[])() = {
196 proc_compare,
197 compare_cpu,
198 compare_size,
199 compare_res,
200 compare_time,
201 compare_pid,
202 compare_uid,
203 compare_rpid,
204 compare_ruid,
205 NULL };
206
207
208 static int kmem = -1;
209 static int nproc;
210 static int bytes;
211 static struct prpsinfo *pbase;
212 static struct prpsinfo **pref;
213 static DIR *procdir;
214
215 /* useful externals */
216 extern int errno;
217 extern char *sys_errlist[];
218 extern char *myname;
219 extern long percentages ();
220 extern int check_nlist ();
221 extern int getkval ();
222 extern void perror ();
223 extern void getptable ();
224 extern void quit ();
225 extern int nlist ();
226
227 /* fwd dcls */
228 static int kmet_init(void );
229 static int get_cpustates(int *new);
230
231
232 int
machine_init(struct statics * statics)233 machine_init (struct statics *statics)
234 {
235 static struct var v;
236 int i;
237
238 /* fill in the statics information */
239 statics->procstate_names = procstatenames;
240 statics->cpustate_names = cpustatenames;
241 statics->memory_names = memorynames;
242 statics->order_names = ordernames;
243
244 /* get the list of symbols we want to access in the kernel */
245 if (nlist (UNIX, nlst))
246 {
247 (void) fprintf (stderr, "Unable to nlist %s\n", UNIX);
248 return (-1);
249 }
250
251 /* make sure they were all found */
252 if (check_nlist (nlst) > 0)
253 return (-1);
254
255 /* open kernel memory */
256 if ((kmem = open (KMEM, O_RDONLY)) == -1)
257 {
258 perror (KMEM);
259 return (-1);
260 }
261
262 v.v_proc=200; /* arbitrary default */
263 /* get the symbol values out of kmem */
264 /* NPROC Tuning parameter for max number of processes */
265 (void) getkval (nlst[X_V].n_value, &v, sizeof (struct var), nlst[X_V].n_name);
266 nproc = v.v_proc;
267 maxprocs = nproc;
268
269 /* stash away certain offsets for later use */
270 mpid_offset = nlst[X_MPID].n_value;
271 avenrun_offset = nlst[X_AVENRUN].n_value;
272
273 /* allocate space for proc structure array and array of pointers */
274 bytes = nproc * sizeof (struct prpsinfo);
275 pbase = (struct prpsinfo *) malloc (bytes);
276 pref = (struct prpsinfo **) malloc (nproc * sizeof (struct prpsinfo *));
277
278 pagesz = sysconf(_SC_PAGESIZE);
279
280
281 /* Just in case ... */
282 if (pbase == (struct prpsinfo *) NULL || pref == (struct prpsinfo **) NULL)
283 {
284 (void) fprintf (stderr, "%s: can't allocate sufficient memory\n", myname);
285 return (-1);
286 }
287
288 if (!(procdir = opendir (PROCFS)))
289 {
290 (void) fprintf (stderr, "Unable to open %s\n", PROCFS);
291 return (-1);
292 }
293
294 if (chdir (PROCFS))
295 { /* handy for later on when we're reading it */
296 (void) fprintf (stderr, "Unable to chdir to %s\n", PROCFS);
297 return (-1);
298 }
299
300
301 kmet_init();
302
303 /* all done! */
304 return (0);
305 }
306
307 char *
format_header(char * uname_field)308 format_header (char *uname_field)
309 {
310 register char *ptr;
311
312 ptr = header + UNAME_START;
313 while (*uname_field != '\0')
314 *ptr++ = *uname_field++;
315
316 return (header);
317 }
318
319 void
get_system_info(struct system_info * si)320 get_system_info (struct system_info *si)
321 {
322 long avenrun[3];
323 long mem;
324 static time_t cp_old[CPUSTATES];
325 static time_t cp_diff[CPUSTATES]; /* for cpu state percentages */
326 register int i;
327 static long swap_total;
328 static long swap_free;
329 int new_states[CPUSTATES];
330
331 get_cpustates(new_states);
332
333 /* convert cp_time counts to percentages */
334 (void) percentages (CPUSTATES, cpu_states, new_states, cp_old, cp_diff);
335
336
337 si->last_pid = -1;
338 /* get mpid -- process id of last process
339 * svr5 is nextpid - next pid to be assigned (already incremented)
340 */
341 (void) getkval (mpid_offset, &(si->last_pid), sizeof (si->last_pid),
342 "nextpid");
343 (si->last_pid)--; /* so we shld decrement for display */
344
345
346 /* get load average array */
347 (void) getkval (avenrun_offset, (int *) avenrun, sizeof (avenrun), "avenrun");
348 /* convert load averages to doubles */
349 for (i = 0; i < 3; i++)
350 si->load_avg[i] = loaddouble(avenrun[i]);
351
352 mem = sysconf(_SC_TOTAL_MEMORY); /* physical mem */
353 memory_stats[0] = pagetok (mem);
354
355 mem = kmet_get_freemem(); /* free mem */
356 memory_stats[2] = pagetok (mem);
357
358 /* mem = sysconf(_SC_GENERAL_MEMORY); */
359 memory_stats[1] = memory_stats[0] - memory_stats[2]; /* active */
360
361 get_swapinfo(&swap_total, &swap_free);
362 memory_stats[3] = pagetok(swap_total - swap_free);
363 memory_stats[4] = pagetok(swap_free);
364
365
366 /* set arrays and strings */
367 si->cpustates = cpu_states;
368 si->memory = memory_stats;
369 }
370
371 static struct handle handle;
372
373 caddr_t
get_process_info(struct system_info * si,struct process_select * sel,int idx)374 get_process_info (
375 struct system_info *si,
376 struct process_select *sel,
377 int idx)
378 {
379 register int i;
380 register int total_procs;
381 register int active_procs;
382 register struct prpsinfo **prefp;
383 register struct prpsinfo *pp;
384
385 /* these are copied out of sel for speed */
386 int show_idle;
387 int show_system;
388 int show_uid;
389
390 /* Get current number of processes */
391
392 /* read all the proc structures */
393 getptable (pbase);
394
395 /* get a pointer to the states summary array */
396 si->procstates = process_states;
397
398 /* set up flags which define what we are going to select */
399 show_idle = sel->idle;
400 show_system = sel->system;
401 show_uid = sel->uid != -1;
402
403 nproc = kmet_get_nproc();
404
405 /* count up process states and get pointers to interesting procs */
406 total_procs = 0;
407 active_procs = 0;
408 (void) memset (process_states, 0, sizeof (process_states));
409 prefp = pref;
410
411 for (pp = pbase, i = 0; i < nproc; pp++, i++)
412 {
413 /*
414 * Place pointers to each valid proc structure in pref[].
415 * Process slots that are actually in use have a non-zero
416 * status field. Processes with PR_ISSYS set are system
417 * processes---these get ignored unless show_sysprocs is set.
418 */
419 if ((pp->pr_state >= SONPROC && pp->pr_state <= SIDL) &&
420 (show_system || ((pp->pr_flag & PR_ISSYS) == 0)))
421 {
422 total_procs++;
423 process_states[pp->pr_state]++;
424 if ((!ZOMBIE(pp)) &&
425 (show_idle || (pp->pr_state == SRUN) || (pp->pr_state == SONPROC)) &&
426 (!show_uid || pp->pr_uid == (uid_t) sel->uid))
427 {
428 *prefp++ = pp;
429 active_procs++;
430 }
431 if (ZOMBIE(pp))
432 process_states[sZOMB]++; /* invented */
433
434 }
435 }
436
437 /* if requested, sort the "interesting" processes */
438 qsort ((char *) pref, active_procs, sizeof (struct prpsinfo *),
439 proc_compares[idx]);
440
441 /* remember active and total counts */
442 si->p_total = total_procs;
443 si->P_ACTIVE = active_procs;
444
445 /* pass back a handle */
446 handle.next_proc = pref;
447 handle.remaining = active_procs;
448 return ((caddr_t) & handle);
449 }
450
451 /*
452 * cpu percentage calculation is as fm ps.c
453 * seems to be ratio of (sys+user time used)/(elapsed time)
454 * i.e percent of cpu utilised when on cpu
455 */
percent_cpu(struct prpsinfo * pp)456 static double percent_cpu( struct prpsinfo *pp)
457 {
458 static time_t tim = 0L;
459 time_t starttime;
460 time_t ctime;
461 time_t etime;
462
463 /* if (tim == 0L) */
464 tim = time((time_t *) 0);
465 starttime = pp->pr_start.tv_sec;
466 if (pp->pr_start.tv_nsec > 500000000)
467 starttime++;
468 etime = (tim - starttime);
469 ctime = pp->pr_time.tv_sec;
470 if (pp->pr_time.tv_nsec > 500000000)
471 ctime++;
472 if (etime)
473 {
474 /* return (float)(ctime * 100) / (unsigned)etime; */
475 /* this was ocasionally giving vals >100 for some
476 * unknown reason so the below normalises it
477 */
478
479 double pct;
480 pct = (float)(ctime * 100) / (unsigned)etime;
481 return (pct < 100.0) ? pct : 100.00;
482 }
483 return 0.00;
484 }
485
486
487 char fmt[MAX_COLS]; /* static area where result is built */
488
489 char *
format_next_process(caddr_t handle,char * (* get_userid)())490 format_next_process (
491 caddr_t handle,
492 char *(*get_userid) ())
493 {
494 register struct prpsinfo *pp;
495 struct handle *hp;
496 register long cputime;
497 register double pctcpu;
498
499 /* find and remember the next proc structure */
500 hp = (struct handle *) handle;
501 pp = *(hp->next_proc++);
502 hp->remaining--;
503
504 /* get the cpu usage and calculate the cpu percentages */
505 cputime = pp->pr_time.tv_sec;
506 pctcpu = percent_cpu(pp);
507
508
509 /* format this entry */
510 (void) sprintf (fmt,
511 Proc_format,
512 pp->pr_pid,
513 (*get_userid) (pp->pr_uid),
514 pp->pr_pri,
515 #ifdef SHOW_NICE
516 pp->pr_nice,
517 #else
518 (u_short)pp->pr_nlwp < 999 ? (u_short)pp->pr_nlwp : 999,
519 #endif
520 format_k(SIZE_K(pp)),
521 format_k(RSS_K(pp)),
522 (ZOMBIE(pp)) ? state_abbrev[sZOMB]
523 : state_abbrev[pp->pr_state],
524 format_time(cputime),
525 /* 100.0 * */ pctcpu,
526 printable(pp->pr_fname));
527
528 /* return the result */
529 return (fmt);
530 }
531
532 /*
533 * check_nlist(nlst) - checks the nlist to see if any symbols were not
534 * found. For every symbol that was not found, a one-line
535 * message is printed to stderr. The routine returns the
536 * number of symbols NOT found.
537 */
538 int
check_nlist(register struct nlist * nlst)539 check_nlist (register struct nlist *nlst)
540 {
541 register int i;
542
543 /* check to see if we got ALL the symbols we requested */
544 /* this will write one line to stderr for every symbol not found */
545
546 i = 0;
547 while (nlst->n_name != NULL)
548 {
549 if (nlst->n_value == 0)
550 {
551 /* this one wasn't found */
552 (void) fprintf (stderr, "kernel: no symbol named `%s'\n", nlst->n_name);
553 i = 1;
554 }
555 nlst++;
556 }
557 return (i);
558 }
559
560
561 /*
562 * getkval(offset, ptr, size, refstr) - get a value out of the kernel.
563 * "offset" is the byte offset into the kernel for the desired value,
564 * "ptr" points to a buffer into which the value is retrieved,
565 * "size" is the size of the buffer (and the object to retrieve),
566 * "refstr" is a reference string used when printing error meessages,
567 * if "refstr" starts with a '!', then a failure on read will not
568 * be fatal (this may seem like a silly way to do things, but I
569 * really didn't want the overhead of another argument).
570 *
571 */
572 int
getkval(unsigned long offset,int * ptr,int size,char * refstr)573 getkval (
574 unsigned long offset,
575 int *ptr,
576 int size,
577 char *refstr)
578 {
579 if (lseek (kmem, (long) offset, 0) == -1)
580 {
581 if (*refstr == '!')
582 refstr++;
583 (void) fprintf (stderr, "%s: lseek to %s: %s\n",
584 myname, refstr, sys_errlist[errno]);
585 quit (22);
586 }
587 if (read (kmem, (char *) ptr, size) == -1)
588 if (*refstr == '!')
589 /* we lost the race with the kernel, process isn't in memory */
590 return (0);
591 else
592 {
593 (void) fprintf (stderr, "%s: reading %s: %s\n",
594 myname, refstr, sys_errlist[errno]);
595 quit (23);
596 }
597 return (1);
598 }
599
600 /* ----------------- comparison routines for qsort ---------------- */
601
602 /* First, the possible comparison keys. These are defined in such a way
603 that they can be merely listed in the source code to define the actual
604 desired ordering.
605 */
606
607 #define ORDERKEY_PCTCPU if (dresult = percent_cpu (p2) - percent_cpu (p1),\
608 (result = dresult > 0.0 ? 1 : \
609 dresult < 0.0 ? -1 : 0) == 0)
610
611 #define ORDERKEY_CPTICKS if ((result = p2->pr_time.tv_sec - p1->pr_time.tv_sec) == 0)
612 #define ORDERKEY_STATE if ((result = (long) (sorted_state[p2->pr_state] - \
613 sorted_state[p1->pr_state])) == 0)
614
615 #define ORDERKEY_PRIO if ((result = p2->pr_pri - p1->pr_pri) == 0)
616 #define ORDERKEY_RSSIZE if ((result = p2->pr_rssize - p1->pr_rssize) == 0)
617 #define ORDERKEY_MEM if ((result = (p2->pr_size - p1->pr_size)) == 0)
618
619 #define ORDERKEY_PID if ((result = (p2->pr_pid - p1->pr_pid)) == 0)
620 #define ORDERKEY_UID if ((result = (p2->pr_uid - p1->pr_uid)) == 0)
621 #define ORDERKEY_RPID if ((result = (p1->pr_pid - p2->pr_pid)) == 0)
622 #define ORDERKEY_RUID if ((result = (p1->pr_uid - p2->pr_uid)) == 0)
623
624 /* states enum {SONPROC, SRUN, SSLEEP, SSTOP, SIDL} */
625 unsigned char sorted_state[] =
626 {
627 7, /* onproc */
628 6, /* run */
629 5, /* sleep */
630 4, /* stop */
631 3, /* idle */
632 2, /* zombie */
633 0, /* unused */
634 0 /* unused */
635 };
636
637 #if 0
638 /*
639 * proc_compare - original singleton comparison function for "qsort"
640 * Compares the resource consumption of two processes using five
641 * distinct keys. The keys (in descending order of importance) are:
642 * percent cpu, cpu ticks, state, resident set size, total virtual
643 * memory usage. The process states are ordered as follows (from least
644 * to most important): WAIT, zombie, sleep, stop, start, run. The
645 * array declaration below maps a process state index into a number
646 * that reflects this ordering.
647 */
648 /* default comparison rtn */
649 int
650 original_proc_compare (
651 struct prpsinfo **pp1,
652 struct prpsinfo **pp2)
653 {
654 register struct prpsinfo *p1;
655 register struct prpsinfo *p2;
656 register long result;
657 double dresult;
658
659 /* remove one level of indirection */
660 p1 = *pp1;
661 p2 = *pp2;
662
663 /* compare percent cpu (pctcpu) */
664 dresult = percent_cpu(p2) - percent_cpu (p1);
665 result = dresult > 0.0 ? 1 :
666 dresult < 0.0 ? -1 : 0;
667 if (result)
668 {
669 /* use cpticks to break the tie */
670 if ((result = p2->pr_time.tv_sec - p1->pr_time.tv_sec) == 0)
671 {
672 /* use process state to break the tie */
673 if ((result = (long) (sorted_state[p2->pr_state] -
674 sorted_state[p1->pr_state])) == 0)
675 {
676 /* use priority to break the tie */
677 if ((result = p2->pr_pri - p1->pr_pri) == 0)
678 {
679 /* use resident set size (rssize) to break the tie */
680 if ((result = p2->pr_rssize - p1->pr_rssize) == 0)
681 {
682 /* use total memory to break the tie */
683 result = (p2->pr_size - p1->pr_size);
684 }
685 }
686 }
687 }
688 }
689 return (result);
690 }
691 #endif /* original comparison rtn */
692
693 /* compare_state - comparison function for sorting by state,pri,time,size */
694 int
proc_compare(struct prpsinfo ** pp1,struct prpsinfo ** pp2)695 proc_compare (
696 struct prpsinfo **pp1,
697 struct prpsinfo **pp2)
698 {
699 register struct prpsinfo *p1;
700 register struct prpsinfo *p2;
701 register long result;
702 double dresult;
703
704 /* remove one level of indirection */
705 p1 = *pp1;
706 p2 = *pp2;
707
708 ORDERKEY_STATE
709 ORDERKEY_PRIO
710 ORDERKEY_CPTICKS
711 ORDERKEY_RSSIZE
712 ORDERKEY_MEM
713 ORDERKEY_PCTCPU
714 ;
715
716 return (result);
717 }
718
719
720 /* compare_cpu - the comparison function for sorting by cpu % (deflt) */
721 int
compare_cpu(struct prpsinfo ** pp1,struct prpsinfo ** pp2)722 compare_cpu (
723 struct prpsinfo **pp1,
724 struct prpsinfo **pp2)
725 {
726 register struct prpsinfo *p1;
727 register struct prpsinfo *p2;
728 register long result;
729 double dresult;
730
731 /* remove one level of indirection */
732 p1 = *pp1;
733 p2 = *pp2;
734
735 ORDERKEY_PCTCPU
736 ORDERKEY_CPTICKS
737 ORDERKEY_STATE
738 ORDERKEY_PRIO
739 ORDERKEY_RSSIZE
740 ORDERKEY_MEM
741 ;
742
743 return (result);
744 }
745
746 /* compare_size - the comparison function for sorting by total memory usage */
747 int
compare_size(struct prpsinfo ** pp1,struct prpsinfo ** pp2)748 compare_size (
749 struct prpsinfo **pp1,
750 struct prpsinfo **pp2)
751 {
752 register struct prpsinfo *p1;
753 register struct prpsinfo *p2;
754 register long result;
755 double dresult;
756
757 /* remove one level of indirection */
758 p1 = *pp1;
759 p2 = *pp2;
760
761 ORDERKEY_MEM
762 ORDERKEY_RSSIZE
763 ORDERKEY_PCTCPU
764 ORDERKEY_CPTICKS
765 ORDERKEY_STATE
766 ORDERKEY_PRIO
767 ;
768
769 return (result);
770 }
771
772 /* compare_res - the comparison function for sorting by resident set size */
773 int
compare_res(struct prpsinfo ** pp1,struct prpsinfo ** pp2)774 compare_res (
775 struct prpsinfo **pp1,
776 struct prpsinfo **pp2)
777 {
778 register struct prpsinfo *p1;
779 register struct prpsinfo *p2;
780 register long result;
781 double dresult;
782
783 /* remove one level of indirection */
784 p1 = *pp1;
785 p2 = *pp2;
786
787 ORDERKEY_RSSIZE
788 ORDERKEY_MEM
789 ORDERKEY_PCTCPU
790 ORDERKEY_CPTICKS
791 ORDERKEY_STATE
792 ORDERKEY_PRIO
793 ;
794
795 return (result);
796 }
797
798 /* compare_time - the comparison function for sorting by total cpu time */
799 int
compare_time(struct prpsinfo ** pp1,struct prpsinfo ** pp2)800 compare_time (
801 struct prpsinfo **pp1,
802 struct prpsinfo **pp2)
803 {
804 register struct prpsinfo *p1;
805 register struct prpsinfo *p2;
806 register long result;
807 double dresult;
808
809 /* remove one level of indirection */
810 p1 = *pp1;
811 p2 = *pp2;
812
813 ORDERKEY_CPTICKS
814 ORDERKEY_PCTCPU
815 ORDERKEY_STATE
816 ORDERKEY_PRIO
817 ORDERKEY_MEM
818 ORDERKEY_RSSIZE
819 ;
820
821 return (result);
822 }
823
824 /* compare_pid - the comparison function for sorting by pid */
825 int
compare_pid(struct prpsinfo ** pp1,struct prpsinfo ** pp2)826 compare_pid (
827 struct prpsinfo **pp1,
828 struct prpsinfo **pp2)
829 {
830 register struct prpsinfo *p1;
831 register struct prpsinfo *p2;
832 register long result;
833 double dresult;
834
835 /* remove one level of indirection */
836 p1 = *pp1;
837 p2 = *pp2;
838
839 ORDERKEY_PID
840 ORDERKEY_CPTICKS
841 ORDERKEY_PCTCPU
842 ORDERKEY_STATE
843 ORDERKEY_PRIO
844 ORDERKEY_MEM
845 ORDERKEY_RSSIZE
846 ;
847
848 return (result);
849 }
850
851 /* compare_uid - the comparison function for sorting by user ID */
852 int
compare_uid(struct prpsinfo ** pp1,struct prpsinfo ** pp2)853 compare_uid (
854 struct prpsinfo **pp1,
855 struct prpsinfo **pp2)
856 {
857 register struct prpsinfo *p1;
858 register struct prpsinfo *p2;
859 register long result;
860 double dresult;
861
862 /* remove one level of indirection */
863 p1 = *pp1;
864 p2 = *pp2;
865
866 ORDERKEY_UID
867 ORDERKEY_CPTICKS
868 ORDERKEY_PCTCPU
869 ORDERKEY_STATE
870 ORDERKEY_PRIO
871 ORDERKEY_MEM
872 ORDERKEY_RSSIZE
873 ;
874
875 return (result);
876 }
877
878 /* compare_rpid - the comparison function for sorting by pid ascending */
879 int
compare_rpid(struct prpsinfo ** pp1,struct prpsinfo ** pp2)880 compare_rpid (
881 struct prpsinfo **pp1,
882 struct prpsinfo **pp2)
883 {
884 register struct prpsinfo *p1;
885 register struct prpsinfo *p2;
886 register long result;
887 double dresult;
888
889 /* remove one level of indirection */
890 p1 = *pp1;
891 p2 = *pp2;
892
893 ORDERKEY_RPID
894 ORDERKEY_CPTICKS
895 ORDERKEY_PCTCPU
896 ORDERKEY_STATE
897 ORDERKEY_PRIO
898 ORDERKEY_MEM
899 ORDERKEY_RSSIZE
900 ;
901
902 return (result);
903 }
904
905 /* compare_uid - the comparison function for sorting by user ID ascending */
906 int
compare_ruid(struct prpsinfo ** pp1,struct prpsinfo ** pp2)907 compare_ruid (
908 struct prpsinfo **pp1,
909 struct prpsinfo **pp2)
910 {
911 register struct prpsinfo *p1;
912 register struct prpsinfo *p2;
913 register long result;
914 double dresult;
915
916 /* remove one level of indirection */
917 p1 = *pp1;
918 p2 = *pp2;
919
920 ORDERKEY_RUID
921 ORDERKEY_CPTICKS
922 ORDERKEY_PCTCPU
923 ORDERKEY_STATE
924 ORDERKEY_PRIO
925 ORDERKEY_MEM
926 ORDERKEY_RSSIZE
927 ;
928
929 return (result);
930 }
931
932
933 /* ---------------- helper rtns ---------------- */
934
935 /*
936 * get process table
937 */
938 void
getptable(struct prpsinfo * baseptr)939 getptable (struct prpsinfo *baseptr)
940 {
941 struct prpsinfo *currproc; /* pointer to current proc structure */
942 int numprocs = 0;
943 struct dirent *direntp;
944
945 currproc = baseptr;
946 for (rewinddir (procdir); direntp = readdir (procdir);)
947 {
948 int fd;
949 char buf[30];
950
951 sprintf(buf,"%s/psinfo", direntp->d_name);
952
953 if ((fd = open (buf, O_RDONLY)) < 0)
954 continue;
955
956 if (read(fd, currproc, sizeof(psinfo_t)) != sizeof(psinfo_t))
957 {
958 (void) close (fd);
959 continue;
960 }
961
962 numprocs++;
963 currproc++;
964
965 (void) close (fd);
966
967 /* Atypical place for growth */
968 if (numprocs >= maxprocs)
969 {
970 reallocproc(2 * numprocs);
971 currproc = (struct prpsinfo *)
972 ((char *)baseptr + sizeof(psinfo_t) * numprocs);
973 }
974
975 }
976
977 if (nproc != numprocs)
978 nproc = numprocs;
979 }
980
981 /* return the owner of the specified process, for use in commands.c as we're
982 running setuid root */
983 int
proc_owner(int pid)984 proc_owner (int pid)
985 {
986 register struct prpsinfo *p;
987 int i;
988 for (i = 0, p = pbase; i < nproc; i++, p++)
989 if (p->pr_pid == (pid_t)pid)
990 return ((int)(p->pr_uid));
991
992 return (-1);
993 }
994
995 int
setpriority(int dummy,int who,int niceval)996 setpriority (int dummy, int who, int niceval)
997 {
998 int scale;
999 int prio;
1000 pcinfo_t pcinfo;
1001 pcparms_t pcparms;
1002 tsparms_t *tsparms;
1003
1004 strcpy (pcinfo.pc_clname, "TS");
1005 if (priocntl (0, 0, PC_GETCID, (caddr_t) & pcinfo) == -1)
1006 return (-1);
1007
1008 prio = niceval;
1009 if (prio > PRIO_MAX)
1010 prio = PRIO_MAX;
1011 else if (prio < PRIO_MIN)
1012 prio = PRIO_MIN;
1013
1014 tsparms = (tsparms_t *) pcparms.pc_clparms;
1015 scale = ((tsinfo_t *) pcinfo.pc_clinfo)->ts_maxupri;
1016 tsparms->ts_uprilim = tsparms->ts_upri = -(scale * prio) / 20;
1017 pcparms.pc_cid = pcinfo.pc_cid;
1018
1019 if (priocntl (P_PID, who, PC_SETPARMS, (caddr_t) & pcparms) == -1)
1020 return (-1);
1021
1022 return (0);
1023 }
1024
1025
get_swapinfo(long * total,long * fr)1026 get_swapinfo(long *total, long *fr)
1027 {
1028 register int cnt, i;
1029 register long t, f;
1030 struct swaptable *swt;
1031 struct swapent *ste;
1032 static char path[256];
1033
1034 /* get total number of swap entries */
1035 cnt = swapctl(SC_GETNSWP, 0);
1036
1037 /* allocate enough space to hold count + n swapents */
1038 swt = (struct swaptable *)malloc(sizeof(int) +
1039 cnt * sizeof(struct swapent));
1040 if (swt == NULL)
1041 {
1042 *total = 0;
1043 *fr = 0;
1044 return;
1045 }
1046 swt->swt_n = cnt;
1047
1048 /* fill in ste_path pointers: we don't care about the paths, so we point
1049 them all to the same buffer */
1050 ste = &(swt->swt_ent[0]);
1051 i = cnt;
1052 while (--i >= 0)
1053 {
1054 ste++->ste_path = path;
1055 }
1056
1057 /* grab all swap info */
1058 swapctl(SC_LIST, swt);
1059
1060 /* walk thru the structs and sum up the fields */
1061 t = f = 0;
1062 ste = &(swt->swt_ent[0]);
1063 i = cnt;
1064 while (--i >= 0)
1065 {
1066 /* dont count slots being deleted */
1067 if (!(ste->ste_flags & ST_INDEL) )
1068 {
1069 t += ste->ste_pages;
1070 f += ste->ste_free;
1071 }
1072 ste++;
1073 }
1074
1075 /* fill in the results */
1076 *total = t;
1077 *fr = f;
1078 free(swt);
1079 }
1080
1081
1082 /*
1083 * When we reach a proc limit, we need to realloc the stuff.
1084 */
reallocproc(int n)1085 static void reallocproc(int n)
1086 {
1087 int bytes;
1088 struct oldproc *op, *endbase;
1089
1090 if (n < maxprocs)
1091 return;
1092
1093 maxprocs = n;
1094
1095 /* allocate space for proc structure array and array of pointers */
1096 bytes = maxprocs * sizeof(psinfo_t) ;
1097 pbase = (struct prpsinfo *) realloc(pbase, bytes);
1098 pref = (struct prpsinfo **) realloc(pref,
1099 maxprocs * sizeof(struct prpsinfo *));
1100
1101 /* Just in case ... */
1102 if (pbase == (struct prpsinfo *) NULL || pref == (struct prpsinfo **) NULL)
1103 {
1104 fprintf (stderr, "%s: can't allocate sufficient memory\n", myname);
1105 quit(1);
1106 }
1107 }
1108
1109 /* ---------------------------------------------------------------- */
1110 /* Access kernel Metrics
1111 * SVR5 uses metreg inteface to Kernel statistics (metrics)
1112 * see /usr/include/mas.h, /usr/include/metreg.h
1113 */
1114
1115 #include <sys/mman.h>
1116 #include <sys/dl.h>
1117 #include <mas.h>
1118 #include <metreg.h>
1119
1120 static int md; /* metric descriptor handle */
1121 static uint32 ncpu; /* number of processors in system */
1122
1123 /* fwd dcls */
1124 static uint32 kmet_get_cpu( int type, char *desc);
1125 static void kmet_verify(
1126 uint32 md, metid_t id, units_t units, type_t mettype,
1127 uint32 metsz, uint32 nobj, uint32 nlocs, resource_t res_id,
1128 uint32 ressz ) ;
1129
1130
get_cpustates(int * new)1131 static int get_cpustates(int *new)
1132 {
1133 new[0] = (int)kmet_get_cpu( MPC_CPU_IDLE, "idle");
1134 new[1] = (int)kmet_get_cpu( MPC_CPU_USR, "usr");
1135 new[2] = (int)kmet_get_cpu( MPC_CPU_SYS, "sys");
1136 new[3] = (int)kmet_get_cpu( MPC_CPU_WIO, "wio");
1137 }
1138
1139
1140 /* initialises kernel metrics access and gets #cpus */
kmet_init()1141 static int kmet_init()
1142 {
1143 uint32 *ncpu_p;
1144
1145 /* open (and map in) the metric access file and assoc data structures */
1146 if( ( md = mas_open( MAS_FILE, MAS_MMAP_ACCESS ) ) < 0 )
1147 {
1148 (void)fprintf(stderr,"mas_open failed\n");
1149 mas_perror();
1150 quit(10);
1151 }
1152
1153 /* verify the NCPU metric is everything we expect */
1154 kmet_verify(md, NCPU, CPUS, CONFIGURABLE, sizeof(short),
1155 1, 1, MAS_SYSTEM, sizeof(uint32) );
1156
1157 /* get the number of cpu's on the system */
1158 if( (ncpu_p = (uint32 *)mas_get_met( md, NCPU, 0 )) == NULL )
1159 {
1160 (void)fprintf(stderr,"mas_get_met of ncpu failed\n");
1161 mas_perror();
1162 quit(12);
1163 }
1164 ncpu = (uint32)(*(short *)ncpu_p);
1165
1166 /* check that MPC_CPU_IDLE is of the form we expect
1167 * ( paranoically we should check the rest as well but ... )
1168 */
1169 kmet_verify( md, MPC_CPU_IDLE, TIX, PROFILE, sizeof(uint32),
1170 1, ncpu, NCPU, sizeof(short) );
1171
1172 kmet_verify( md, PROCUSE, PROCESSES, COUNT, sizeof(uint32),
1173 1, 1, MAS_SYSTEM, sizeof(uint32) );
1174 nproc = kmet_get_nproc();
1175
1176 return 0;
1177 }
1178
1179 /* done with kernel metrics access */
1180 static int
kmet_done()1181 kmet_done()
1182 {
1183 if ( mas_close( md ) < 0 )
1184 {
1185 (void)fprintf(stderr,"mas_close failed\n");
1186 mas_perror();
1187 quit(14);
1188 }
1189 }
1190
1191
1192 static uint32
kmet_get_cpu(int type,char * desc)1193 kmet_get_cpu( int type, char *desc)
1194 {
1195 int i;
1196 uint32 r=0, rtot=0 ;
1197
1198 for (i=0; i <ncpu; i++)
1199 {
1200 r=*(uint32 *)mas_get_met( md, (metid_t)type, 0 );
1201 if ( !r)
1202 {
1203 (void)fprintf(stderr,"mas_get_met of %s failed\n", desc);
1204 mas_perror();
1205 quit(12);
1206 }
1207 rtot += r; /* sum them for multi cpus */
1208 }
1209 return rtot /* /ncpu */ ;
1210 }
1211
1212 static int
kmet_get_freemem()1213 kmet_get_freemem()
1214 {
1215 dl_t *fm_p, fm, fmc, denom;
1216 time_t td1;
1217 static time_t td0;
1218 static dl_t fm_old;
1219
1220
1221 td1 = time(NULL);
1222 if ((fm_p = (dl_t *)mas_get_met( md, FREEMEM, 0 )) == NULL )
1223 {
1224 (void)fprintf(stderr,"mas_get_met of freemem failed\n");
1225 mas_perror();
1226 quit(12);
1227 }
1228 fm = *fm_p;
1229
1230 denom.dl_hop = 0;
1231 denom.dl_lop = (long) (td1 - td0);
1232 td0 = td1;
1233
1234 /* calculate the freemem difference divided by the time diff
1235 * giving the freemem in that time sample
1236 * (new - old) / (time_between_samples)
1237 */
1238 fmc = lsub(fm, fm_old);
1239 fm_old = fm;
1240
1241 fmc = ldivide(fmc, denom);
1242 return fmc.dl_lop;
1243 }
1244
1245 /*
1246 * return # of processes currently executing on system
1247 */
1248 static int
kmet_get_nproc()1249 kmet_get_nproc()
1250 {
1251 uint32 *p;
1252 if ((p = (uint32 *)mas_get_met( md, PROCUSE, 0 )) == NULL )
1253 {
1254 (void)fprintf(stderr,"mas_get_met of procuse failed\n");
1255 mas_perror();
1256 quit(11);
1257 }
1258 nproc = (int)*p;
1259 }
1260
1261
1262 /*
1263 * Function: kmet_verify
1264 * renamed from mas_usrtime example verify_met() fm Doug Souders
1265 *
1266 * Description: Verify the registration data associated with this metric
1267 * match what are expected. Cautious consumer applications
1268 * should do this sort of verification before using metrics.
1269 */
1270 static void
kmet_verify(uint32 md,metid_t id,units_t units,type_t mettype,uint32 metsz,uint32 nobj,uint32 nlocs,resource_t res_id,uint32 ressz)1271 kmet_verify(
1272 uint32 md, /* metric descriptor */
1273 metid_t id, /* metric id number */
1274 units_t units, /* expected units of metric */
1275 type_t mettype, /* expected type of metric */
1276 uint32 metsz, /* expected object size of metric */
1277 uint32 nobj, /* expected number of array elements */
1278 uint32 nlocs, /* expected number of instances */
1279 resource_t res_id, /* expected resource id number */
1280 uint32 ressz /* expected resource object size */
1281 )
1282 {
1283
1284 char *name; /* the name of the metric */
1285 units_t *units_p; /* the units of the metric */
1286 type_t *mettype_p; /* type field of the metric */
1287 uint32 *objsz_p; /* size of each element in met */
1288 uint32 *nobj_p; /* num of elements >1 then array*/
1289 uint32 *nlocs_p; /* total number of instances */
1290 uint32 *status_p; /* status word (update|avail) */
1291 resource_t *resource_p; /* the resource list of the met */
1292 uint32 *resval_p; /* pointer to resource */
1293 uint32 *ressz_p; /* size of the resource met */
1294
1295 if (!(name = mas_get_met_name( md, id )))
1296 {
1297 (void)fprintf(stderr,"mas_get_met_name failed\n");
1298 mas_perror();
1299 quit(11);
1300 }
1301
1302 if (!(status_p = mas_get_met_status( md, id )))
1303 {
1304 (void)fprintf(stderr,"mas_get_met_status of %s failed\n",
1305 name );
1306 mas_perror();
1307 quit(11);
1308 }
1309 if ( *status_p != MAS_AVAILABLE )
1310 {
1311 (void)fprintf(stderr,"unexpected status word for %s\n"
1312 "- expected %u got %u\n",
1313 name, MAS_AVAILABLE, *status_p );
1314 quit(11);
1315 }
1316 if (!(units_p = mas_get_met_units( md, id )))
1317 {
1318 (void)fprintf(stderr,"mas_get_met_units of %s failed\n",
1319 name );
1320 mas_perror();
1321 quit(11);
1322 }
1323 if (units != *units_p )
1324 {
1325 (void)fprintf(stderr,"unexpected units for %s\n"
1326 "- expected %u got %u\n",
1327 name, units, *units_p );
1328 quit(11);
1329 }
1330
1331 if (!(mettype_p = mas_get_met_type( md, id )))
1332 {
1333 (void)fprintf(stderr,"mas_get_met_type of %s failed\n",
1334 name );
1335 mas_perror();
1336 quit(11);
1337 }
1338 if (mettype != *mettype_p )
1339 {
1340 (void)fprintf(stderr,"unexpected metric type for %s\n"
1341 "- expected %u got %u\n",
1342 name, mettype , *mettype_p );
1343 quit(11);
1344 }
1345
1346 if (!(objsz_p = mas_get_met_objsz( md, id )))
1347 {
1348 (void)fprintf(stderr,"mas_get_met_objsz of %s failed\n", name );
1349 mas_perror();
1350 quit(11);
1351 }
1352 if (*objsz_p != metsz )
1353 {
1354 (void)fprintf(stderr,"unexpected object size for %s\n"
1355 "- expected %u got %u\n",
1356 name, metsz, *objsz_p );
1357 quit(11);
1358 }
1359
1360 if (!(nobj_p = mas_get_met_nobj( md, id )))
1361 {
1362 (void)fprintf(stderr,"mas_get_met_nobj of %s failed\n", name );
1363 mas_perror();
1364 quit(11);
1365 }
1366 if (nobj != *nobj_p )
1367 {
1368 (void)fprintf(stderr,"unexpected number of objects for %s\n"
1369 "- expected %u got %u\n",
1370 name, nobj, *nobj_p );
1371 quit(11);
1372 }
1373
1374 /* get the number of instances that libmas thinks it knows about */
1375 if (!(nlocs_p = mas_get_met_nlocs( md, id )))
1376 {
1377 (void)fprintf(stderr,"mas_get_met_nlocs of %s failed\n", name );
1378 mas_perror();
1379 quit(11);
1380 }
1381 if (nlocs != *nlocs_p )
1382 {
1383 (void)fprintf(stderr,"unexpected number of instances for %s"
1384 " - expected %u got %u\n",
1385 name, nlocs, *nlocs_p );
1386 quit(11);
1387
1388 }
1389 /* get the resource list for the metric */
1390 if (!(resource_p = mas_get_met_resources( md, id )))
1391 {
1392 (void)fprintf(stderr,"mas_get_met_resources of %s failed\n", name );
1393 mas_perror();
1394 quit(11);
1395 }
1396 if (*resource_p != res_id )
1397 {
1398 (void)fprintf(stderr,"unexpected resource id for %s\n"
1399 "- expected %u got %u\n",
1400 name, res_id, *resource_p);
1401 quit(11);
1402 }
1403 /* get the size of the resource */
1404 if (!(ressz_p = mas_get_met_objsz( md, (metid_t)(*resource_p) )))
1405 {
1406 (void)fprintf(stderr,"mas_get_met_objsz of resource failed\n");
1407 mas_perror();
1408 quit(11);
1409 }
1410 if (*ressz_p != ressz )
1411 {
1412 (void)fprintf(stderr,"unexpected resource size for %s\n"
1413 "- expected %u got %u\n",
1414 name, ressz, *ressz_p );
1415 quit(11);
1416 }
1417 /*
1418 * get the address of the resource
1419 */
1420 if (!(resval_p = (uint32 *)mas_get_met( md, *resource_p, 0 )))
1421 {
1422 (void)fprintf(stderr,"mas_get_met of resource failed\n");
1423 mas_perror();
1424 quit(11);
1425 }
1426 if (ressz == sizeof( short ) )
1427 {
1428 if( (uint32)(*(short *)resval_p) != nlocs )
1429 {
1430 (void)fprintf(stderr,"unexpected resource value for %s\n"
1431 "- expected %u got %u\n",
1432 name, nlocs, (uint32)(*(short *)resval_p) );
1433 quit(11);
1434 }
1435 }
1436 else
1437 { /* assume size of uint32 */
1438 if (*resval_p != nlocs )
1439 {
1440 (void)fprintf(stderr,"unexpected resource value for %s\n"
1441 "- expected %u got %u\n",
1442 name, nlocs, *resval_p );
1443 quit(11);
1444 }
1445 }
1446 return;
1447 }
1448
1449