Old s-svr4.c
  1 /* System dependent file for System V Release 4.0      */
  2 /* Tested on Unixware 1.1 (SVR4.2)                     */
  3 
  4 /* $Id: s-svr4.c,v 1.3 1996/01/04 20:12:08 jdd Exp $ */
  5 
  6 #include "s.h"
  7 
  8 #include <stdio.h>
  9 #include <stdlib.h>
 10 #include <string.h>
 11 #include <unistd.h>
 12 #include <fcntl.h>
 13 #include <nlist.h>
 14 #include <sys/types.h>
 15 #include <sys/mman.h>
 16 #include <sys/immu.h>
 17 
 18 #include <sys/utsname.h>
 19 #include <sys/sysinfo.h>
 20 
 21 #include <errno.h>
 22 
 23 extern int   diskflag, cpuflag;
 24 extern char *kernelSymbols;
 25 
 26 extern void draw_bar(int bar_num, int *states, int num_states);
 27 extern void display_bars(int);
 28 
 29 
 30 
 31 /*  The following defines are used to index the variable kernel_nl[] */
 32 #define KERNEL_UTSNAME   0
 33 #define KERNEL_SYSINFO   1
 34 #define KERNEL_LBOLT     2
 35 
 36 /* kernel_nl[]  --  list of symbol addresses needed.
 37 */
 38 static struct nlist kernel_nl[] =
 39 {
 40   { "utsname",       0 },
 41   { "sysinfo",       0 },
 42   { "lbolt",         0 },
 43 
 44   { 0,               0 }
 45 };
 46 
 47 
 48 
 49 /* kernel_symbols[] -- list of places to look for the running kernel's symbols
 50 */
 51 char *kernel_symbols[] = {
 52   "/stand/unix.diag",
 53   "/stand/unix",
 54   "/stand/unix.old",
 55   0,                   /* used by -kernel option  */
 56 
 57   0
 58 };
 59 
 60 
 61 
 62 enum stype
 63 {
 64   CPU_STATE,
 65 };
 66 
 67 
 68 enum stype      state = CPU_STATE;
 69 int             kmem;              /* file descriptor for /dev/kmem      */
 70 char           *kernel_name;       /* name of kernel (/stand/unix)       */
 71 struct utsname  nodename;          /* nodename.nodename                  */
 72 int             kmem_is_mapped;
 73 int             kmem_is_reading;
 74 int             kmem_can_map    = 1;
 75 
 76 
 77 /*  Fake kernel memory mapping information
 78 */
 79 typedef struct kmem_map
 80 {
 81   struct kmem_map *km_next;
 82   void            *km_addr;
 83   size_t           km_size;
 84   char             km_buf[4];
 85 } kmem_map_t;
 86 
 87 kmem_map_t *kmem_list;             /* linked list of fake memory mappings */
 88 
 89 /*  CPU Information
 90 */
 91 int             kncpu;             /* maximum number of cpu's on system  */
 92 int             ncpus;             /* number of cpu's which exist        */
 93 time_t         *lastcpu;           /* last snapshot of each cpu's times  */
 94 struct sysinfo *cpuinfo;
 95 
 96 
 97 
 98 #define CPU_STATE_SZ   sizeof(cpuinfo[0].cpu)
 99 
