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