kgmon.c revision 1.8 1 /* $NetBSD: kgmon.c,v 1.8 1997/10/18 17:31:38 mrg Exp $ */
2
3 /*
4 * Copyright (c) 1983, 1992, 1993
5 * The Regents of the University of California. All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * 3. All advertising materials mentioning features or use of this software
16 * must display the following acknowledgement:
17 * This product includes software developed by the University of
18 * California, Berkeley and its contributors.
19 * 4. Neither the name of the University nor the names of its contributors
20 * may be used to endorse or promote products derived from this software
21 * without specific prior written permission.
22 *
23 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33 * SUCH DAMAGE.
34 */
35
36 #include <sys/cdefs.h>
37 #ifndef lint
38 __COPYRIGHT("@(#) Copyright (c) 1983, 1992, 1993\n\
39 The Regents of the University of California. All rights reserved.\n");
40 #endif /* not lint */
41
42 #ifndef lint
43 #if 0
44 static char sccsid[] = "from: @(#)kgmon.c 8.1 (Berkeley) 6/6/93";
45 #else
46 __RCSID("$NetBSD: kgmon.c,v 1.8 1997/10/18 17:31:38 mrg Exp $");
47 #endif
48 #endif /* not lint */
49
50 #include <sys/param.h>
51 #include <sys/file.h>
52 #include <sys/sysctl.h>
53 #include <sys/gmon.h>
54
55 #include <ctype.h>
56 #include <errno.h>
57 #include <kvm.h>
58 #include <limits.h>
59 #include <nlist.h>
60 #include <paths.h>
61 #include <stdio.h>
62 #include <stdlib.h>
63 #include <string.h>
64 #include <unistd.h>
65
66 struct nlist nl[] = {
67 #define N_GMONPARAM 0
68 { "__gmonparam" },
69 #define N_PROFHZ 1
70 { "_profhz" },
71 { 0, }
72 };
73
74 struct kvmvars {
75 kvm_t *kd;
76 struct gmonparam gpm;
77 };
78
79 int bflag, hflag, kflag, rflag, pflag;
80 int debug = 0;
81 void setprof __P((struct kvmvars *kvp, int state));
82 void dumpstate __P((struct kvmvars *kvp));
83 void reset __P((struct kvmvars *kvp));
84 int openfiles __P((char *, char *, struct kvmvars *));
85 int getprof __P((struct kvmvars *));
86 void kern_readonly __P((int));
87 int getprofhz __P(( struct kvmvars *));
88
89 int
90 main(int argc, char **argv)
91 {
92 int ch, mode, disp, accessmode;
93 struct kvmvars kvmvars;
94 char *system, *kmemf;
95
96 seteuid(getuid());
97 kmemf = NULL;
98 system = NULL;
99 while ((ch = getopt(argc, argv, "M:N:bhpr")) != EOF) {
100 switch((char)ch) {
101
102 case 'M':
103 kmemf = optarg;
104 kflag = 1;
105 break;
106
107 case 'N':
108 system = optarg;
109 break;
110
111 case 'b':
112 bflag = 1;
113 break;
114
115 case 'h':
116 hflag = 1;
117 break;
118
119 case 'p':
120 pflag = 1;
121 break;
122
123 case 'r':
124 rflag = 1;
125 break;
126
127 default:
128 (void)fprintf(stderr,
129 "usage: kgmon [-bhrp] [-M core] [-N system]\n");
130 exit(1);
131 }
132 }
133 argc -= optind;
134 argv += optind;
135
136 #define BACKWARD_COMPATIBILITY
137 #ifdef BACKWARD_COMPATIBILITY
138 if (*argv) {
139 system = *argv;
140 if (*++argv) {
141 kmemf = *argv;
142 ++kflag;
143 }
144 }
145 #endif
146 if (system == NULL)
147 system = _PATH_UNIX;
148 accessmode = openfiles(system, kmemf, &kvmvars);
149 mode = getprof(&kvmvars);
150 if (hflag)
151 disp = GMON_PROF_OFF;
152 else if (bflag)
153 disp = GMON_PROF_ON;
154 else
155 disp = mode;
156 if (pflag)
157 dumpstate(&kvmvars);
158 if (rflag)
159 reset(&kvmvars);
160 if (accessmode == O_RDWR)
161 setprof(&kvmvars, disp);
162 (void)fprintf(stdout, "kgmon: kernel profiling is %s.\n",
163 disp == GMON_PROF_OFF ? "off" : "running");
164 return (0);
165 }
166
167 /*
168 * Check that profiling is enabled and open any ncessary files.
169 */
170 int
171 openfiles(system, kmemf, kvp)
172 char *system;
173 char *kmemf;
174 struct kvmvars *kvp;
175 {
176 int mib[3], state, openmode;
177 size_t size;
178 char errbuf[_POSIX2_LINE_MAX];
179
180 if (!kflag) {
181 mib[0] = CTL_KERN;
182 mib[1] = KERN_PROF;
183 mib[2] = GPROF_STATE;
184 size = sizeof state;
185 if (sysctl(mib, 3, &state, &size, NULL, 0) < 0) {
186 (void)fprintf(stderr,
187 "kgmon: profiling not defined in kernel.\n");
188 exit(20);
189 }
190 if (!(bflag || hflag || rflag ||
191 (pflag && state == GMON_PROF_ON)))
192 return (O_RDONLY);
193 (void)seteuid(0);
194 if (sysctl(mib, 3, NULL, NULL, &state, size) >= 0)
195 return (O_RDWR);
196 (void)seteuid(getuid());
197 kern_readonly(state);
198 return (O_RDONLY);
199 }
200 openmode = (bflag || hflag || pflag || rflag) ? O_RDWR : O_RDONLY;
201 kvp->kd = kvm_openfiles(system, kmemf, NULL, openmode, errbuf);
202 if (kvp->kd == NULL) {
203 if (openmode == O_RDWR) {
204 openmode = O_RDONLY;
205 kvp->kd = kvm_openfiles(system, kmemf, NULL, O_RDONLY,
206 errbuf);
207 }
208 if (kvp->kd == NULL) {
209 (void)fprintf(stderr, "kgmon: kvm_openfiles: %s\n",
210 errbuf);
211 exit(2);
212 }
213 kern_readonly(GMON_PROF_ON);
214 }
215 if (kvm_nlist(kvp->kd, nl) < 0) {
216 (void)fprintf(stderr, "kgmon: %s: no namelist\n", system);
217 exit(3);
218 }
219 if (!nl[N_GMONPARAM].n_value) {
220 (void)fprintf(stderr,
221 "kgmon: profiling not defined in kernel.\n");
222 exit(20);
223 }
224 return (openmode);
225 }
226
227 /*
228 * Suppress options that require a writable kernel.
229 */
230 void
231 kern_readonly(mode)
232 int mode;
233 {
234
235 (void)fprintf(stderr, "kgmon: kernel read-only: ");
236 if (pflag && mode == GMON_PROF_ON)
237 (void)fprintf(stderr, "data may be inconsistent\n");
238 if (rflag)
239 (void)fprintf(stderr, "-r supressed\n");
240 if (bflag)
241 (void)fprintf(stderr, "-b supressed\n");
242 if (hflag)
243 (void)fprintf(stderr, "-h supressed\n");
244 rflag = bflag = hflag = 0;
245 }
246
247 /*
248 * Get the state of kernel profiling.
249 */
250 int
251 getprof(kvp)
252 struct kvmvars *kvp;
253 {
254 int mib[3];
255 size_t size;
256
257 if (kflag) {
258 size = kvm_read(kvp->kd, nl[N_GMONPARAM].n_value, &kvp->gpm,
259 sizeof kvp->gpm);
260 } else {
261 mib[0] = CTL_KERN;
262 mib[1] = KERN_PROF;
263 mib[2] = GPROF_GMONPARAM;
264 size = sizeof kvp->gpm;
265 if (sysctl(mib, 3, &kvp->gpm, &size, NULL, 0) < 0)
266 size = 0;
267 }
268 if (size != sizeof kvp->gpm) {
269 (void)fprintf(stderr, "kgmon: cannot get gmonparam: %s\n",
270 kflag ? kvm_geterr(kvp->kd) : strerror(errno));
271 exit (4);
272 }
273 return (kvp->gpm.state);
274 }
275
276 /*
277 * Enable or disable kernel profiling according to the state variable.
278 */
279 void
280 setprof(kvp, state)
281 struct kvmvars *kvp;
282 int state;
283 {
284 struct gmonparam *p = (struct gmonparam *)nl[N_GMONPARAM].n_value;
285 int mib[3], oldstate;
286 size_t sz;
287
288 sz = sizeof(state);
289 if (!kflag) {
290 mib[0] = CTL_KERN;
291 mib[1] = KERN_PROF;
292 mib[2] = GPROF_STATE;
293 if (sysctl(mib, 3, &oldstate, &sz, NULL, 0) < 0)
294 goto bad;
295 if (oldstate == state)
296 return;
297 (void)seteuid(0);
298 if (sysctl(mib, 3, NULL, NULL, &state, sz) >= 0) {
299 (void)seteuid(getuid());
300 return;
301 }
302 (void)seteuid(getuid());
303 } else if (kvm_write(kvp->kd, (u_long)&p->state, (void *)&state, sz)
304 == sz)
305 return;
306 bad:
307 (void)fprintf(stderr, "kgmon: warning: cannot turn profiling %s\n",
308 state == GMON_PROF_OFF ? "off" : "on");
309 }
310
311 /*
312 * Build the gmon.out file.
313 */
314 void
315 dumpstate(kvp)
316 struct kvmvars *kvp;
317 {
318 register FILE *fp;
319 struct rawarc rawarc;
320 struct tostruct *tos;
321 u_long frompc;
322 u_short *froms, *tickbuf;
323 int mib[3];
324 size_t i;
325 struct gmonhdr h;
326 int fromindex, endfrom, toindex;
327
328 setprof(kvp, GMON_PROF_OFF);
329 fp = fopen("gmon.out", "w");
330 if (fp == 0) {
331 perror("gmon.out");
332 return;
333 }
334
335 /*
336 * Build the gmon header and write it to a file.
337 */
338 bzero(&h, sizeof(h));
339 h.lpc = kvp->gpm.lowpc;
340 h.hpc = kvp->gpm.highpc;
341 h.ncnt = kvp->gpm.kcountsize + sizeof(h);
342 h.version = GMONVERSION;
343 h.profrate = getprofhz(kvp);
344 fwrite((char *)&h, sizeof(h), 1, fp);
345
346 /*
347 * Write out the tick buffer.
348 */
349 mib[0] = CTL_KERN;
350 mib[1] = KERN_PROF;
351 if ((tickbuf = (u_short *)malloc(kvp->gpm.kcountsize)) == NULL) {
352 fprintf(stderr, "kgmon: cannot allocate kcount space\n");
353 exit (5);
354 }
355 if (kflag) {
356 i = kvm_read(kvp->kd, (u_long)kvp->gpm.kcount, (void *)tickbuf,
357 kvp->gpm.kcountsize);
358 } else {
359 mib[2] = GPROF_COUNT;
360 i = kvp->gpm.kcountsize;
361 if (sysctl(mib, 3, tickbuf, &i, NULL, 0) < 0)
362 i = 0;
363 }
364 if (i != kvp->gpm.kcountsize) {
365 (void)fprintf(stderr, "kgmon: read ticks: read %lu, got %d: %s",
366 kvp->gpm.kcountsize, i,
367 kflag ? kvm_geterr(kvp->kd) : strerror(errno));
368 exit(6);
369 }
370 if ((fwrite(tickbuf, kvp->gpm.kcountsize, 1, fp)) != 1) {
371 perror("kgmon: writing tocks to gmon.out");
372 exit(7);
373 }
374 free(tickbuf);
375
376 /*
377 * Write out the arc info.
378 */
379 if ((froms = (u_short *)malloc(kvp->gpm.fromssize)) == NULL) {
380 fprintf(stderr, "kgmon: cannot allocate froms space\n");
381 exit (8);
382 }
383 if (kflag) {
384 i = kvm_read(kvp->kd, (u_long)kvp->gpm.froms, (void *)froms,
385 kvp->gpm.fromssize);
386 } else {
387 mib[2] = GPROF_FROMS;
388 i = kvp->gpm.fromssize;
389 if (sysctl(mib, 3, froms, &i, NULL, 0) < 0)
390 i = 0;
391 }
392 if (i != kvp->gpm.fromssize) {
393 (void)fprintf(stderr, "kgmon: read froms: read %lu, got %d: %s",
394 kvp->gpm.fromssize, i,
395 kflag ? kvm_geterr(kvp->kd) : strerror(errno));
396 exit(9);
397 }
398 if ((tos = (struct tostruct *)malloc(kvp->gpm.tossize)) == NULL) {
399 fprintf(stderr, "kgmon: cannot allocate tos space\n");
400 exit(10);
401 }
402 if (kflag) {
403 i = kvm_read(kvp->kd, (u_long)kvp->gpm.tos, (void *)tos,
404 kvp->gpm.tossize);
405 } else {
406 mib[2] = GPROF_TOS;
407 i = kvp->gpm.tossize;
408 if (sysctl(mib, 3, tos, &i, NULL, 0) < 0)
409 i = 0;
410 }
411 if (i != kvp->gpm.tossize) {
412 (void)fprintf(stderr, "kgmon: read tos: read %lu, got %d: %s",
413 kvp->gpm.tossize, i,
414 kflag ? kvm_geterr(kvp->kd) : strerror(errno));
415 exit(11);
416 }
417 if (debug)
418 (void)fprintf(stderr, "kgmon: lowpc 0x%lx, textsize 0x%lx\n",
419 kvp->gpm.lowpc, kvp->gpm.textsize);
420 endfrom = kvp->gpm.fromssize / sizeof(*froms);
421 for (fromindex = 0; fromindex < endfrom; ++fromindex) {
422 if (froms[fromindex] == 0)
423 continue;
424 frompc = (u_long)kvp->gpm.lowpc +
425 (fromindex * kvp->gpm.hashfraction * sizeof(*froms));
426 for (toindex = froms[fromindex]; toindex != 0;
427 toindex = tos[toindex].link) {
428 if (debug)
429 (void)fprintf(stderr,
430 "%s: [mcleanup] frompc 0x%lx selfpc 0x%lx count %ld\n",
431 "kgmon", frompc, tos[toindex].selfpc,
432 tos[toindex].count);
433 rawarc.raw_frompc = frompc;
434 rawarc.raw_selfpc = (u_long)tos[toindex].selfpc;
435 rawarc.raw_count = tos[toindex].count;
436 fwrite((char *)&rawarc, sizeof(rawarc), 1, fp);
437 }
438 }
439 fclose(fp);
440 }
441
442 /*
443 * Get the profiling rate.
444 */
445 int
446 getprofhz(kvp)
447 struct kvmvars *kvp;
448 {
449 int mib[2], profrate;
450 size_t size;
451 struct clockinfo clockrate;
452
453 if (kflag) {
454 profrate = 1;
455 if (kvm_read(kvp->kd, nl[N_PROFHZ].n_value, &profrate,
456 sizeof profrate) != sizeof profrate)
457 (void)fprintf(stderr, "kgmon: get clockrate: %s\n",
458 kvm_geterr(kvp->kd));
459 return (profrate);
460 }
461 mib[0] = CTL_KERN;
462 mib[1] = KERN_CLOCKRATE;
463 clockrate.profhz = 1;
464 size = sizeof clockrate;
465 if (sysctl(mib, 2, &clockrate, &size, NULL, 0) < 0)
466 (void)fprintf(stderr, "kgmon: get clockrate: %s\n",
467 strerror(errno));
468 return (clockrate.profhz);
469 }
470
471 /*
472 * Reset the kernel profiling date structures.
473 */
474 void
475 reset(kvp)
476 struct kvmvars *kvp;
477 {
478 char *zbuf;
479 u_long biggest;
480 int mib[3];
481
482 setprof(kvp, GMON_PROF_OFF);
483
484 biggest = kvp->gpm.kcountsize;
485 if (kvp->gpm.fromssize > biggest)
486 biggest = kvp->gpm.fromssize;
487 if (kvp->gpm.tossize > biggest)
488 biggest = kvp->gpm.tossize;
489 if ((zbuf = (char *)malloc(biggest)) == NULL) {
490 fprintf(stderr, "kgmon: cannot allocate zbuf space\n");
491 exit(12);
492 }
493 bzero(zbuf, biggest);
494 if (kflag) {
495 if (kvm_write(kvp->kd, (u_long)kvp->gpm.kcount, zbuf,
496 kvp->gpm.kcountsize) != kvp->gpm.kcountsize) {
497 (void)fprintf(stderr, "kgmon: tickbuf zero: %s\n",
498 kvm_geterr(kvp->kd));
499 exit(13);
500 }
501 if (kvm_write(kvp->kd, (u_long)kvp->gpm.froms, zbuf,
502 kvp->gpm.fromssize) != kvp->gpm.fromssize) {
503 (void)fprintf(stderr, "kgmon: froms zero: %s\n",
504 kvm_geterr(kvp->kd));
505 exit(14);
506 }
507 if (kvm_write(kvp->kd, (u_long)kvp->gpm.tos, zbuf,
508 kvp->gpm.tossize) != kvp->gpm.tossize) {
509 (void)fprintf(stderr, "kgmon: tos zero: %s\n",
510 kvm_geterr(kvp->kd));
511 exit(15);
512 }
513 return;
514 }
515 (void)seteuid(0);
516 mib[0] = CTL_KERN;
517 mib[1] = KERN_PROF;
518 mib[2] = GPROF_COUNT;
519 if (sysctl(mib, 3, NULL, NULL, zbuf, kvp->gpm.kcountsize) < 0) {
520 (void)fprintf(stderr, "kgmon: tickbuf zero: %s\n",
521 strerror(errno));
522 exit(13);
523 }
524 mib[2] = GPROF_FROMS;
525 if (sysctl(mib, 3, NULL, NULL, zbuf, kvp->gpm.fromssize) < 0) {
526 (void)fprintf(stderr, "kgmon: froms zero: %s\n",
527 strerror(errno));
528 exit(14);
529 }
530 mib[2] = GPROF_TOS;
531 if (sysctl(mib, 3, NULL, NULL, zbuf, kvp->gpm.tossize) < 0) {
532 (void)fprintf(stderr, "kgmon: tos zero: %s\n",
533 strerror(errno));
534 exit(15);
535 }
536 (void)seteuid(getuid());
537 free(zbuf);
538 }
539