100 /* Computes the time spent in the specified state.
101 */
102 
103 #define delta(state) \
104    ((int) cpuinfo[cpu].cpu[state] - cputime[state])
105 
106 
107 /* Save the state times for a specific cpu
108 */
109 #define savecpuinfo(cpu) \
110    memcpy(cputime, cpuinfo[cpu].cpu, CPU_STATE_SZ);
111 
112 
113 /* The size of the local state time array
114 */
115 #define NSTATES   CPU_STATE_SZ
116 #define CPUSTATES (CPU_STATE_SZ / sizeof(cpuinfo[0].cpu[0]))
117 
118 /* Display an error message based upon errno and
119    return 0 to caller
120 */
121 #define ERR_EXIT(msg) \
122 {               \
123    perror(msg); \
124    return 0;    \
125 }
126 
127 
128 /* Read a specified number of bytes of kernel memory
129    and report any error conditions which might arise.
130 */
131 #define seekreadok(desc, seekpos, buf, sz, msg) \
132   if (-1 == lseek(desc, (off_t)seekpos, SEEK_SET))  \
133     ERR_EXIT("lseeking " msg);               \
134  \
135   if ((sz) != read(desc, buf, (sz)))         \
136     ERR_EXIT("reading " msg);
137 
138 #define seekreaderr(desc, seekpos, buf, sz, msg) \
139    ( -1  == lseek(desc, (off_t)seekpos, SEEK_SET) ||  \
140     (sz) != read(desc, buf, (sz)))
141 
142 
143 /* fakethis()  -- provide faked out memory mappings if unable to mmap kernel.
144 */
145 void *
146 fakethis(void *loc, size_t sz)
147 {
148   kmem_map_t *mapping = (kmem_map_t *)malloc( sizeof(kmem_map_t) + sz );
149 
150   if (!mapping)
151     return 0;
152 
153   mapping->km_addr = loc;
154   mapping->km_size = sz;
155   mapping->km_next = kmem_list;
156 
157   seekreadok(kmem, mapping->km_addr, mapping->km_buf, mapping->km_size,
158              "faking mmap");
159 
160   if (!kmem_list && kmem_can_map)
161     fprintf(stderr, "Unable to mmap kernel addresses, using lseek/read\n");
162 
163   kmem_list = mapping;
164   kmem_is_reading++;
165 
166   return (void *) mapping->km_buf;
167 }
168 
169 /* refreshfakes()  --  Refresh fake mmap snapshots.
170 */
171 int
172 refreshfakes(void)
173 {
174   kmem_map_t *mapping;
175   int         errs    = 0;
176 
177   for (mapping = kmem_list; mapping; mapping = mapping->km_next)
178     if (seekreaderr(kmem, mapping->km_addr, mapping->km_buf, mapping->km_size,
179                     "refreshing fake mmap's"))
180       errs++;
181 
182   return errs;
183 }
184 
185 /*  Given an address (and size in bytes) in kernel virtual memory,
186     this function uses the mmap function to provide a mapping for
187     those addresses in the current process.  The result is either
188     a pointer to the data in this processes virtual space or
189     (void *)-1 if unable to provide the mapping.
190 */
191 void *
192 mapthis(int desc, void *locp, unsigned long sz)
193 {
194   unsigned long pgoffs = (unsigned long) locp & POFFMASK;
195   unsigned long pgstrt = (unsigned long) locp & PG_ADDR;
196   unsigned long pgend  = ((unsigned long) locp + sz) & PG_ADDR;
197   unsigned long mapsz  = pgend - pgstrt + NBPP;
198   char         *map    = (char *)-1;
199 
200   if (kmem_can_map)
201     map = mmap(0, mapsz, PROT_READ, MAP_SHARED, desc, pgstrt);
202 
203   if ((char *)-1 == map)
204     {
205       if (kmem_is_mapped)
206         fprintf(stderr, "Mixing mapping and lseek/read kernel memory?\n");
207 
208       map = fakethis(locp, sz);
209       return map ? map : (void *)-1;
210     }
211 
212   kmem_is_mapped++;
213   return (void *) (map + pgoffs);
214 }
215 
216 
217 
218 /* get_kernel_symbols  --  Find the file containing the symbol definitions
219                            for the currently running kernel.
220 
221    The kernel must be running out of /stand (on an svr4 system) and the
222    symbol value for utsname must match the name returned by the uname()
223    system call.
224 
225    This function is called given a list of possible files from /stand which
226    may be running and returns the name of the first file which appears
227    to be correct.
228 */
229 
230 char *
231 get_kernel_symbols(int kmemfd, char **knamelist)
232 {
233   char **cpp;
234   int    nf, errs;
235   int    nsz = strlen(nodename.nodename) + 1;
236   struct utsname nname;
237 
238   if (1 >= nsz)
239     fprintf(stderr, "xcpustate: Empty node name\n");
240 
241   if (kernelSymbols)
242     *knamelist = kernelSymbols;
243 
244   if (0 == *knamelist) knamelist++;  /* skip over initial NULL pointer */
245 
246   for (cpp = knamelist; *cpp; cpp++)
247     {
248       for (nf = 0; kernel_nl[nf].n_name; nf++)
249         kernel_nl[nf].n_value = 0;
250 
251       nf = nlist(*cpp, kernel_nl);
252       if (-1 == nf || 0 == kernel_nl[KERNEL_UTSNAME].n_value)
253         continue;
254 
255       if (!seekreaderr(kmemfd, kernel_nl[KERNEL_UTSNAME].n_value,
256                      &nname, sizeof(struct utsname), "utsname") &&
257           0 == strncmp(nodename.nodename, nname.nodename, nsz))
258         {
259           for (errs = nf = 0; kernel_nl[nf].n_name; nf ++)
260             if (0 == kernel_nl[nf].n_value)
261               {
262                 fprintf(stderr, "kernel symbol `%s' not found\n",
263                         kernel_nl[nf].n_name);
264                 errs++;
265               }
266 
267           if (errs)
268             return 0;
269 
270           return *cpp;
271         }
272     }
273 
274   fprintf(stderr, "xcpustate: No kernel symbol file matches running kernel\n");
275   return 0;
276 }
277 
278 
279 
280 /* init_cpu_init()  --  Initialize for CPU display
281 */
282 int
283 init_cpu_info(void)
284 {
285   int nf;
286 
287   cpuinfo    = mapthis(kmem, (void *)kernel_nl[KERNEL_SYSINFO].n_value,
288                        sizeof(struct sysinfo));
289   if (-1 == (int)cpuinfo)
290     ERR_EXIT("mapping sysinfo");
291 
292   ncpus = kncpu = 1;
293 
294   nf = kncpu * CPU_STATE_SZ;
295   lastcpu = (time_t *) malloc(nf);
296   memset(lastcpu, 0, nf);
297 
298   return ncpus;
299 }
300 
301 
302 
303 /* Called at the beginning to inquire how many bars are needed
304 */
305 int
306 num_bars(void)
307 {
308   kmem   = open("/dev/kmem", O_RDONLY);
309   if (-1 == kmem)
310     ERR_EXIT("opening /dev/kmem");
311 
312   if (-1 == uname(&nodename))
313     ERR_EXIT("uname");
314 
315   kernel_name = get_kernel_symbols(kmem, kernel_symbols);
316   if (!kernel_name)
317     return 0;
318 
319   if (diskflag)
320     {
321       fprintf(stderr, "Only cpu state information is supported\n");
322       return 0;
323     }
324 
325   return init_cpu_info();
326 }
327 
328 
329 /*
330  * Indicates how many levels each bar has.  For most machines, each bar will
331  * have the same stuff.  But one can, for instance, display memory use on one
332  * bar, processor levels on others, etc.
333  */
334 void
335 bar_items(nbars, items)
336 int nbars;
337 int items[];    /* nbars items in this */
338 {
339     items[0] = CPUSTATES;
340 }
341 
342 
343 /* Called after num_bars to ask for bar names */
344 char **
345 label_cpu_bars(nbars)
346   int    nbars;
347 {
348   char **names;
349   int    hsz   = strlen(nodename.nodename);
350   int    rowsz = (hsz +   1 +   2 + 4) * kncpu;
351   int    sz    = nbars * rowsz;
352   int    cpu, i;
353   char  *cp;
354 
355   shorten(nodename.nodename);
356 
357   names  = (char **)malloc(sz + sizeof(char *) * nbars);
358 
359   cp     = (char *)names + sizeof(char *) * nbars;
360 
361   for (cpu = i = 0; i < kncpu; i++)
362     {
363       names[cpu++] = cp;
364 
365       if (1 == kncpu)
366         sprintf(cp, "%s", nodename.nodename);
367       else if (0 == i)
368         sprintf(cp, "%s #%d", nodename.nodename, i);
369       else
370         sprintf(cp, "%*s #%d", hsz, "", i);
371 
372       cp += rowsz;
373     }
374 
375   return names;
376 }
377 
378 
379 
380 char **
381 label_bars(nbars)
382   int    nbars;
383 {
384   switch (state)
385     {
386     case CPU_STATE:  return label_cpu_bars(nbars);
387     }
388 
389   return NULL;
390 }
391 
392 
393 
394 
395 /*  Called after the bars are created to do machine dependent inits */
396 void
397 init_bars(nbars)
398   int nbars;
399 {
400   (void) display_bars(nbars);
401 }
402 
403 
404 
405 
406 /*
407    This function is called every interval to compute and display
408    the bars.  It should call draw_bar() with the bar number, the array of
409    integer values to display in the bar, and the number of values in the
410    array.
411 */
412 void
413 display_cpu_bars(nbars)
414   int nbars;
415 {
416   int states[NSTATES];
417   int nstates;
418   int cpu, i;
419   time_t *cputime = lastcpu;
420 
421   for (i = cpu = 0; i < kncpu && cpu < nbars; i++)
422     {
423       nstates = 0;
424 
425       if (kncpu)
426         {
427           states[nstates++] = delta(CPU_IDLE);
428           states[nstates++] = delta(CPU_USER);
429           states[nstates++] = delta(CPU_KERNEL);
430           states[nstates++] = delta(CPU_WAIT) + delta(CPU_SXBRK);
431 #if 0
432           states[nstates++] = delta(CPU_SXBRK);
433 #endif
434         }
435       else
436         {
437           states[nstates++] = 100;  /* CPU_IDLE   */
438           states[nstates++] =   0;  /* CPU_USER   */
439           states[nstates++] =   0;  /* CPU_KERNEL */
440           states[nstates++] =   0;  /* CPU_WAIT   */
441 #if 0
442           states[nstates++] =   0;  /* CPU_SXBRK  */
443 #endif
444         }
445       savecpuinfo(cpu);
446       draw_bar(cpu, states, nstates);
447 
448       cpu++;
449       cputime += CPU_STATE_SZ / sizeof(time_t);
450     }
451 }
452 
453 
454 
455 void
456 display_bars(nbars)
457   int nbars;
458 {
459   if (kmem_list)
460     refreshfakes();
461 
462   switch (state)
463     {
464     case CPU_STATE:  display_cpu_bars(nbars);
465     }
466 
467   return;
468 }