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