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