kgmon.c revision 1.6 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[] = "from: @(#)kgmon.c 8.1 (Berkeley) 6/6/93";*/
42 static char *rcsid = "$Id: kgmon.c,v 1.6 1994/12/23 16:45:33 cgd Exp $";
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, openmode;
167 size_t size;
168 char errbuf[_POSIX2_LINE_MAX];
169
170 if (!kflag) {
171 mib[0] = CTL_KERN;
172 mib[1] = KERN_PROF;
173 mib[2] = GPROF_STATE;
174 size = sizeof state;
175 if (sysctl(mib, 3, &state, &size, NULL, 0) < 0) {
176 (void)fprintf(stderr,
177 "kgmon: profiling not defined in kernel.\n");
178 exit(20);
179 }
180 if (!(bflag || hflag || rflag ||
181 (pflag && state == GMON_PROF_ON)))
182 return (O_RDONLY);
183 (void)seteuid(0);
184 if (sysctl(mib, 3, NULL, NULL, &state, size) >= 0)
185 return (O_RDWR);
186 (void)seteuid(getuid());
187 kern_readonly(state);
188 return (O_RDONLY);
189 }
190 openmode = (bflag || hflag || pflag || rflag) ? O_RDWR : O_RDONLY;
191 kvp->kd = kvm_openfiles(system, kmemf, NULL, openmode, errbuf);
192 if (kvp->kd == NULL) {
193 if (openmode == O_RDWR) {
194 openmode = O_RDONLY;
195 kvp->kd = kvm_openfiles(system, kmemf, NULL, O_RDONLY,
196 errbuf);
197 }
198 if (kvp->kd == NULL) {
199 (void)fprintf(stderr, "kgmon: kvm_openfiles: %s\n",
200 errbuf);
201 exit(2);
202 }
203 kern_readonly(GMON_PROF_ON);
204 }
205 if (kvm_nlist(kvp->kd, nl) < 0) {
206 (void)fprintf(stderr, "kgmon: %s: no namelist\n", system);
207 exit(3);
208 }
209 if (!nl[N_GMONPARAM].n_value) {
210 (void)fprintf(stderr,
211 "kgmon: profiling not defined in kernel.\n");
212 exit(20);
213 }
214 return (openmode);
215 }
216
217 /*
218 * Suppress options that require a writable kernel.
219 */
220 kern_readonly(mode)
221 int mode;
222 {
223
224 (void)fprintf(stderr, "kgmon: kernel read-only: ");
225 if (pflag && mode == GMON_PROF_ON)
226 (void)fprintf(stderr, "data may be inconsistent\n");
227 if (rflag)
228 (void)fprintf(stderr, "-r supressed\n");
229 if (bflag)
230 (void)fprintf(stderr, "-b supressed\n");
231 if (hflag)
232 (void)fprintf(stderr, "-h supressed\n");
233 rflag = bflag = hflag = 0;
234 }
235
236 /*
237 * Get the state of kernel profiling.
238 */
239 getprof(kvp)
240 struct kvmvars *kvp;
241 {
242 int mib[3];
243 size_t size;
244
245 if (kflag) {
246 size = kvm_read(kvp->kd, nl[N_GMONPARAM].n_value, &kvp->gpm,
247 sizeof kvp->gpm);
248 } else {
249 mib[0] = CTL_KERN;
250 mib[1] = KERN_PROF;
251 mib[2] = GPROF_GMONPARAM;
252 size = sizeof kvp->gpm;
253 if (sysctl(mib, 3, &kvp->gpm, &size, NULL, 0) < 0)
254 size = 0;
255 }
256 if (size != sizeof kvp->gpm) {
257 (void)fprintf(stderr, "kgmon: cannot get gmonparam: %s\n",
258 kflag ? kvm_geterr(kvp->kd) : strerror(errno));
259 exit (4);
260 }
261 return (kvp->gpm.state);
262 }
263
264 /*
265 * Enable or disable kernel profiling according to the state variable.
266 */
267 void
268 setprof(kvp, state)
269 struct kvmvars *kvp;
270 int state;
271 {
272 struct gmonparam *p = (struct gmonparam *)nl[N_GMONPARAM].n_value;
273 int mib[3], oldstate;
274 size_t sz;
275
276 sz = sizeof(state);
277 if (!kflag) {
278 mib[0] = CTL_KERN;
279 mib[1] = KERN_PROF;
280 mib[2] = GPROF_STATE;
281 if (sysctl(mib, 3, &oldstate, &sz, NULL, 0) < 0)
282 goto bad;
283 if (oldstate == state)
284 return;
285 (void)seteuid(0);
286 if (sysctl(mib, 3, NULL, NULL, &state, sz) >= 0) {
287 (void)seteuid(getuid());
288 return;
289 }
290 (void)seteuid(getuid());
291 } else if (kvm_write(kvp->kd, (u_long)&p->state, (void *)&state, sz)
292 == sz)
293 return;
294 bad:
295 (void)fprintf(stderr, "kgmon: warning: cannot turn profiling %s\n",
296 state == GMON_PROF_OFF ? "off" : "on");
297 }
298
299 /*
300 * Build the gmon.out file.
301 */
302 void
303 dumpstate(kvp)
304 struct kvmvars *kvp;
305 {
306 register FILE *fp;
307 struct rawarc rawarc;
308 struct tostruct *tos;
309 u_long frompc, addr;
310 u_short *froms, *tickbuf;
311 int mib[3];
312 size_t i;
313 struct gmonhdr h;
314 int fromindex, endfrom, toindex;
315
316 setprof(kvp, GMON_PROF_OFF);
317 fp = fopen("gmon.out", "w");
318 if (fp == 0) {
319 perror("gmon.out");
320 return;
321 }
322
323 /*
324 * Build the gmon header and write it to a file.
325 */
326 bzero(&h, sizeof(h));
327 h.lpc = kvp->gpm.lowpc;
328 h.hpc = kvp->gpm.highpc;
329 h.ncnt = kvp->gpm.kcountsize + sizeof(h);
330 h.version = GMONVERSION;
331 h.profrate = getprofhz(kvp);
332 fwrite((char *)&h, sizeof(h), 1, fp);
333
334 /*
335 * Write out the tick buffer.
336 */
337 mib[0] = CTL_KERN;
338 mib[1] = KERN_PROF;
339 if ((tickbuf = (u_short *)malloc(kvp->gpm.kcountsize)) == NULL) {
340 fprintf(stderr, "kgmon: cannot allocate kcount space\n");
341 exit (5);
342 }
343 if (kflag) {
344 i = kvm_read(kvp->kd, (u_long)kvp->gpm.kcount, (void *)tickbuf,
345 kvp->gpm.kcountsize);
346 } else {
347 mib[2] = GPROF_COUNT;
348 i = kvp->gpm.kcountsize;
349 if (sysctl(mib, 3, tickbuf, &i, NULL, 0) < 0)
350 i = 0;
351 }
352 if (i != kvp->gpm.kcountsize) {
353 (void)fprintf(stderr, "kgmon: read ticks: read %u, got %d: %s",
354 kvp->gpm.kcountsize, i,
355 kflag ? kvm_geterr(kvp->kd) : strerror(errno));
356 exit(6);
357 }
358 if ((fwrite(tickbuf, kvp->gpm.kcountsize, 1, fp)) != 1) {
359 perror("kgmon: writing tocks to gmon.out");
360 exit(7);
361 }
362 free(tickbuf);
363
364 /*
365 * Write out the arc info.
366 */
367 if ((froms = (u_short *)malloc(kvp->gpm.fromssize)) == NULL) {
368 fprintf(stderr, "kgmon: cannot allocate froms space\n");
369 exit (8);
370 }
371 if (kflag) {
372 i = kvm_read(kvp->kd, (u_long)kvp->gpm.froms, (void *)froms,
373 kvp->gpm.fromssize);
374 } else {
375 mib[2] = GPROF_FROMS;
376 i = kvp->gpm.fromssize;
377 if (sysctl(mib, 3, froms, &i, NULL, 0) < 0)
378 i = 0;
379 }
380 if (i != kvp->gpm.fromssize) {
381 (void)fprintf(stderr, "kgmon: read froms: read %u, got %d: %s",
382 kvp->gpm.fromssize, i,
383 kflag ? kvm_geterr(kvp->kd) : strerror(errno));
384 exit(9);
385 }
386 if ((tos = (struct tostruct *)malloc(kvp->gpm.tossize)) == NULL) {
387 fprintf(stderr, "kgmon: cannot allocate tos space\n");
388 exit(10);
389 }
390 if (kflag) {
391 i = kvm_read(kvp->kd, (u_long)kvp->gpm.tos, (void *)tos,
392 kvp->gpm.tossize);
393 } else {
394 mib[2] = GPROF_TOS;
395 i = kvp->gpm.tossize;
396 if (sysctl(mib, 3, tos, &i, NULL, 0) < 0)
397 i = 0;
398 }
399 if (i != kvp->gpm.tossize) {
400 (void)fprintf(stderr, "kgmon: read tos: read %u, got %d: %s",
401 kvp->gpm.tossize, i,
402 kflag ? kvm_geterr(kvp->kd) : strerror(errno));
403 exit(11);
404 }
405 if (debug)
406 (void)fprintf(stderr, "kgmon: lowpc 0x%x, textsize 0x%x\n",
407 kvp->gpm.lowpc, kvp->gpm.textsize);
408 endfrom = kvp->gpm.fromssize / sizeof(*froms);
409 for (fromindex = 0; fromindex < endfrom; ++fromindex) {
410 if (froms[fromindex] == 0)
411 continue;
412 frompc = (u_long)kvp->gpm.lowpc +
413 (fromindex * kvp->gpm.hashfraction * sizeof(*froms));
414 for (toindex = froms[fromindex]; toindex != 0;
415 toindex = tos[toindex].link) {
416 if (debug)
417 (void)fprintf(stderr,
418 "%s: [mcleanup] frompc 0x%x selfpc 0x%x count %d\n",
419 "kgmon", frompc, tos[toindex].selfpc,
420 tos[toindex].count);
421 rawarc.raw_frompc = frompc;
422 rawarc.raw_selfpc = (u_long)tos[toindex].selfpc;
423 rawarc.raw_count = tos[toindex].count;
424 fwrite((char *)&rawarc, sizeof(rawarc), 1, fp);
425 }
426 }
427 fclose(fp);
428 }
429
430 /*
431 * Get the profiling rate.
432 */
433 int
434 getprofhz(kvp)
435 struct kvmvars *kvp;
436 {
437 int mib[2], profrate;
438 size_t size;
439 struct clockinfo clockrate;
440
441 if (kflag) {
442 profrate = 1;
443 if (kvm_read(kvp->kd, nl[N_PROFHZ].n_value, &profrate,
444 sizeof profrate) != sizeof profrate)
445 (void)fprintf(stderr, "kgmon: get clockrate: %s\n",
446 kvm_geterr(kvp->kd));
447 return (profrate);
448 }
449 mib[0] = CTL_KERN;
450 mib[1] = KERN_CLOCKRATE;
451 clockrate.profhz = 1;
452 size = sizeof clockrate;
453 if (sysctl(mib, 2, &clockrate, &size, NULL, 0) < 0)
454 (void)fprintf(stderr, "kgmon: get clockrate: %s\n",
455 strerror(errno));
456 return (clockrate.profhz);
457 }
458
459 /*
460 * Reset the kernel profiling date structures.
461 */
462 void
463 reset(kvp)
464 struct kvmvars *kvp;
465 {
466 char *zbuf;
467 u_long biggest;
468 int mib[3];
469
470 setprof(kvp, GMON_PROF_OFF);
471
472 biggest = kvp->gpm.kcountsize;
473 if (kvp->gpm.fromssize > biggest)
474 biggest = kvp->gpm.fromssize;
475 if (kvp->gpm.tossize > biggest)
476 biggest = kvp->gpm.tossize;
477 if ((zbuf = (char *)malloc(biggest)) == NULL) {
478 fprintf(stderr, "kgmon: cannot allocate zbuf space\n");
479 exit(12);
480 }
481 bzero(zbuf, biggest);
482 if (kflag) {
483 if (kvm_write(kvp->kd, (u_long)kvp->gpm.kcount, zbuf,
484 kvp->gpm.kcountsize) != kvp->gpm.kcountsize) {
485 (void)fprintf(stderr, "kgmon: tickbuf zero: %s\n",
486 kvm_geterr(kvp->kd));
487 exit(13);
488 }
489 if (kvm_write(kvp->kd, (u_long)kvp->gpm.froms, zbuf,
490 kvp->gpm.fromssize) != kvp->gpm.fromssize) {
491 (void)fprintf(stderr, "kgmon: froms zero: %s\n",
492 kvm_geterr(kvp->kd));
493 exit(14);
494 }
495 if (kvm_write(kvp->kd, (u_long)kvp->gpm.tos, zbuf,
496 kvp->gpm.tossize) != kvp->gpm.tossize) {
497 (void)fprintf(stderr, "kgmon: tos zero: %s\n",
498 kvm_geterr(kvp->kd));
499 exit(15);
500 }
501 return;
502 }
503 (void)seteuid(0);
504 mib[0] = CTL_KERN;
505 mib[1] = KERN_PROF;
506 mib[2] = GPROF_COUNT;
507 if (sysctl(mib, 3, NULL, NULL, zbuf, kvp->gpm.kcountsize) < 0) {
508 (void)fprintf(stderr, "kgmon: tickbuf zero: %s\n",
509 strerror(errno));
510 exit(13);
511 }
512 mib[2] = GPROF_FROMS;
513 if (sysctl(mib, 3, NULL, NULL, zbuf, kvp->gpm.fromssize) < 0) {
514 (void)fprintf(stderr, "kgmon: froms zero: %s\n",
515 strerror(errno));
516 exit(14);
517 }
518 mib[2] = GPROF_TOS;
519 if (sysctl(mib, 3, NULL, NULL, zbuf, kvp->gpm.tossize) < 0) {
520 (void)fprintf(stderr, "kgmon: tos zero: %s\n",
521 strerror(errno));
522 exit(15);
523 }
524 (void)seteuid(getuid());
525 free(zbuf);
526 }
527