xref: /original-bsd/usr.sbin/kgmon/kgmon.c (revision 50ed812d)
1 /*
2  * Copyright (c) 1983, 1992 The Regents of the University of California.
3  * All rights reserved.
4  *
5  * %sccs.include.redist.c%
6  */
7 
8 #ifndef lint
9 char copyright[] =
10 "@(#) Copyright (c) 1983, 1992 The Regents of the University of California.\n\
11  All rights reserved.\n";
12 #endif /* not lint */
13 
14 #ifndef lint
15 static char sccsid[] = "@(#)kgmon.c	5.16 (Berkeley) 04/29/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 /*
41  * We should call this _gmonparam for consistency, but that would cause a
42  * problem if we want to profile this program itself.
43  */
44 struct gmonparam gmonparam;
45 
46 int profhz;
47 
48 kvm_t	*kd;
49 int	bflag, hflag, kflag, rflag, pflag;
50 int	debug = 0;
51 
52 #define KREAD(kd, addr, s)\
53 	kvm_read(kd, addr, (void *)(s), sizeof*(s)) != (sizeof*(s))
54 
55 /*
56  * Build the gmon header and write it to a file.
57  */
58 void
59 dumphdr(FILE *fp, struct gmonparam *p)
60 {
61 	struct gmonhdr h;
62 
63 	/* zero out the unused fields */
64 	bzero(&h, sizeof(h));
65 
66 	h.lpc = p->lowpc;
67 	h.hpc = p->highpc;
68 	h.ncnt = p->kcountsize + sizeof(h);
69 	h.version = GMONVERSION;
70 	h.profrate = profhz;
71 
72 	fwrite((char *)&h, sizeof(h), 1, fp);
73 }
74 
75 /*
76  * Dump a range of kernel memory to a file.
77  */
78 void
79 dumpbuf(FILE *fp, u_long addr, int cc)
80 {
81 	int ret, n;
82 	char buf[8192];
83 
84 	while (cc > 0) {
85 		n = MIN(cc, sizeof(buf));
86 		if ((ret = kvm_read(kd, addr, buf, n)) != n) {
87 			(void)fprintf(stderr,
88 			    "kgmon: read kmem: read %d, got %d: %s\n",
89 			    n, ret, kvm_geterr(kd));
90 			exit(4);
91 		}
92 		if ((ret = fwrite(buf, n, 1, fp)) != 1) {
93 			perror("kgmon: gmon.out");
94 			exit(1);
95 		}
96 		addr += n;
97 		cc -= n;
98 	}
99 }
100 
101 /*
102  * Enable or disable kernel profiling according to the state variable.
103  */
104 void
105 setprof(int state)
106 {
107 	struct gmonparam *p = (struct gmonparam *)nl[N_GMONPARAM].n_value;
108 	int mib[3], sz;
109 
110 	sz = sizeof(state);
111 	if (!kflag) {
112 		mib[0] = CTL_KERN;
113 		mib[1] = KERN_PROF;
114 		mib[2] = GPROF_STATE;
115 		if (sysctl(mib, 3, NULL, NULL, &state, sz) >= 0)
116 			return;
117 	} else if (kvm_write(kd, (u_long)&p->state, (void *)&state, sz) == sz)
118 		return;
119 	(void)fprintf(stderr, "kgmon: warning: cannot turn profiling %s\n",
120 	    state == GMON_PROF_OFF ? "off" : "on");
121 }
122 
123 /*
124  * Build the gmon.out file.
125  */
126 void
127 dumpstate(struct gmonparam *p)
128 {
129 	register FILE *fp;
130 	struct rawarc rawarc;
131 	struct tostruct *tos;
132 	u_long frompc, addr;
133 	u_short *froms;
134 	int i, n;
135 	int fromindex, endfrom, toindex;
136 
137 	setprof(GMON_PROF_OFF);
138 	fp = fopen("gmon.out", "w");
139 	if (fp == 0) {
140 		perror("gmon.out");
141 		return;
142 	}
143 	dumphdr(fp, p);
144 	dumpbuf(fp, (u_long)p->kcount, p->kcountsize);
145 
146 	froms = (u_short *)malloc(p->fromssize);
147 	i = kvm_read(kd, (u_long)p->froms, (void *)froms, p->fromssize);
148 	if (i != p->fromssize) {
149 		(void)fprintf(stderr, "kgmon: read kmem: read %u, got %d: %s",
150 		    p->fromssize, i, strerror(errno));
151 		exit(5);
152 	}
153 	tos = (struct tostruct *)malloc(p->tossize);
154 	i = kvm_read(kd, (u_long)p->tos, (void *)tos, p->tossize);
155 	if (i != p->tossize) {
156 		(void)fprintf(stderr, "kgmon: read kmem: read %u, got %d: %s",
157 		    p->tossize, i, kvm_geterr(kd));
158 		exit(6);
159 	}
160 	if (debug)
161 		(void)fprintf(stderr, "lowpc 0x%x, textsize 0x%x\n",
162 			      p->lowpc, p->textsize);
163 	endfrom = p->fromssize / sizeof(*froms);
164 	for (fromindex = 0; fromindex < endfrom; ++fromindex) {
165 		if (froms[fromindex] == 0)
166 			continue;
167 		frompc = (u_long)p->lowpc +
168 		    (fromindex * HASHFRACTION * sizeof(*froms));
169 		for (toindex = froms[fromindex]; toindex != 0;
170 		   toindex = tos[toindex].link) {
171 			if (debug)
172 			    (void)fprintf(stderr,
173 			    "[mcleanup] frompc 0x%x selfpc 0x%x count %d\n" ,
174 			    frompc, tos[toindex].selfpc, tos[toindex].count);
175 			rawarc.raw_frompc = frompc;
176 			rawarc.raw_selfpc = (u_long)tos[toindex].selfpc;
177 			rawarc.raw_count = tos[toindex].count;
178 			fwrite((char *)&rawarc, sizeof(rawarc), 1, fp);
179 		}
180 	}
181 	fclose(fp);
182 }
183 
184 /*
185  * Zero out a region of kernel memory.
186  */
187 int
188 kzero(u_long addr, int cc)
189 {
190 	static char zbuf[MAXBSIZE];
191 
192 	while (cc > 0) {
193 		register int n = MIN(cc, sizeof(zbuf));
194 
195 		if (kvm_write(kd, addr, zbuf, n) != n)
196 			return (-1);
197 		addr += n;
198 		cc -= n;
199 	}
200 	return (0);
201 }
202 
203 /*
204  * Reset the kernel profiling date structures.
205  */
206 void
207 reset(struct gmonparam *p)
208 {
209 
210 	setprof(GMON_PROF_OFF);
211 
212 	if (kzero((u_long)p->kcount, p->kcountsize)) {
213 		(void)fprintf(stderr, "kgmon: sbuf write: %s\n",
214 		    kvm_geterr(kd));
215 		exit(7);
216 	}
217 	if (kzero((u_long)p->froms, p->fromssize)) {
218 		(void)fprintf(stderr, "kgmon: kfroms write: %s\n",
219 		    kvm_geterr(kd));
220 		exit(8);
221 	}
222 	if (kzero((u_long)p->tos, p->tossize)) {
223 		(void)fprintf(stderr, "kgmon: ktos write: %s\n",
224 		    kvm_geterr(kd));
225 		exit(9);
226 	}
227 }
228 
229 int
230 main(int argc, char **argv)
231 {
232 	extern char *optarg;
233 	extern int optind;
234 	int ch, mode, disp, openmode;
235 	char *system, *kmemf;
236 	char errbuf[_POSIX2_LINE_MAX];
237 
238 	kmemf = NULL;
239 	system = NULL;
240 	while ((ch = getopt(argc, argv, "M:N:bhpr")) != EOF) {
241 		switch((char)ch) {
242 
243 		case 'M':
244 			kmemf = optarg;
245 			kflag = 1;
246 			break;
247 
248 		case 'N':
249 			system = optarg;
250 			break;
251 
252 		case 'b':
253 			bflag = 1;
254 			break;
255 
256 		case 'h':
257 			hflag = 1;
258 			break;
259 
260 		case 'p':
261 			pflag = 1;
262 			break;
263 
264 		case 'r':
265 			rflag = 1;
266 			break;
267 
268 		default:
269 			(void)fprintf(stderr,
270 			    "usage: kgmon [-bhrp] [-M core] [-N system]\n");
271 			exit(1);
272 		}
273 	}
274 	argc -= optind;
275 	argv += optind;
276 
277 #define BACKWARD_COMPATIBILITY
278 #ifdef	BACKWARD_COMPATIBILITY
279 	if (*argv) {
280 		system = *argv;
281 		if (*++argv) {
282 			kmemf = *argv;
283 			++kflag;
284 		}
285 	}
286 #endif
287 	if (system == NULL)
288 		system = _PATH_UNIX;
289 	if (!kflag)
290 		openmode = rflag ? O_RDWR : O_RDONLY;
291 	else
292 		openmode =
293 		    (bflag || hflag || pflag || rflag) ? O_RDWR : O_RDONLY;
294 	kd = kvm_openfiles(system, kmemf, NULL, openmode, errbuf);
295 	if (kd == NULL) {
296 		if (openmode == O_RDWR) {
297 			openmode = O_RDONLY;
298 			kd = kvm_openfiles(system, kmemf, NULL, O_RDONLY,
299 			    errbuf);
300 		}
301 		if (kd == NULL) {
302 			(void)fprintf(stderr, "kgmon: kvm_openfiles: %s\n",
303 			    errbuf);
304 			exit(2);
305 		}
306 		(void)fprintf(stderr, "kgmon: kernel opened read-only\n");
307 		if (rflag)
308 			(void)fprintf(stderr, "-r supressed\n");
309 		rflag = 0;
310 	}
311 	if (kvm_nlist(kd, nl) < 0) {
312 		(void)fprintf(stderr, "kgmon: %s: no namelist\n", system);
313 		exit(2);
314 	}
315 	if (!nl[N_GMONPARAM].n_value) {
316 		(void)fprintf(stderr,
317 		    "kgmon: profiling not defined in kernel.\n");
318 		exit(10);
319 	}
320 	if (KREAD(kd, nl[N_GMONPARAM].n_value, &gmonparam))
321 		(void)fprintf(stderr,
322 		    "kgmon: read kmem: %s\n", kvm_geterr(kd));
323 	if (KREAD(kd, nl[N_PROFHZ].n_value, &profhz))
324 		(void)fprintf(stderr, "kgmon: read kmem: %s\n",
325 			      kvm_geterr(kd));
326 
327 	mode = gmonparam.state;
328 	if (hflag)
329 		disp = GMON_PROF_OFF;
330 	else if (bflag)
331 		disp = GMON_PROF_ON;
332 	else
333 		disp = mode;
334 	if (pflag) {
335 		if (kflag && openmode == O_RDONLY && mode == GMON_PROF_ON)
336 			(void)fprintf(stderr, "data may be inconsistent\n");
337 		dumpstate(&gmonparam);
338 	}
339 	if (rflag)
340 		reset(&gmonparam);
341 	if (!kflag || openmode == O_RDWR)
342 		setprof(disp);
343 	(void)fprintf(stdout, "kernel profiling is %s.\n",
344 		      disp == GMON_PROF_OFF ? "off" : "running");
345 
346 	return (0);
347 }
348