xref: /netbsd-src/usr.sbin/kgmon/kgmon.c (revision ae9172d6cd9432a6a1a56760d86b32c57a66c39c)
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.5 1994/06/11 08:04:25 mycroft 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, 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