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