document split envelope/header mailer-specific rewriting
[unix-history] / usr / src / usr.sbin / kgmon / kgmon.c
CommitLineData
528b0614 1/*
d7c7cf55
KB
2 * Copyright (c) 1983, 1992, 1993
3 * The Regents of the University of California. All rights reserved.
c82fa61b 4 *
32ce521f 5 * %sccs.include.redist.c%
528b0614
DF
6 */
7
8#ifndef lint
d7c7cf55
KB
9static char copyright[] =
10"@(#) Copyright (c) 1983, 1992, 1993\n\
11 The Regents of the University of California. All rights reserved.\n";
4d32c205 12#endif /* not lint */
528b0614 13
d841a9ba 14#ifndef lint
d7c7cf55 15static char sccsid[] = "@(#)kgmon.c 8.1 (Berkeley) %G%";
4d32c205 16#endif /* not lint */
d841a9ba 17
306e368d 18#include <sys/param.h>
769cbf71 19#include <sys/file.h>
fb8a29c5 20#include <sys/sysctl.h>
16d65440 21#include <sys/gmon.h>
d22f4fb8
CT
22#include <errno.h>
23#include <kvm.h>
24#include <limits.h>
d841a9ba 25#include <stdio.h>
d22f4fb8
CT
26#include <stdlib.h>
27#include <string.h>
d841a9ba
KM
28#include <nlist.h>
29#include <ctype.h>
7abf8d65 30#include <paths.h>
d841a9ba 31
d841a9ba 32struct nlist nl[] = {
16d65440
KM
33#define N_GMONPARAM 0
34 { "__gmonparam" },
8b8a3f9e 35#define N_PROFHZ 1
16d65440 36 { "_profhz" },
d841a9ba
KM
37 0,
38};
39
71a0f9c4
KM
40struct kvmvars {
41 kvm_t *kd;
42 struct gmonparam gpm;
43};
16d65440 44
ecc6febd 45int bflag, hflag, kflag, rflag, pflag;
78e8943b 46int debug = 0;
71a0f9c4
KM
47void setprof __P((struct kvmvars *kvp, int state));
48void dumpstate __P((struct kvmvars *kvp));
49void reset __P((struct kvmvars *kvp));
d841a9ba 50
71a0f9c4
KM
51int
52main(int argc, char **argv)
16d65440 53{
71a0f9c4
KM
54 extern char *optarg;
55 extern int optind;
56 int ch, mode, disp, accessmode;
57 struct kvmvars kvmvars;
58 char *system, *kmemf;
16d65440 59
71a0f9c4
KM
60 seteuid(getuid());
61 kmemf = NULL;
62 system = NULL;
63 while ((ch = getopt(argc, argv, "M:N:bhpr")) != EOF) {
64 switch((char)ch) {
16d65440 65
71a0f9c4
KM
66 case 'M':
67 kmemf = optarg;
68 kflag = 1;
69 break;
16d65440 70
71a0f9c4
KM
71 case 'N':
72 system = optarg;
73 break;
74
75 case 'b':
76 bflag = 1;
77 break;
78
79 case 'h':
80 hflag = 1;
81 break;
82
83 case 'p':
84 pflag = 1;
85 break;
86
87 case 'r':
88 rflag = 1;
89 break;
90
91 default:
92 (void)fprintf(stderr,
93 "usage: kgmon [-bhrp] [-M core] [-N system]\n");
94 exit(1);
95 }
96 }
97 argc -= optind;
98 argv += optind;
99
100#define BACKWARD_COMPATIBILITY
101#ifdef BACKWARD_COMPATIBILITY
102 if (*argv) {
103 system = *argv;
104 if (*++argv) {
105 kmemf = *argv;
106 ++kflag;
107 }
108 }
109#endif
110 if (system == NULL)
111 system = _PATH_UNIX;
112 accessmode = openfiles(system, kmemf, &kvmvars);
113 mode = getprof(&kvmvars);
114 if (hflag)
115 disp = GMON_PROF_OFF;
116 else if (bflag)
117 disp = GMON_PROF_ON;
118 else
119 disp = mode;
120 if (pflag)
121 dumpstate(&kvmvars);
122 if (rflag)
123 reset(&kvmvars);
124 if (accessmode == O_RDWR)
125 setprof(&kvmvars, disp);
126 (void)fprintf(stdout, "kgmon: kernel profiling is %s.\n",
127 disp == GMON_PROF_OFF ? "off" : "running");
128 return (0);
16d65440
KM
129}
130
131/*
71a0f9c4 132 * Check that profiling is enabled and open any ncessary files.
16d65440 133 */
71a0f9c4
KM
134openfiles(system, kmemf, kvp)
135 char *system;
136 char *kmemf;
137 struct kvmvars *kvp;
16d65440 138{
71a0f9c4
KM
139 int mib[3], state, size, openmode;
140 char errbuf[_POSIX2_LINE_MAX];
16d65440 141
71a0f9c4
KM
142 if (!kflag) {
143 mib[0] = CTL_KERN;
144 mib[1] = KERN_PROF;
145 mib[2] = GPROF_STATE;
146 size = sizeof state;
147 if (sysctl(mib, 3, &state, &size, NULL, 0) < 0) {
16d65440 148 (void)fprintf(stderr,
71a0f9c4
KM
149 "kgmon: profiling not defined in kernel.\n");
150 exit(20);
16d65440 151 }
71a0f9c4
KM
152 if (!(bflag || hflag || rflag ||
153 (pflag && state == GMON_PROF_ON)))
154 return (O_RDONLY);
155 (void)seteuid(0);
156 if (sysctl(mib, 3, NULL, NULL, &state, size) >= 0)
157 return (O_RDWR);
158 (void)seteuid(getuid());
159 kern_readonly(state);
160 return (O_RDONLY);
161 }
162 openmode = (bflag || hflag || pflag || rflag) ? O_RDWR : O_RDONLY;
163 kvp->kd = kvm_openfiles(system, kmemf, NULL, openmode, errbuf);
164 if (kvp->kd == NULL) {
165 if (openmode == O_RDWR) {
166 openmode = O_RDONLY;
167 kvp->kd = kvm_openfiles(system, kmemf, NULL, O_RDONLY,
168 errbuf);
16d65440 169 }
71a0f9c4
KM
170 if (kvp->kd == NULL) {
171 (void)fprintf(stderr, "kgmon: kvm_openfiles: %s\n",
172 errbuf);
173 exit(2);
174 }
175 kern_readonly(GMON_PROF_ON);
176 }
177 if (kvm_nlist(kvp->kd, nl) < 0) {
178 (void)fprintf(stderr, "kgmon: %s: no namelist\n", system);
179 exit(3);
180 }
181 if (!nl[N_GMONPARAM].n_value) {
182 (void)fprintf(stderr,
183 "kgmon: profiling not defined in kernel.\n");
184 exit(20);
185 }
186 return (openmode);
187}
188
189/*
190 * Suppress options that require a writable kernel.
191 */
192kern_readonly(mode)
193 int mode;
194{
195
196 (void)fprintf(stderr, "kgmon: kernel read-only: ");
197 if (pflag && mode == GMON_PROF_ON)
198 (void)fprintf(stderr, "data may be inconsistent\n");
199 if (rflag)
200 (void)fprintf(stderr, "-r supressed\n");
201 if (bflag)
202 (void)fprintf(stderr, "-b supressed\n");
203 if (hflag)
204 (void)fprintf(stderr, "-h supressed\n");
205 rflag = bflag = hflag = 0;
206}
207
208/*
209 * Get the state of kernel profiling.
210 */
211getprof(kvp)
212 struct kvmvars *kvp;
213{
214 int mib[3], size;
215
216 if (kflag) {
217 size = kvm_read(kvp->kd, nl[N_GMONPARAM].n_value, &kvp->gpm,
218 sizeof kvp->gpm);
219 } else {
220 mib[0] = CTL_KERN;
221 mib[1] = KERN_PROF;
222 mib[2] = GPROF_GMONPARAM;
223 size = sizeof kvp->gpm;
224 if (sysctl(mib, 3, &kvp->gpm, &size, NULL, 0) < 0)
225 size = 0;
226 }
227 if (size != sizeof kvp->gpm) {
228 (void)fprintf(stderr, "kgmon: cannot get gmonparam: %s\n",
229 kflag ? kvm_geterr(kvp->kd) : strerror(errno));
230 exit (4);
16d65440 231 }
71a0f9c4 232 return (kvp->gpm.state);
16d65440
KM
233}
234
235/*
236 * Enable or disable kernel profiling according to the state variable.
237 */
238void
71a0f9c4
KM
239setprof(kvp, state)
240 struct kvmvars *kvp;
241 int state;
16d65440
KM
242{
243 struct gmonparam *p = (struct gmonparam *)nl[N_GMONPARAM].n_value;
71a0f9c4 244 int mib[3], sz, oldstate;
fb8a29c5
KM
245
246 sz = sizeof(state);
247 if (!kflag) {
248 mib[0] = CTL_KERN;
249 mib[1] = KERN_PROF;
250 mib[2] = GPROF_STATE;
71a0f9c4
KM
251 if (sysctl(mib, 3, &oldstate, &sz, NULL, 0) < 0)
252 goto bad;
253 if (oldstate == state)
fb8a29c5 254 return;
71a0f9c4
KM
255 (void)seteuid(0);
256 if (sysctl(mib, 3, NULL, NULL, &state, sz) >= 0) {
257 (void)seteuid(getuid());
258 return;
259 }
260 (void)seteuid(getuid());
261 } else if (kvm_write(kvp->kd, (u_long)&p->state, (void *)&state, sz)
262 == sz)
fb8a29c5 263 return;
71a0f9c4 264bad:
fb8a29c5
KM
265 (void)fprintf(stderr, "kgmon: warning: cannot turn profiling %s\n",
266 state == GMON_PROF_OFF ? "off" : "on");
16d65440
KM
267}
268
269/*
270 * Build the gmon.out file.
271 */
272void
71a0f9c4
KM
273dumpstate(kvp)
274 struct kvmvars *kvp;
16d65440
KM
275{
276 register FILE *fp;
277 struct rawarc rawarc;
278 struct tostruct *tos;
279 u_long frompc, addr;
71a0f9c4
KM
280 u_short *froms, *tickbuf;
281 int mib[3], i;
282 struct gmonhdr h;
16d65440 283 int fromindex, endfrom, toindex;
16d65440 284
71a0f9c4 285 setprof(kvp, GMON_PROF_OFF);
16d65440
KM
286 fp = fopen("gmon.out", "w");
287 if (fp == 0) {
288 perror("gmon.out");
289 return;
290 }
71a0f9c4
KM
291
292 /*
293 * Build the gmon header and write it to a file.
294 */
295 bzero(&h, sizeof(h));
296 h.lpc = kvp->gpm.lowpc;
297 h.hpc = kvp->gpm.highpc;
298 h.ncnt = kvp->gpm.kcountsize + sizeof(h);
299 h.version = GMONVERSION;
300 h.profrate = getprofhz(kvp);
301 fwrite((char *)&h, sizeof(h), 1, fp);
302
303 /*
304 * Write out the tick buffer.
305 */
306 mib[0] = CTL_KERN;
307 mib[1] = KERN_PROF;
308 if ((tickbuf = (u_short *)malloc(kvp->gpm.kcountsize)) == NULL) {
309 fprintf(stderr, "kgmon: cannot allocate kcount space\n");
310 exit (5);
16d65440 311 }
71a0f9c4
KM
312 if (kflag) {
313 i = kvm_read(kvp->kd, (u_long)kvp->gpm.kcount, (void *)tickbuf,
314 kvp->gpm.kcountsize);
315 } else {
316 mib[2] = GPROF_COUNT;
317 i = kvp->gpm.kcountsize;
318 if (sysctl(mib, 3, tickbuf, &i, NULL, 0) < 0)
319 i = 0;
320 }
321 if (i != kvp->gpm.kcountsize) {
322 (void)fprintf(stderr, "kgmon: read ticks: read %u, got %d: %s",
323 kvp->gpm.kcountsize, i,
324 kflag ? kvm_geterr(kvp->kd) : strerror(errno));
16d65440
KM
325 exit(6);
326 }
71a0f9c4
KM
327 if ((fwrite(tickbuf, kvp->gpm.kcountsize, 1, fp)) != 1) {
328 perror("kgmon: writing tocks to gmon.out");
329 exit(7);
330 }
331 free(tickbuf);
332
333 /*
334 * Write out the arc info.
335 */
336 if ((froms = (u_short *)malloc(kvp->gpm.fromssize)) == NULL) {
337 fprintf(stderr, "kgmon: cannot allocate froms space\n");
338 exit (8);
339 }
340 if (kflag) {
341 i = kvm_read(kvp->kd, (u_long)kvp->gpm.froms, (void *)froms,
342 kvp->gpm.fromssize);
343 } else {
344 mib[2] = GPROF_FROMS;
345 i = kvp->gpm.fromssize;
346 if (sysctl(mib, 3, froms, &i, NULL, 0) < 0)
347 i = 0;
348 }
349 if (i != kvp->gpm.fromssize) {
350 (void)fprintf(stderr, "kgmon: read froms: read %u, got %d: %s",
351 kvp->gpm.fromssize, i,
352 kflag ? kvm_geterr(kvp->kd) : strerror(errno));
353 exit(9);
354 }
355 if ((tos = (struct tostruct *)malloc(kvp->gpm.tossize)) == NULL) {
356 fprintf(stderr, "kgmon: cannot allocate tos space\n");
357 exit(10);
358 }
359 if (kflag) {
360 i = kvm_read(kvp->kd, (u_long)kvp->gpm.tos, (void *)tos,
361 kvp->gpm.tossize);
362 } else {
363 mib[2] = GPROF_TOS;
364 i = kvp->gpm.tossize;
365 if (sysctl(mib, 3, tos, &i, NULL, 0) < 0)
366 i = 0;
367 }
368 if (i != kvp->gpm.tossize) {
369 (void)fprintf(stderr, "kgmon: read tos: read %u, got %d: %s",
370 kvp->gpm.tossize, i,
371 kflag ? kvm_geterr(kvp->kd) : strerror(errno));
372 exit(11);
373 }
16d65440 374 if (debug)
71a0f9c4
KM
375 (void)fprintf(stderr, "kgmon: lowpc 0x%x, textsize 0x%x\n",
376 kvp->gpm.lowpc, kvp->gpm.textsize);
377 endfrom = kvp->gpm.fromssize / sizeof(*froms);
16d65440
KM
378 for (fromindex = 0; fromindex < endfrom; ++fromindex) {
379 if (froms[fromindex] == 0)
380 continue;
71a0f9c4
KM
381 frompc = (u_long)kvp->gpm.lowpc +
382 (fromindex * kvp->gpm.hashfraction * sizeof(*froms));
16d65440
KM
383 for (toindex = froms[fromindex]; toindex != 0;
384 toindex = tos[toindex].link) {
385 if (debug)
386 (void)fprintf(stderr,
71a0f9c4
KM
387 "%s: [mcleanup] frompc 0x%x selfpc 0x%x count %d\n",
388 "kgmon", frompc, tos[toindex].selfpc,
389 tos[toindex].count);
16d65440
KM
390 rawarc.raw_frompc = frompc;
391 rawarc.raw_selfpc = (u_long)tos[toindex].selfpc;
392 rawarc.raw_count = tos[toindex].count;
393 fwrite((char *)&rawarc, sizeof(rawarc), 1, fp);
394 }
395 }
396 fclose(fp);
397}
398
399/*
71a0f9c4 400 * Get the profiling rate.
16d65440
KM
401 */
402int
71a0f9c4
KM
403getprofhz(kvp)
404 struct kvmvars *kvp;
16d65440 405{
71a0f9c4
KM
406 int mib[2], size, profrate;
407 struct clockinfo clockrate;
408
409 if (kflag) {
410 profrate = 1;
411 if (kvm_read(kvp->kd, nl[N_PROFHZ].n_value, &profrate,
412 sizeof profrate) != sizeof profrate)
413 (void)fprintf(stderr, "kgmon: get clockrate: %s\n",
414 kvm_geterr(kvp->kd));
415 return (profrate);
16d65440 416 }
71a0f9c4
KM
417 mib[0] = CTL_KERN;
418 mib[1] = KERN_CLOCKRATE;
419 clockrate.profhz = 1;
420 size = sizeof clockrate;
421 if (sysctl(mib, 2, &clockrate, &size, NULL, 0) < 0)
422 (void)fprintf(stderr, "kgmon: get clockrate: %s\n",
423 strerror(errno));
424 return (clockrate.profhz);
16d65440
KM
425}
426
427/*
428 * Reset the kernel profiling date structures.
429 */
430void
71a0f9c4
KM
431reset(kvp)
432 struct kvmvars *kvp;
16d65440 433{
71a0f9c4
KM
434 char *zbuf;
435 u_long biggest;
436 int mib[3];
437
438 setprof(kvp, GMON_PROF_OFF);
439
440 biggest = kvp->gpm.kcountsize;
441 if (kvp->gpm.fromssize > biggest)
442 biggest = kvp->gpm.fromssize;
443 if (kvp->gpm.tossize > biggest)
444 biggest = kvp->gpm.tossize;
445 if ((zbuf = (char *)malloc(biggest)) == NULL) {
446 fprintf(stderr, "kgmon: cannot allocate zbuf space\n");
447 exit(12);
16d65440 448 }
71a0f9c4
KM
449 bzero(zbuf, biggest);
450 if (kflag) {
451 if (kvm_write(kvp->kd, (u_long)kvp->gpm.kcount, zbuf,
452 kvp->gpm.kcountsize) != kvp->gpm.kcountsize) {
453 (void)fprintf(stderr, "kgmon: tickbuf zero: %s\n",
454 kvm_geterr(kvp->kd));
455 exit(13);
4d32c205 456 }
71a0f9c4
KM
457 if (kvm_write(kvp->kd, (u_long)kvp->gpm.froms, zbuf,
458 kvp->gpm.fromssize) != kvp->gpm.fromssize) {
459 (void)fprintf(stderr, "kgmon: froms zero: %s\n",
460 kvm_geterr(kvp->kd));
461 exit(14);
d22f4fb8 462 }
71a0f9c4
KM
463 if (kvm_write(kvp->kd, (u_long)kvp->gpm.tos, zbuf,
464 kvp->gpm.tossize) != kvp->gpm.tossize) {
465 (void)fprintf(stderr, "kgmon: tos zero: %s\n",
466 kvm_geterr(kvp->kd));
467 exit(15);
d841a9ba 468 }
71a0f9c4 469 return;
d841a9ba 470 }
71a0f9c4
KM
471 (void)seteuid(0);
472 mib[0] = CTL_KERN;
473 mib[1] = KERN_PROF;
474 mib[2] = GPROF_COUNT;
475 if (sysctl(mib, 3, NULL, NULL, zbuf, kvp->gpm.kcountsize) < 0) {
476 (void)fprintf(stderr, "kgmon: tickbuf zero: %s\n",
477 strerror(errno));
478 exit(13);
d22f4fb8 479 }
71a0f9c4
KM
480 mib[2] = GPROF_FROMS;
481 if (sysctl(mib, 3, NULL, NULL, zbuf, kvp->gpm.fromssize) < 0) {
482 (void)fprintf(stderr, "kgmon: froms zero: %s\n",
483 strerror(errno));
484 exit(14);
d841a9ba 485 }
71a0f9c4
KM
486 mib[2] = GPROF_TOS;
487 if (sysctl(mib, 3, NULL, NULL, zbuf, kvp->gpm.tossize) < 0) {
488 (void)fprintf(stderr, "kgmon: tos zero: %s\n",
489 strerror(errno));
490 exit(15);
79ec4952 491 }
71a0f9c4
KM
492 (void)seteuid(getuid());
493 free(zbuf);
d22f4fb8 494}