source: trunk/third/top/machine/m_sco.c @ 16185

Revision 16185, 15.7 KB checked in by ghudson, 23 years ago (diff)
This commit was generated by cvs2svn to compensate for changes in r16184, which included commits to RCS files with non-trunk default branches.
Line 
1/*
2 * top - a top users display for Unix
3 *
4 * SYNOPSIS:  SCO UNIX
5 *
6 * DESCRIPTION:
7 * This is the machine-dependent module for SCO UNIX.
8 * Originally written for BSD4.3 system by Christos Zoulas.
9 * Works for:
10 * SCO UNIX 3.2v4.2
11 *
12 * CFLAGS: -iBCS2
13 *
14 * You HAVE to use compiler option: -iBCS2
15 *       (Enforces strict Intel Binary Compatibility Standard 2 conformance.)
16 *
17 * AUTHOR:  Gregory Shilin <shilin@onyx.co.il>
18 *          Georgi Kuzmanov <georgi@sco.aubg.bg>
19 */
20
21#include <sys/types.h>
22#include <sys/param.h>
23
24#include <stdio.h>
25#include <unistd.h>
26#include <fcntl.h>
27#include <nlist.h>
28#include <math.h>
29#include <signal.h>
30
31#include <sys/dir.h>
32#include <sys/immu.h>
33#include <sys/region.h>
34#include <sys/proc.h>
35#include <sys/user.h>
36#include <sys/sysinfo.h>
37#include <sys/systm.h>
38#include <sys/sysmacros.h>
39#include <sys/var.h>
40#include <sys/sysi86.h>
41
42#include "top.h"
43#include "machine.h"
44#include "utils.h"
45#include "loadavg.h"
46
47typedef unsigned long  ulong;
48typedef unsigned int   uint;
49typedef unsigned short ushort;
50typedef unsigned char  uchar;
51
52#define VMUNIX  "/unix"
53#define KMEM    "/dev/kmem"
54#define MEM     "/dev/mem"
55
56/* get_process_info passes back a handle. This is what it looks like: */
57struct handle {
58   struct proc **next_proc; /* points to next valid proc pointer */
59   int           remaining; /* number of pointers remaining */
60};
61
62/* define what weighted cpu is */
63#define weighted_cpu(pct, pp) ((pp)->p_time == 0 ? 0.0 : \
64                         ((pct) / (1.0 - exp((pp)->p_time * logcpu))))
65
66#define bytetok(bytes) ((bytes) >> 10)
67
68/* what we consider to be process size: */
69#define PROCSIZE(up) bytetok(ctob((up)->u_tsize + (up)->u_dsize +(up)->u_ssize))
70
71/* definitions for indices in the nlist array */
72#define X_V             0  /* System configuration information */
73#define X_PROC          1  /* process tables */
74#define X_FREEMEM       2  /* current free memory */
75#define X_AVAILRMEM     3  /* available resident (not swappable) mem in pages */
76#define X_AVAILSMEM     4  /* available swappable memory in pages */
77#define X_MAXMEM        5  /* maximum available free memory in clicks */
78#define X_PHYSMEM       6  /* physical memory in clicks */
79#define X_NSWAP         7  /* size of swap space in blocks */
80#define X_HZ            8  /* ticks/second of the clock */
81#define X_MPID          9  /* last process id */
82#define X_SYSINFO       10 /* system information (cpu states) */
83#define X_CUR_CPU       11
84#define X_AVENRUN       12 /* load averages */
85
86
87static struct nlist nlst[] = {
88   { "v" },             /* 0 */
89   { "proc" },          /* 1 */
90   { "freemem" },       /* 2 */
91   { "availrmem" },     /* 3 */
92   { "availsmem" },     /* 4 */
93   { "maxmem" },        /* 5 */
94   { "physmem" },       /* 6 */
95   { "nswap" },         /* 7 */
96   { "Hz" },            /* 8 */
97   { "mpid" },          /* 9 */
98   { "sysinfo" },       /* 10 */
99   { "cur_cpu" },       /* 11 */
100   { "avenrun" },       /* 12 */
101   { NULL }
102};
103
104/*
105 *  These definitions control the format of the per-process area
106 */
107
108static char header[] =
109  "  PID X        PRI NICE   SIZE   RES  STATE   TIME  COMMAND";
110/* 0123456   -- field to fill in starts at header+6 */
111#define UNAME_START 6
112
113#define Proc_format \
114        "%5d %-8.8s %3d %4d  %5s %5dK %-5s %6s  %.28s"
115
116static int kmem, mem;
117
118static double logcpu;
119
120/* these are retrieved from the kernel in _init */
121static int   Hz;
122static struct var   v;
123static ulong proca;
124static load_avg  cur_cpu;
125static unsigned short int anr[NUM_AVERAGES];
126
127
128/* these are for detailing the process states */
129int process_states[8];
130char *procstatenames[] = {
131    "", " sleeping, ", " running, ", " zombie, ", " stopped, ",
132    " created, ", " onproc, ", " xswapped, ",
133    NULL
134};
135
136/* process state names for the "STATE" column of the display */
137char *state_abbrev[] = {
138   "", "sleep", "run", "zomb", "stop", "create", "onpr", "swap"
139};
140
141/* these are for calculating cpu state percentages */
142#define CPUSTATES       5    /* definition from struct sysinfo */
143static time_t cp_time[CPUSTATES];
144static time_t cp_old[CPUSTATES];
145static time_t cp_diff[CPUSTATES];
146
147/* these are for detailing the cpu states */
148int cpu_states[CPUSTATES];
149char *cpustatenames[] = {
150    "idle", "user", "system", "wait", "sxbrk",
151    NULL
152};
153
154/* these are for detailing the memory statistics */
155int memory_stats[6];
156char *memorynames[] = {
157    "K phys, ", "K max, ", "K free, ", "K locked, ", "K unlocked, ", "K swap, ", NULL
158};
159
160/* these are for keeping track of the proc array */
161static int bytes;
162static int pref_len;
163static struct proc *pbase;
164static struct proc **pref;
165
166/* useful externals */
167extern int errno;
168extern char *sys_errlist[];
169
170long time();
171long percentages();
172
173machine_init(statics)
174struct statics *statics;
175{
176ulong ptr;
177
178   if ((kmem = open(KMEM, O_RDONLY)) == -1) {
179      perror(KMEM);
180      return -1;
181   }
182   if ((mem = open(MEM, O_RDONLY)) == -1) {
183      perror(MEM);
184      return -1;
185   }
186
187   /* get the list of symbols we want to access in the kernel */
188   if (nlist(VMUNIX, nlst) == -1) {
189      fprintf(stderr, "top: nlist failed\n");
190      return -1;
191   }
192   /* make sure they were all found */
193   /*ZZ
194   if (check_nlist(nlst) > 0)
195      return -1;
196   */
197
198   proca = nlst[X_PROC].n_value;
199
200   /* get the symbol values out of kmem */
201   (void) getkval(nlst[X_CUR_CPU].n_value, (int *)(&cur_cpu), sizeof(cur_cpu),
202                  nlst[X_CUR_CPU].n_name);
203   (void) getkval(nlst[X_HZ].n_value,      (int *)(&Hz),      sizeof(Hz),
204                  nlst[X_HZ].n_name);
205   (void) getkval(nlst[X_V].n_value,       (int *)(&v),       sizeof(v),
206                  nlst[X_V].n_name);
207
208   /* this is used in calculating WCPU -- calculate it ahead of time */
209   logcpu = log(fabs(loaddouble(cur_cpu)));
210
211   /* allocate space for proc structure array and array of pointers */
212   bytes = v.v_proc * sizeof(struct proc);
213   pbase = (struct proc *)malloc(bytes);
214   pref  = (struct proc **)malloc(v.v_proc * sizeof(struct proc *));
215   if (pbase == (struct proc *)NULL || pref == (struct proc **)NULL) {
216      fprintf(stderr, "top: cannot allocate sufficient memory\n");
217      return -1;
218   }
219
220   /* fill in the statics information */
221   statics->procstate_names = procstatenames;
222   statics->cpustate_names = cpustatenames;
223   statics->memory_names = memorynames;
224
225   return 0;
226}
227
228char *format_header(uname_field)
229register char *uname_field;
230{
231    register char *ptr;
232
233    ptr = header + UNAME_START;
234    while (*uname_field != '\0')
235    {
236        *ptr++ = *uname_field++;
237    }
238
239    return(header);
240}
241
242get_system_info(si)
243struct system_info *si;
244{
245int i;
246long total;
247
248   /* get process id of the last process */
249   (void) getkval(nlst[X_MPID].n_value,  &(si->last_pid),  sizeof(si->last_pid),
250                  nlst[X_MPID].n_name);
251   /* get the cp_time array */
252   (void) getkval(nlst[X_SYSINFO].n_value, (int *)cp_time, sizeof(cp_time),
253                  nlst[X_SYSINFO].n_name);
254
255   /* get the load averages and convert them... */
256   (void) getkval(nlst[X_AVENRUN].n_value, (int *)(&anr[0]), sizeof(anr),
257                  nlst[X_AVENRUN].n_name);
258
259   for (i = 0; i < NUM_AVERAGES; i++)
260       si->load_avg[i] = (double)anr[i]/256.0;
261
262
263   /* convert cp_time counts to percentages */
264   total = percentages(CPUSTATES, cpu_states, cp_time, cp_old, cp_diff);
265
266   /* sum memory statistics */
267   (void) getkval(nlst[X_PHYSMEM].n_value, &memory_stats[0],
268                  sizeof(memory_stats[0]), nlst[X_PHYSMEM].n_name);
269   (void) getkval(nlst[X_MAXMEM].n_value, &memory_stats[1],
270                  sizeof(memory_stats[1]), nlst[X_MAXMEM].n_name);
271   (void) getkval(nlst[X_FREEMEM].n_value, &memory_stats[2],
272                  sizeof(memory_stats[2]), nlst[X_FREEMEM].n_name);
273   (void) getkval(nlst[X_AVAILRMEM].n_value, &memory_stats[3],
274                  sizeof(memory_stats[3]), nlst[X_AVAILRMEM].n_name);
275   (void) getkval(nlst[X_AVAILSMEM].n_value, &memory_stats[4],
276                  sizeof(memory_stats[4]), nlst[X_AVAILSMEM].n_name);
277   (void) getkval(nlst[X_NSWAP].n_value, &memory_stats[5],
278                  sizeof(memory_stats[5]), nlst[X_NSWAP].n_name);
279   memory_stats[0] = bytetok(ctob(memory_stats[0]));    /* clicks -> bytes    */
280   memory_stats[1] = bytetok(ctob(memory_stats[1]));    /* clicks -> bytes    */
281   memory_stats[2] = bytetok(ctob(memory_stats[2]));    /* clicks -> bytes    */
282   memory_stats[3] = bytetok(memory_stats[3] * NBPP);   /* # bytes per page   */
283   memory_stats[4] = bytetok(memory_stats[4] * NBPP);   /* # bytes per page   */
284   memory_stats[5] = bytetok(memory_stats[5] * NBPSCTR);/* # bytes per sector */
285
286   /* set arrays and strings */
287   si->cpustates = cpu_states;
288   si->memory = memory_stats;
289}
290
291static struct handle handle;
292
293caddr_t get_process_info(si, sel, compare)
294        struct system_info *si;
295        struct process_select *sel;
296        int (*compare)();
297{
298register int i;
299register int total_procs;
300register int active_procs;
301register struct proc **prefp;
302register struct proc *pp;
303
304/* set up flags of what we are going to select */
305/* these are copied out of sel for simplicity */
306int show_idle = sel->idle;
307int show_system = sel->system;
308int show_uid = sel->uid != -1;
309int show_command = sel->command != NULL;
310
311   /* read all the proc structures in one fell swoop */
312   (void) getkval(proca, (int *)pbase, bytes, "proc array");
313
314   /* get a pointer to the states summary array */
315   si->procstates = process_states;
316
317   /* count up process states and get pointers to interesting procs */
318   total_procs = active_procs = 0;
319   memset((char *)process_states, 0, sizeof(process_states));
320   prefp = pref;
321   for (pp = pbase, i = 0; i < v.v_proc; pp++, i++) {
322      /*
323       * Place pointers to each valid proc structure in pref[].
324       * Process slots that are actually in use have a non-zero
325       * status field. Processes with SSYS set are system processes --
326       * these are ignored unless show_system is set.
327       */
328      if (pp->p_stat && (show_system || ((pp->p_flag & SSYS) == 0))) {
329         total_procs++;
330         process_states[pp->p_stat]++;
331         if ((pp->p_stat != SZOMB) &&
332             (show_idle || (pp->p_stat == SRUN) || (pp->p_stat == SONPROC)) &&
333             (!show_uid || pp->p_uid == (ushort)sel->uid)) {
334                *prefp++ = pp;
335                active_procs++;
336         }
337      }
338   }
339
340   /* if requested, sort the "interesting" processes */
341   if (compare)
342      qsort((char *)pref, active_procs, sizeof(struct proc *), compare);
343
344   /* remember active and total counts */
345   si->p_total = total_procs;
346   si->p_active = pref_len = active_procs;
347
348   /* pass back a handle */
349   handle.next_proc = pref;
350   handle.remaining = active_procs;
351   return((caddr_t)&handle);
352}
353
354char fmt[128];          /* static area where result is built */
355
356char *format_next_process(handle, get_userid)
357      caddr_t handle;
358      char *(*get_userid)();
359{
360register struct proc *pp;
361register time_t cputime;
362register double pct;
363int where;
364struct user u;
365struct handle *hp;
366char command[29];
367
368   /* find and remember the next proc structure */
369   hp = (struct handle *)handle;
370   pp = *(hp->next_proc++);
371   hp->remaining--;
372
373   /* get the process's user struct and set cputime */
374   if ((where = sysi86(RDUBLK, pp->p_pid, &u, sizeof(struct user))) != -1)
375      where = (pp->p_flag & SLOAD) ? 0 : 1;
376   if (where == -1) {
377      strcpy(command, "<swapped>");
378      cputime = 0;
379   } else {
380      /* set u_comm for system processes */
381      if (u.u_comm[0] == '\0') {
382         if (pp->p_pid == 0)
383            strcpy(command, "Swapper");
384         else if (pp->p_pid == 2)
385            strcpy(command, "Pager");
386         else if (pp->p_pid == 3)
387            strcpy(command, "Sync'er");
388      } else if (where == 1) {
389         /* print swapped processes as <pname> */
390         register char *s1;
391
392         u.u_psargs[28 - 3] = '\0';
393         strcpy(command, "<");
394         strcat(command, strtok(u.u_psargs, " "));
395         strcat(command, ">");
396         while (s1 = (char *)strtok(NULL, " "))
397            strcat(command, s1);
398      } else {
399         sprintf(command, "%s", u.u_psargs);
400      }
401      cputime = u.u_utime + u.u_stime;
402   }
403   /* calculate the base for cpu percentages */
404   pct = pctdouble(pp->p_cpu);
405
406   /* format this entry */
407   sprintf(fmt,
408           Proc_format,
409           pp->p_pid,
410           (*get_userid)(pp->p_uid),
411           pp->p_pri - PZERO,
412           pp->p_nice - NZERO,
413           format_k(PROCSIZE(&u)),
414           0,
415           state_abbrev[pp->p_stat],
416           format_time(cputime / Hz),
417           printable(command));
418
419   return(fmt);
420}
421
422/*
423 * Checks the nlist to see if any symbols were not found.
424 * For every symbol that was not found, a one-line message
425 * is printed to stderr. The routine returns the number of
426 * symbols NOT founded.
427 */
428
429int check_nlist(nlst)
430    register struct nlist *nlst;
431{
432register int i = 0;
433
434   while (nlst->n_name) {
435      if (nlst->n_type == 0) {
436         fprintf(stderr, "kernel: no symbol named `%s'\n", nlst->n_name);
437         i++;
438      }
439      nlst++;
440   }
441   return i;
442}
443
444/*
445 *  getkval(offset, ptr, size, refstr) - get a value out of the kernel.
446 *      "offset" is the byte offset into the kernel for the desired value,
447 *      "ptr" points to a buffer into which the value is retrieved,
448 *      "size" is the size of the buffer (and the object to retrieve),
449 *      "refstr" is a reference string used when printing error meessages,
450 *          if "refstr" starts with a '!', then a failure on read will not
451 *          be fatal (this may seem like a silly way to do things, but I
452 *          really didn't want the overhead of another argument).
453 *     
454 */
455
456getkval(offset, ptr, size, refstr)
457unsigned long offset;
458int *ptr;
459int size;
460char *refstr;
461{
462   if (lseek(kmem, (long)offset, SEEK_SET) == -1) {
463      if (*refstr == '!')
464         refstr++;
465      fprintf(stderr, "%s: lseek to %s: %s\n", KMEM,
466               refstr, errmsg(errno));
467      quit(23);
468   }
469   if (read(kmem, (char *)ptr, size) == -1) {
470      if (*refstr == '!')
471         return 0;
472      fprintf(stderr, "%s: reading %s: %s\n", KMEM,
473               refstr, errmsg(errno));
474      quit(23);
475   }
476   return(1);
477}
478   
479/* comparison routine for qsort */
480/* NOTE: this is specific to the BSD proc structure, but it should
481   give you a good place to start. */
482
483/*
484 *  proc_compare - comparison function for "qsort"
485 *      Compares the resource consumption of two processes using five
486 *      distinct keys.  The keys (in descending order of importance) are:
487 *      percent cpu, cpu ticks, state, resident set size, total virtual
488 *      memory usage.  The process states are ordered as follows (from least
489 *      to most important):  WAIT, zombie, sleep, stop, start, run.  The
490 *      array declaration below maps a process state index into a number
491 *      that reflects this ordering.
492 */
493
494static unsigned char sorted_state[] =
495{
496    0,  /* not used             */
497    5,  /* sleep                */
498    6,  /* run                  */
499    2,  /* zombie               */
500    4,  /* stop                 */
501    1,  /* start                */
502    7,  /* onpr                 */
503    3,  /* swap                 */
504};
505 
506proc_compare(pp1, pp2)
507struct proc **pp1;
508struct proc **pp2;
509
510{
511register struct proc *p1;
512register struct proc *p2;
513register int result;
514register ulong lresult;
515
516   /* remove one level of indirection */
517   p1 = *pp1;
518   p2 = *pp2;
519
520   /* use process state to break the tie */
521   if ((result = sorted_state[p2->p_stat] -
522                 sorted_state[p1->p_stat])  == 0)
523   {
524      /* use priority to break the tie */
525      if ((result = p2->p_pri - p1->p_pri) == 0)
526      {
527         /* use time to break the tie */
528         if ((result = (p2->p_utime + p2->p_stime) -
529                       (p1->p_utime + p1->p_stime)) == 0)
530         {
531            /* use resident set size (rssize) to break the tie */
532            if ((result = p2->p_size - p1->p_size) == 0)
533            {
534               result = 0;
535            }
536         }
537      }
538   }
539
540    return(result);
541}
542
543/* returns uid of owner of process pid */
544proc_owner(pid)
545int pid;
546{
547register int cnt;
548register struct proc **prefp;
549register struct proc  *pp;
550
551   prefp = pref;
552   cnt = pref_len;
553   while (--cnt >= 0) {
554      if ((pp = *prefp++)->p_pid == (short)pid)
555         return ((int)pp->p_uid);
556   }
557   return(-1);
558}
559
560int setpriority(int dummy, int who, int nicewal)
561{
562   errno = 1;
563   return -1;
564}
565
566/* sigblock is not POSIX conformant */
567sigset_t sigblock (sigset_t mask)
568{
569sigset_t oset;
570
571   sigemptyset(&oset);
572   sigprocmask(SIG_BLOCK, &mask, &oset);
573   return oset;
574}
575
576/* sigsetmask is not POSIX conformant */
577sigsetmask(sigset_t mask)
578{
579sigset_t oset;
580
581   sigemptyset(&oset);
582   sigprocmask(SIG_SETMASK, &mask, &oset);
583   return oset;
584}
585
Note: See TracBrowser for help on using the repository browser.