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