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 * 4. Neither the name of the University nor the names of its contributors
14 * may be used to endorse or promote products derived from this software
15 * without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27 * SUCH DAMAGE.
28 */
29
30 #ifndef lint
31 static const char copyright[] =
32 "@(#) Copyright (c) 1983, 1992, 1993\n\
33 The Regents of the University of California. All rights reserved.\n";
34 #endif /* not lint */
35
36 #ifndef lint
37 #if 0
38 static char sccsid[] = "@(#)kgmon.c 8.1 (Berkeley) 6/6/93";
39 #endif
40 #endif /* not lint */
41
42 #include <sys/cdefs.h>
43 __FBSDID("$FreeBSD$");
44
45 #include <sys/param.h>
46 #include <sys/file.h>
47 #include <sys/time.h>
48 #include <sys/sysctl.h>
49 #include <sys/gmon.h>
50 #include <ctype.h>
51 #include <err.h>
52 #include <errno.h>
53 #include <kvm.h>
54 #include <limits.h>
55 #include <nlist.h>
56 #include <paths.h>
57 #include <stddef.h>
58 #include <stdio.h>
59 #include <stdlib.h>
60 #include <string.h>
61 #include <unistd.h>
62
63 struct nlist nl[] = {
64 #define N_GMONPARAM 0
65 { "__gmonparam" },
66 #define N_PROFHZ 1
67 { "_profhz" },
68 { NULL },
69 };
70
71 struct kvmvars {
72 kvm_t *kd;
73 struct gmonparam gpm;
74 };
75
76 int Bflag, bflag, hflag, kflag, rflag, pflag;
77 int debug = 0;
78 int getprof(struct kvmvars *);
79 int getprofhz(struct kvmvars *);
80 void kern_readonly(int);
81 int openfiles(const char *, char *, struct kvmvars *);
82 void setprof(struct kvmvars *kvp, int state);
83 void dumpstate(struct kvmvars *kvp);
84 void reset(struct kvmvars *kvp);
85 static void usage(void);
86
87 int
main(int argc,char ** argv)88 main(int argc, char **argv)
89 {
90 int ch, mode, disp, accessmode;
91 struct kvmvars kvmvars;
92 const char *systemname;
93 char *kmemf;
94
95 if (seteuid(getuid()) != 0) {
96 err(1, "seteuid failed\n");
97 }
98 kmemf = NULL;
99 systemname = NULL;
100 while ((ch = getopt(argc, argv, "M:N:Bbhpr")) != -1) {
101 switch((char)ch) {
102
103 case 'M':
104 kmemf = optarg;
105 kflag = 1;
106 break;
107
108 case 'N':
109 systemname = optarg;
110 break;
111
112 case 'B':
113 Bflag = 1;
114 break;
115
116 case 'b':
117 bflag = 1;
118 break;
119
120 case 'h':
121 hflag = 1;
122 break;
123
124 case 'p':
125 pflag = 1;
126 break;
127
128 case 'r':
129 rflag = 1;
130 break;
131
132 default:
133 usage();
134 }
135 }
136 argc -= optind;
137 argv += optind;
138
139 #define BACKWARD_COMPATIBILITY
140 #ifdef BACKWARD_COMPATIBILITY
141 if (*argv) {
142 systemname = *argv;
143 if (*++argv) {
144 kmemf = *argv;
145 ++kflag;
146 }
147 }
148 #endif
149 if (systemname == NULL)
150 systemname = getbootfile();
151 accessmode = openfiles(systemname, kmemf, &kvmvars);
152 mode = getprof(&kvmvars);
153 if (hflag)
154 disp = GMON_PROF_OFF;
155 else if (Bflag)
156 disp = GMON_PROF_HIRES;
157 else if (bflag)
158 disp = GMON_PROF_ON;
159 else
160 disp = mode;
161 if (pflag)
162 dumpstate(&kvmvars);
163 if (rflag)
164 reset(&kvmvars);
165 if (accessmode == O_RDWR)
166 setprof(&kvmvars, disp);
167 (void)fprintf(stdout, "kgmon: kernel profiling is %s.\n",
168 disp == GMON_PROF_OFF ? "off" :
169 disp == GMON_PROF_HIRES ? "running (high resolution)" :
170 disp == GMON_PROF_ON ? "running" :
171 disp == GMON_PROF_BUSY ? "busy" :
172 disp == GMON_PROF_ERROR ? "off (error)" :
173 "in an unknown state");
174 return (0);
175 }
176
177 static void
usage(void)178 usage(void)
179 {
180 fprintf(stderr, "usage: kgmon [-Bbhrp] [-M core] [-N system]\n");
181 exit(1);
182 }
183
184 /*
185 * Check that profiling is enabled and open any necessary files.
186 */
187 int
openfiles(const char * systemname,char * kmemf,struct kvmvars * kvp)188 openfiles(const char *systemname, char *kmemf, struct kvmvars *kvp)
189 {
190 size_t size;
191 int mib[3], state, openmode;
192 char errbuf[_POSIX2_LINE_MAX];
193
194 if (!kflag) {
195 mib[0] = CTL_KERN;
196 mib[1] = KERN_PROF;
197 mib[2] = GPROF_STATE;
198 size = sizeof state;
199 if (sysctl(mib, 3, &state, &size, NULL, 0) < 0)
200 errx(20, "profiling not defined in kernel");
201 if (!(Bflag || bflag || hflag || rflag ||
202 (pflag &&
203 (state == GMON_PROF_HIRES || state == GMON_PROF_ON))))
204 return (O_RDONLY);
205 (void)seteuid(0);
206 if (sysctl(mib, 3, NULL, NULL, &state, size) >= 0)
207 return (O_RDWR);
208 (void)seteuid(getuid());
209 kern_readonly(state);
210 return (O_RDONLY);
211 }
212 openmode = (Bflag || bflag || hflag || pflag || rflag)
213 ? O_RDWR : O_RDONLY;
214 kvp->kd = kvm_openfiles(systemname, kmemf, NULL, openmode, errbuf);
215 if (kvp->kd == NULL) {
216 if (openmode == O_RDWR) {
217 openmode = O_RDONLY;
218 kvp->kd = kvm_openfiles(systemname, kmemf, NULL, O_RDONLY,
219 errbuf);
220 }
221 if (kvp->kd == NULL)
222 errx(2, "kvm_openfiles: %s", errbuf);
223 kern_readonly(GMON_PROF_ON);
224 }
225 if (kvm_nlist(kvp->kd, nl) < 0)
226 errx(3, "%s: no namelist", systemname);
227 if (!nl[N_GMONPARAM].n_value)
228 errx(20, "profiling not defined in kernel");
229 return (openmode);
230 }
231
232 /*
233 * Suppress options that require a writable kernel.
234 */
235 void
kern_readonly(int mode)236 kern_readonly(int mode)
237 {
238
239 (void)fprintf(stderr, "kgmon: kernel read-only: ");
240 if (pflag && (mode == GMON_PROF_HIRES || mode == GMON_PROF_ON))
241 (void)fprintf(stderr, "data may be inconsistent\n");
242 if (rflag)
243 (void)fprintf(stderr, "-r suppressed\n");
244 if (Bflag)
245 (void)fprintf(stderr, "-B suppressed\n");
246 if (bflag)
247 (void)fprintf(stderr, "-b suppressed\n");
248 if (hflag)
249 (void)fprintf(stderr, "-h suppressed\n");
250 rflag = Bflag = bflag = hflag = 0;
251 }
252
253 /*
254 * Get the state of kernel profiling.
255 */
256 int
getprof(struct kvmvars * kvp)257 getprof(struct kvmvars *kvp)
258 {
259 size_t size;
260 int mib[3];
261
262 if (kflag) {
263 size = kvm_read(kvp->kd, nl[N_GMONPARAM].n_value, &kvp->gpm,
264 sizeof kvp->gpm);
265 } else {
266 mib[0] = CTL_KERN;
267 mib[1] = KERN_PROF;
268 mib[2] = GPROF_GMONPARAM;
269 size = sizeof kvp->gpm;
270 if (sysctl(mib, 3, &kvp->gpm, &size, NULL, 0) < 0)
271 size = 0;
272 }
273
274 /*
275 * Accept certain undersized "structs" from old kernels. We need
276 * everything up to hashfraction, and want profrate and
277 * histcounter_type. Assume that the kernel doesn't put garbage
278 * in any padding that is returned instead of profrate and
279 * histcounter_type. This is a bad assumption for dead kernels,
280 * since kvm_read() will normally return garbage for bytes beyond
281 * the end of the actual kernel struct, if any.
282 */
283 if (size < offsetof(struct gmonparam, hashfraction) +
284 sizeof(kvp->gpm.hashfraction) || size > sizeof(kvp->gpm))
285 errx(4, "cannot get gmonparam: %s",
286 kflag ? kvm_geterr(kvp->kd) : strerror(errno));
287 bzero((char *)&kvp->gpm + size, sizeof(kvp->gpm) - size);
288 if (kvp->gpm.profrate == 0)
289 kvp->gpm.profrate = getprofhz(kvp);
290 #ifdef __i386__
291 if (kvp->gpm.histcounter_type == 0) {
292 /*
293 * This fixup only works for not-so-old i386 kernels. The
294 * magic 16 is the kernel FUNCTION_ALIGNMENT. 64-bit
295 * counters are signed; smaller counters are unsigned.
296 */
297 kvp->gpm.histcounter_type = 16 /
298 (kvp->gpm.textsize / kvp->gpm.kcountsize) * CHAR_BIT;
299 if (kvp->gpm.histcounter_type == 64)
300 kvp->gpm.histcounter_type = -64;
301 }
302 #endif
303
304 return (kvp->gpm.state);
305 }
306
307 /*
308 * Enable or disable kernel profiling according to the state variable.
309 */
310 void
setprof(struct kvmvars * kvp,int state)311 setprof(struct kvmvars *kvp, int state)
312 {
313 struct gmonparam *p = (struct gmonparam *)nl[N_GMONPARAM].n_value;
314 size_t sz;
315 int mib[3], oldstate;
316
317 sz = sizeof(state);
318 if (!kflag) {
319 mib[0] = CTL_KERN;
320 mib[1] = KERN_PROF;
321 mib[2] = GPROF_STATE;
322 if (sysctl(mib, 3, &oldstate, &sz, NULL, 0) < 0)
323 goto bad;
324 if (oldstate == state)
325 return;
326 (void)seteuid(0);
327 if (sysctl(mib, 3, NULL, NULL, &state, sz) >= 0) {
328 (void)seteuid(getuid());
329 return;
330 }
331 (void)seteuid(getuid());
332 } else if (kvm_write(kvp->kd, (u_long)&p->state, (void *)&state, sz)
333 == (ssize_t)sz)
334 return;
335 bad:
336 warnx("warning: cannot turn profiling %s",
337 state == GMON_PROF_OFF ? "off" : "on");
338 }
339
340 /*
341 * Build the gmon.out file.
342 */
343 void
dumpstate(struct kvmvars * kvp)344 dumpstate(struct kvmvars *kvp)
345 {
346 register FILE *fp;
347 struct rawarc rawarc;
348 struct tostruct *tos;
349 u_long frompc;
350 u_short *froms, *tickbuf;
351 size_t i;
352 int mib[3];
353 struct gmonhdr h;
354 int fromindex, endfrom, toindex;
355
356 setprof(kvp, GMON_PROF_OFF);
357 fp = fopen("gmon.out", "w");
358 if (fp == 0) {
359 warn("gmon.out");
360 return;
361 }
362
363 /*
364 * Build the gmon header and write it to a file.
365 */
366 bzero(&h, sizeof(h));
367 h.lpc = kvp->gpm.lowpc;
368 h.hpc = kvp->gpm.highpc;
369 h.ncnt = kvp->gpm.kcountsize + sizeof(h);
370 h.version = GMONVERSION;
371 h.profrate = kvp->gpm.profrate;
372 h.histcounter_type = kvp->gpm.histcounter_type;
373 fwrite((char *)&h, sizeof(h), 1, fp);
374
375 /*
376 * Write out the tick buffer.
377 */
378 mib[0] = CTL_KERN;
379 mib[1] = KERN_PROF;
380 if ((tickbuf = (u_short *)malloc(kvp->gpm.kcountsize)) == NULL)
381 errx(5, "cannot allocate kcount space");
382 if (kflag) {
383 i = kvm_read(kvp->kd, (u_long)kvp->gpm.kcount, (void *)tickbuf,
384 kvp->gpm.kcountsize);
385 } else {
386 mib[2] = GPROF_COUNT;
387 i = kvp->gpm.kcountsize;
388 if (sysctl(mib, 3, tickbuf, &i, NULL, 0) < 0)
389 i = 0;
390 }
391 if (i != kvp->gpm.kcountsize)
392 errx(6, "read ticks: read %lu, got %ld: %s",
393 kvp->gpm.kcountsize, (long)i,
394 kflag ? kvm_geterr(kvp->kd) : strerror(errno));
395 if ((fwrite(tickbuf, kvp->gpm.kcountsize, 1, fp)) != 1)
396 err(7, "writing tocks to gmon.out");
397 free(tickbuf);
398
399 /*
400 * Write out the arc info.
401 */
402 if ((froms = (u_short *)malloc(kvp->gpm.fromssize)) == NULL)
403 errx(8, "cannot allocate froms space");
404 if (kflag) {
405 i = kvm_read(kvp->kd, (u_long)kvp->gpm.froms, (void *)froms,
406 kvp->gpm.fromssize);
407 } else {
408 mib[2] = GPROF_FROMS;
409 i = kvp->gpm.fromssize;
410 if (sysctl(mib, 3, froms, &i, NULL, 0) < 0)
411 i = 0;
412 }
413 if (i != kvp->gpm.fromssize)
414 errx(9, "read froms: read %lu, got %ld: %s",
415 kvp->gpm.fromssize, (long)i,
416 kflag ? kvm_geterr(kvp->kd) : strerror(errno));
417 if ((tos = (struct tostruct *)malloc(kvp->gpm.tossize)) == NULL)
418 errx(10, "cannot allocate tos space");
419 if (kflag) {
420 i = kvm_read(kvp->kd, (u_long)kvp->gpm.tos, (void *)tos,
421 kvp->gpm.tossize);
422 } else {
423 mib[2] = GPROF_TOS;
424 i = kvp->gpm.tossize;
425 if (sysctl(mib, 3, tos, &i, NULL, 0) < 0)
426 i = 0;
427 }
428 if (i != kvp->gpm.tossize)
429 errx(11, "read tos: read %lu, got %ld: %s",
430 kvp->gpm.tossize, (long)i,
431 kflag ? kvm_geterr(kvp->kd) : strerror(errno));
432 if (debug)
433 warnx("lowpc 0x%lx, textsize 0x%lx",
434 (unsigned long)kvp->gpm.lowpc, kvp->gpm.textsize);
435 endfrom = kvp->gpm.fromssize / sizeof(*froms);
436 for (fromindex = 0; fromindex < endfrom; ++fromindex) {
437 if (froms[fromindex] == 0)
438 continue;
439 frompc = (u_long)kvp->gpm.lowpc +
440 (fromindex * kvp->gpm.hashfraction * sizeof(*froms));
441 for (toindex = froms[fromindex]; toindex != 0;
442 toindex = tos[toindex].link) {
443 if (debug)
444 warnx("[mcleanup] frompc 0x%lx selfpc 0x%lx "
445 "count %ld", frompc, tos[toindex].selfpc,
446 tos[toindex].count);
447 rawarc.raw_frompc = frompc;
448 rawarc.raw_selfpc = (u_long)tos[toindex].selfpc;
449 rawarc.raw_count = tos[toindex].count;
450 fwrite((char *)&rawarc, sizeof(rawarc), 1, fp);
451 }
452 }
453 fclose(fp);
454 }
455
456 /*
457 * Get the profiling rate.
458 */
459 int
getprofhz(struct kvmvars * kvp)460 getprofhz(struct kvmvars *kvp)
461 {
462 size_t size;
463 int mib[2], profrate;
464 struct clockinfo clockrate;
465
466 if (kflag) {
467 profrate = 1;
468 if (kvm_read(kvp->kd, nl[N_PROFHZ].n_value, &profrate,
469 sizeof profrate) != sizeof profrate)
470 warnx("get clockrate: %s", kvm_geterr(kvp->kd));
471 return (profrate);
472 }
473 mib[0] = CTL_KERN;
474 mib[1] = KERN_CLOCKRATE;
475 clockrate.profhz = 1;
476 size = sizeof clockrate;
477 if (sysctl(mib, 2, &clockrate, &size, NULL, 0) < 0)
478 warn("get clockrate");
479 return (clockrate.profhz);
480 }
481
482 /*
483 * Reset the kernel profiling date structures.
484 */
485 void
reset(struct kvmvars * kvp)486 reset(struct kvmvars *kvp)
487 {
488 char *zbuf;
489 u_long biggest;
490 int mib[3];
491
492 setprof(kvp, GMON_PROF_OFF);
493
494 biggest = kvp->gpm.kcountsize;
495 if (kvp->gpm.fromssize > biggest)
496 biggest = kvp->gpm.fromssize;
497 if (kvp->gpm.tossize > biggest)
498 biggest = kvp->gpm.tossize;
499 if ((zbuf = (char *)malloc(biggest)) == NULL)
500 errx(12, "cannot allocate zbuf space");
501 bzero(zbuf, biggest);
502 if (kflag) {
503 if (kvm_write(kvp->kd, (u_long)kvp->gpm.kcount, zbuf,
504 kvp->gpm.kcountsize) != (ssize_t)kvp->gpm.kcountsize)
505 errx(13, "tickbuf zero: %s", kvm_geterr(kvp->kd));
506 if (kvm_write(kvp->kd, (u_long)kvp->gpm.froms, zbuf,
507 kvp->gpm.fromssize) != (ssize_t)kvp->gpm.fromssize)
508 errx(14, "froms zero: %s", kvm_geterr(kvp->kd));
509 if (kvm_write(kvp->kd, (u_long)kvp->gpm.tos, zbuf,
510 kvp->gpm.tossize) != (ssize_t)kvp->gpm.tossize)
511 errx(15, "tos zero: %s", kvm_geterr(kvp->kd));
512 return;
513 }
514 (void)seteuid(0);
515 mib[0] = CTL_KERN;
516 mib[1] = KERN_PROF;
517 mib[2] = GPROF_COUNT;
518 if (sysctl(mib, 3, NULL, NULL, zbuf, kvp->gpm.kcountsize) < 0)
519 err(13, "tickbuf zero");
520 mib[2] = GPROF_FROMS;
521 if (sysctl(mib, 3, NULL, NULL, zbuf, kvp->gpm.fromssize) < 0)
522 err(14, "froms zero");
523 mib[2] = GPROF_TOS;
524 if (sysctl(mib, 3, NULL, NULL, zbuf, kvp->gpm.tossize) < 0)
525 err(15, "tos zero");
526 (void)seteuid(getuid());
527 free(zbuf);
528 }
529