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