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 }