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