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