1124208Sdes/*	$NetBSD$	*/
2124208Sdes
398937Sdes/*
4124208Sdes * Copyright (c) 1983, 1992, 1993
5124208Sdes *	The Regents of the University of California.  All rights reserved.
6124208Sdes *
7124208Sdes * Redistribution and use in source and binary forms, with or without
898937Sdes * modification, are permitted provided that the following conditions
9124208Sdes * are met:
10124208Sdes * 1. Redistributions of source code must retain the above copyright
11124208Sdes *    notice, this list of conditions and the following disclaimer.
12124208Sdes * 2. Redistributions in binary form must reproduce the above copyright
13124208Sdes *    notice, this list of conditions and the following disclaimer in the
14124208Sdes *    documentation and/or other materials provided with the distribution.
15124208Sdes * 3. Neither the name of the University nor the names of its contributors
16124208Sdes *    may be used to endorse or promote products derived from this software
17124208Sdes *    without specific prior written permission.
18124208Sdes *
19124208Sdes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
2098937Sdes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21124208Sdes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22124208Sdes * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
23124208Sdes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24124208Sdes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25124208Sdes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26124208Sdes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27124208Sdes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28124208Sdes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29124208Sdes * SUCH DAMAGE.
30124208Sdes */
31124208Sdes
3298937Sdes#include <sys/cdefs.h>
3398937Sdes#ifndef lint
3498937Sdes__COPYRIGHT("@(#) Copyright (c) 1983, 1992, 1993\
3598937Sdes The Regents of the University of California.  All rights reserved.");
3698937Sdes#endif /* not lint */
3798937Sdes
38162852Sdes#ifndef lint
39162852Sdes#if 0
40113908Sdesstatic char sccsid[] = "from: @(#)kgmon.c	8.1 (Berkeley) 6/6/93";
41113908Sdes#else
42124208Sdes__RCSID("$NetBSD$");
43113908Sdes#endif
44162852Sdes#endif /* not lint */
4598937Sdes
46181111Sdes#include <sys/param.h>
47181111Sdes#include <sys/file.h>
48124208Sdes#include <sys/sysctl.h>
49126274Sdes#include <sys/gmon.h>
50126274Sdes
51113908Sdes#include <ctype.h>
52124208Sdes#include <errno.h>
53124208Sdes#include <kvm.h>
5498937Sdes#include <err.h>
5598937Sdes#include <limits.h>
56124208Sdes#include <nlist.h>
57124208Sdes#include <stdio.h>
58113908Sdes#include <stdlib.h>
5998937Sdes#include <string.h>
60124208Sdes#include <unistd.h>
61124208Sdes
62124208Sdesstatic struct nlist nl[] = {
63113908Sdes#define	N_GMONPARAM	0
64113908Sdes	{ "__gmonparam", 0, 0, 0, 0 },
65124208Sdes#define	N_PROFHZ	1
66113908Sdes	{ "_profhz", 0, 0, 0, 0 },
6798937Sdes	{ 0, 0, 0, 0, 0 }
68124208Sdes};
6998937Sdes
70261320Sdesstruct kvmvars {
71261320Sdes	kvm_t	*kd;
72124208Sdes	struct gmonparam gpm;
73124208Sdes};
74124208Sdes
75124208Sdesstatic int	bflag, hflag, kflag, rflag, pflag;
7698937Sdesstatic int	debug = 0;
77113908Sdesstatic void	setprof(struct kvmvars *kvp, int state);
78124208Sdesstatic void	dumpstate(struct kvmvars *kvp);
79323134Sdesstatic void	reset(struct kvmvars *kvp);
80124208Sdesstatic int	openfiles(char *, char *, struct kvmvars *);
81113908Sdesstatic int	getprof(struct kvmvars *);
82113908Sdesstatic void	kern_readonly(int);
83124208Sdesstatic int	getprofhz(struct kvmvars *);
84124208Sdes
85113908Sdesint
86124208Sdesmain(int argc, char **argv)
87124208Sdes{
88124208Sdes	int ch, mode, disp, accessmode;
89162852Sdes	struct kvmvars kvmvars;
90124208Sdes	char *sys, *kmemf;
91124208Sdes
92124208Sdes	setprogname(argv[0]);
93124208Sdes	(void)seteuid(getuid());
94113908Sdes	kmemf = NULL;
95323134Sdes	sys = NULL;
96124208Sdes	while ((ch = getopt(argc, argv, "M:N:bdhpr")) != -1) {
97113908Sdes		switch((char)ch) {
98124208Sdes
99124208Sdes		case 'M':
100124208Sdes			kmemf = optarg;
101113908Sdes			kflag = 1;
102124208Sdes			break;
103124208Sdes
104124208Sdes		case 'N':
105124208Sdes			sys = optarg;
10698937Sdes			break;
107124208Sdes
108124208Sdes		case 'b':
109124208Sdes			bflag = 1;
11098937Sdes			break;
111323134Sdes
112323134Sdes		case 'h':
113124208Sdes			hflag = 1;
114124208Sdes			break;
115124208Sdes
116124208Sdes		case 'p':
117124208Sdes			pflag = 1;
118124208Sdes			break;
11998937Sdes
120113908Sdes		case 'r':
121124208Sdes			rflag = 1;
122113908Sdes			break;
123124208Sdes
124113908Sdes		case 'd':
125124208Sdes			debug = 1;
126124208Sdes			break;
127181111Sdes
128124208Sdes		default:
129261320Sdes			(void)fprintf(stderr,
130124208Sdes			    "usage: %s [-bdhrp] [-M core] [-N system]\n",
131124208Sdes			    getprogname());
132124208Sdes			exit(1);
133113908Sdes		}
134113908Sdes	}
135124208Sdes	argc -= optind;
136124208Sdes	argv += optind;
137124208Sdes
138124208Sdes#define BACKWARD_COMPATIBILITY
139113908Sdes#ifdef	BACKWARD_COMPATIBILITY
140124208Sdes	if (*argv) {
141113908Sdes		sys = *argv;
142261320Sdes		if (*++argv) {
143124208Sdes			kmemf = *argv;
144124208Sdes			++kflag;
145124208Sdes		}
146124208Sdes	}
147261320Sdes#endif
148113908Sdes	accessmode = openfiles(sys, kmemf, &kvmvars);
149124208Sdes	mode = getprof(&kvmvars);
150261320Sdes	if (hflag)
151261320Sdes		disp = GMON_PROF_OFF;
152181111Sdes	else if (bflag)
153181111Sdes		disp = GMON_PROF_ON;
154113908Sdes	else
155124208Sdes		disp = mode;
156181111Sdes	if (pflag)
157181111Sdes		dumpstate(&kvmvars);
158124208Sdes	if (rflag)
159323134Sdes		reset(&kvmvars);
160124208Sdes	if (accessmode == O_RDWR)
161181111Sdes		setprof(&kvmvars, disp);
162124208Sdes	(void)fprintf(stdout, "%s: kernel profiling is %s.\n",
163124208Sdes	     getprogname(), disp == GMON_PROF_OFF ? "off" : "running");
164124208Sdes	return (0);
165113908Sdes}
166124208Sdes
167113908Sdes/*
168113908Sdes * Check that profiling is enabled and open any ncessary files.
169124208Sdes */
170static int
171openfiles(char *sys, char *kmemf, struct kvmvars *kvp)
172{
173	int mib[3], state, openmode;
174	size_t size;
175	char errbuf[_POSIX2_LINE_MAX];
176
177	if (!kflag) {
178		mib[0] = CTL_KERN;
179		mib[1] = KERN_PROF;
180		mib[2] = GPROF_STATE;
181		size = sizeof state;
182		if (sysctl(mib, 3, &state, &size, NULL, 0) < 0)
183			err(EXIT_FAILURE, "profiling not defined in kernel");
184		if (!(bflag || hflag || rflag ||
185		    (pflag && state == GMON_PROF_ON)))
186			return (O_RDONLY);
187		(void)seteuid(0);
188		if (sysctl(mib, 3, NULL, NULL, &state, size) >= 0)
189			return (O_RDWR);
190		(void)seteuid(getuid());
191		kern_readonly(state);
192		return (O_RDONLY);
193	}
194	openmode = (bflag || hflag || pflag || rflag) ? O_RDWR : O_RDONLY;
195	kvp->kd = kvm_openfiles(sys, kmemf, NULL, openmode, errbuf);
196	if (kvp->kd == NULL) {
197		if (openmode == O_RDWR) {
198			openmode = O_RDONLY;
199			kvp->kd = kvm_openfiles(sys, kmemf, NULL, O_RDONLY,
200			    errbuf);
201		}
202		if (kvp->kd == NULL)
203			errx(EXIT_FAILURE, "kvm_openfiles: %s", errbuf);
204		kern_readonly(GMON_PROF_ON);
205	}
206	if (kvm_nlist(kvp->kd, nl) < 0)
207		errx(EXIT_FAILURE, "%s: no namelist", sys);
208	if (!nl[N_GMONPARAM].n_value)
209		errx(EXIT_FAILURE, "profiling not defined in kernel");
210	return (openmode);
211}
212
213/*
214 * Suppress options that require a writable kernel.
215 */
216static void
217kern_readonly(int mode)
218{
219
220	(void)fprintf(stderr, "%s: kernel read-only: ", getprogname());
221	if (pflag && mode == GMON_PROF_ON)
222		(void)fprintf(stderr, "data may be inconsistent\n");
223	if (rflag)
224		(void)fprintf(stderr, "-r supressed\n");
225	if (bflag)
226		(void)fprintf(stderr, "-b supressed\n");
227	if (hflag)
228		(void)fprintf(stderr, "-h supressed\n");
229	rflag = bflag = hflag = 0;
230}
231
232/*
233 * Get the state of kernel profiling.
234 */
235static int
236getprof(struct kvmvars *kvp)
237{
238	int mib[3];
239	size_t size;
240
241	if (kflag) {
242		size = kvm_read(kvp->kd, nl[N_GMONPARAM].n_value, &kvp->gpm,
243		    sizeof kvp->gpm);
244	} else {
245		mib[0] = CTL_KERN;
246		mib[1] = KERN_PROF;
247		mib[2] = GPROF_GMONPARAM;
248		size = sizeof kvp->gpm;
249		if (sysctl(mib, 3, &kvp->gpm, &size, NULL, 0) < 0)
250			size = 0;
251	}
252	if (size != sizeof kvp->gpm)
253		errx(EXIT_FAILURE, "cannot get gmonparam: %s",
254		    kflag ? kvm_geterr(kvp->kd) : strerror(errno));
255	return (kvp->gpm.state);
256}
257
258/*
259 * Enable or disable kernel profiling according to the state variable.
260 */
261static void
262setprof(struct kvmvars *kvp, int state)
263{
264	struct gmonparam *p = (struct gmonparam *)nl[N_GMONPARAM].n_value;
265	int mib[3], oldstate;
266	size_t sz;
267
268	sz = sizeof(state);
269	if (!kflag) {
270		mib[0] = CTL_KERN;
271		mib[1] = KERN_PROF;
272		mib[2] = GPROF_STATE;
273		if (sysctl(mib, 3, &oldstate, &sz, NULL, 0) < 0)
274			goto bad;
275		if (oldstate == state)
276			return;
277		(void)seteuid(0);
278		if (sysctl(mib, 3, NULL, NULL, &state, sz) >= 0) {
279			(void)seteuid(getuid());
280			return;
281		}
282		(void)seteuid(getuid());
283	} else if ((size_t)kvm_write(kvp->kd, (u_long)&p->state, (void *)&state, sz)
284	    == sz)
285		return;
286bad:
287	warnx("cannot turn profiling %s", state == GMON_PROF_OFF ?
288	    "off" : "on");
289}
290
291/*
292 * Build the gmon.out file.
293 */
294static void
295dumpstate(struct kvmvars *kvp)
296{
297	FILE *fp;
298	struct rawarc rawarc;
299	struct tostruct *tos;
300	u_long frompc;
301	u_short *froms, *tickbuf;
302	int mib[3];
303	size_t i;
304	struct gmonhdr h;
305	int fromindex, endfrom, toindex;
306	size_t kcountsize;
307
308	setprof(kvp, GMON_PROF_OFF);
309	fp = fopen("gmon.out", "w");
310	if (fp == NULL) {
311		warn("cannot open `gmon.out'");
312		return;
313	}
314
315	/*
316	 * Build the gmon header and write it to a file.
317	 */
318	bzero(&h, sizeof(h));
319	h.lpc = kvp->gpm.lowpc;
320	h.hpc = kvp->gpm.highpc;
321	h.ncnt = kvp->gpm.kcountsize + sizeof(h);
322	h.version = GMONVERSION;
323	h.profrate = getprofhz(kvp);
324	if (fwrite(&h, sizeof(h), 1, fp) != 1)
325		err(EXIT_FAILURE, "writing header to gmon.out");
326
327	kcountsize = (size_t)kvp->gpm.kcountsize;
328
329	/*
330	 * Write out the tick buffer.
331	 */
332	mib[0] = CTL_KERN;
333	mib[1] = KERN_PROF;
334	if ((tickbuf = malloc(kcountsize)) == NULL)
335		err(EXIT_FAILURE, "Cannot allocate %zu kcount space",
336		    kcountsize);
337	if (kflag) {
338		i = kvm_read(kvp->kd, (u_long)kvp->gpm.kcount, tickbuf,
339		    kcountsize);
340	} else {
341		mib[2] = GPROF_COUNT;
342		i = kcountsize;
343		if (sysctl(mib, 3, tickbuf, &i, NULL, 0) < 0)
344			i = 0;
345	}
346	if (i != kcountsize)
347		errx(EXIT_FAILURE, "read ticks: read %zu, got %zu: %s",
348		    kcountsize, i,
349		    kflag ? kvm_geterr(kvp->kd) : strerror(errno));
350	if ((fwrite(tickbuf, kcountsize, 1, fp)) != 1)
351		err(EXIT_FAILURE, "writing ticks to gmon.out");
352	free(tickbuf);
353
354	/*
355	 * Write out the arc info.
356	 */
357	if ((froms = malloc((size_t)kvp->gpm.fromssize)) == NULL)
358		err(EXIT_FAILURE, "cannot allocate %zu froms space",
359		    (size_t)kvp->gpm.fromssize);
360	if (kflag) {
361		i = kvm_read(kvp->kd, (u_long)kvp->gpm.froms, froms,
362		    (size_t)kvp->gpm.fromssize);
363	} else {
364		mib[2] = GPROF_FROMS;
365		i = kvp->gpm.fromssize;
366		if (sysctl(mib, 3, froms, &i, NULL, 0) < 0)
367			i = 0;
368	}
369	if (i != kvp->gpm.fromssize)
370		errx(EXIT_FAILURE, "read froms: read %lu, got %lu: %s",
371		    kvp->gpm.fromssize, (u_long)i,
372		    kflag ? kvm_geterr(kvp->kd) : strerror(errno));
373	if ((tos = malloc((size_t)kvp->gpm.tossize)) == NULL)
374		err(EXIT_FAILURE, "cannot allocate %zu tos space",
375		    (size_t)kvp->gpm.tossize);
376	if (kflag) {
377		i = kvm_read(kvp->kd, (u_long)kvp->gpm.tos, (void *)tos,
378		    (size_t)kvp->gpm.tossize);
379	} else {
380		mib[2] = GPROF_TOS;
381		i = kvp->gpm.tossize;
382		if (sysctl(mib, 3, tos, &i, NULL, 0) < 0)
383			i = 0;
384	}
385	if (i != kvp->gpm.tossize)
386		errx(EXIT_FAILURE, "read tos: read %zu, got %zu: %s",
387		    (size_t)kvp->gpm.tossize, i,
388		    kflag ? kvm_geterr(kvp->kd) : strerror(errno));
389	if (debug)
390		(void)fprintf(stderr, "%s: lowpc 0x%lx, textsize 0x%lx\n",
391		     getprogname(), kvp->gpm.lowpc, kvp->gpm.textsize);
392	endfrom = kvp->gpm.fromssize / sizeof(*froms);
393	for (fromindex = 0; fromindex < endfrom; ++fromindex) {
394		if (froms[fromindex] == 0)
395			continue;
396		frompc = (u_long)kvp->gpm.lowpc +
397		    (fromindex * kvp->gpm.hashfraction * sizeof(*froms));
398		for (toindex = froms[fromindex]; toindex != 0;
399		   toindex = tos[toindex].link) {
400			if (debug)
401			    (void)fprintf(stderr,
402			    "%s: [mcleanup] frompc 0x%lx selfpc 0x%lx count %ld\n",
403			    getprogname(), frompc, tos[toindex].selfpc,
404			    tos[toindex].count);
405			rawarc.raw_frompc = frompc;
406			rawarc.raw_selfpc = (u_long)tos[toindex].selfpc;
407			rawarc.raw_count = tos[toindex].count;
408			if (fwrite(&rawarc, sizeof(rawarc), 1,fp) != 1){
409				err(EXIT_FAILURE,
410				    "writing raw arc to gmon.out");
411			}
412		}
413	}
414	free(tos);
415	(void)fclose(fp);
416}
417
418/*
419 * Get the profiling rate.
420 */
421static int
422getprofhz(struct kvmvars *kvp)
423{
424	int mib[2], profrate;
425	size_t size;
426	struct clockinfo clockrate;
427
428	if (kflag) {
429		profrate = 1;
430		if (kvm_read(kvp->kd, nl[N_PROFHZ].n_value, &profrate,
431		    sizeof profrate) != sizeof profrate)
432			warnx("get clockrate: %s", kvm_geterr(kvp->kd));
433		return (profrate);
434	}
435	mib[0] = CTL_KERN;
436	mib[1] = KERN_CLOCKRATE;
437	clockrate.profhz = 1;
438	size = sizeof clockrate;
439	if (sysctl(mib, 2, &clockrate, &size, NULL, 0) < 0)
440		warn("get clockrate");
441	return (clockrate.profhz);
442}
443
444/*
445 * Reset the kernel profiling date structures.
446 */
447static void
448reset(struct kvmvars *kvp)
449{
450	char *zbuf;
451	size_t biggest;
452	int mib[3];
453
454	setprof(kvp, GMON_PROF_OFF);
455
456	biggest = (size_t)kvp->gpm.kcountsize;
457	if ((size_t)kvp->gpm.fromssize > biggest)
458		biggest = (size_t)kvp->gpm.fromssize;
459	if ((size_t)kvp->gpm.tossize > biggest)
460		biggest = (size_t)kvp->gpm.tossize;
461	if ((zbuf = malloc(biggest)) == NULL)
462		err(EXIT_FAILURE, "cannot allocate zbuf space");
463	(void)memset(zbuf, 0, biggest);
464	if (kflag) {
465		if ((size_t)kvm_write(kvp->kd, (u_long)kvp->gpm.kcount, zbuf,
466		    (size_t)kvp->gpm.kcountsize) != kvp->gpm.kcountsize)
467			errx(EXIT_FAILURE, "tickbuf zero: %s",
468			     kvm_geterr(kvp->kd));
469		if ((size_t)kvm_write(kvp->kd, (u_long)kvp->gpm.froms, zbuf,
470		    (size_t)kvp->gpm.fromssize) != kvp->gpm.fromssize)
471			errx(EXIT_FAILURE, "froms zero: %s",
472			     kvm_geterr(kvp->kd));
473		if ((size_t)kvm_write(kvp->kd, (u_long)kvp->gpm.tos, zbuf,
474		    (size_t)kvp->gpm.tossize) != kvp->gpm.tossize)
475			errx(EXIT_FAILURE, "tos zero: %s", kvm_geterr(kvp->kd));
476		free(zbuf);
477		return;
478	}
479	(void)seteuid(0);
480	mib[0] = CTL_KERN;
481	mib[1] = KERN_PROF;
482	mib[2] = GPROF_COUNT;
483	if (sysctl(mib, 3, NULL, NULL, zbuf, (size_t)kvp->gpm.kcountsize) < 0)
484		err(EXIT_FAILURE, "tickbuf zero");
485	mib[2] = GPROF_FROMS;
486	if (sysctl(mib, 3, NULL, NULL, zbuf, (size_t)kvp->gpm.fromssize) < 0)
487		err(EXIT_FAILURE, "froms zero");
488	mib[2] = GPROF_TOS;
489	if (sysctl(mib, 3, NULL, NULL, zbuf, (size_t)kvp->gpm.tossize) < 0)
490		err(EXIT_FAILURE, "tos zero");
491	(void)seteuid(getuid());
492	free(zbuf);
493}
494