kgmon.c revision 11914
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
35static 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
41static 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/user.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
59struct nlist nl[] = {
60#define	N_GMONPARAM	0
61	{ "__gmonparam" },
62#define	N_PROFHZ	1
63	{ "_profhz" },
64	0,
65};
66
67struct kvmvars {
68	kvm_t	*kd;
69	struct gmonparam gpm;
70};
71
72int	bflag, hflag, kflag, rflag, pflag;
73int	debug = 0;
74void	setprof __P((struct kvmvars *kvp, int state));
75void	dumpstate __P((struct kvmvars *kvp));
76void	reset __P((struct kvmvars *kvp));
77
78int
79main(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 = (char *)getbootfile();
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 */
161openfiles(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 */
219kern_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 */
238getprof(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 */
265void
266setprof(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;
291bad:
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 */
299void
300dumpstate(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 */
429int
430getprofhz(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 */
457void
458reset(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