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