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