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