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