2 * Copyright (c) 1983, 1992, 1993
3 * The Regents of the University of California. All rights reserved.
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
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.
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
31 static const char copyright[] =
32 "@(#) Copyright (c) 1983, 1992, 1993\n\
33 The Regents of the University of California. All rights reserved.\n";
38 static char sccsid[] = "@(#)kgmon.c 8.1 (Berkeley) 6/6/93";
42 #include <sys/cdefs.h>
43 __FBSDID("$FreeBSD$");
45 #include <sys/param.h>
48 #include <sys/sysctl.h>
76 int Bflag, bflag, hflag, kflag, rflag, pflag;
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);
88 main(int argc, char **argv)
90 int ch, mode, disp, accessmode;
91 struct kvmvars kvmvars;
92 const char *systemname;
95 if (seteuid(getuid()) != 0) {
96 err(1, "seteuid failed\n");
100 while ((ch = getopt(argc, argv, "M:N:Bbhpr")) != -1) {
139 #define BACKWARD_COMPATIBILITY
140 #ifdef BACKWARD_COMPATIBILITY
149 if (systemname == NULL)
150 systemname = getbootfile();
151 accessmode = openfiles(systemname, kmemf, &kvmvars);
152 mode = getprof(&kvmvars);
154 disp = GMON_PROF_OFF;
156 disp = GMON_PROF_HIRES;
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");
180 fprintf(stderr, "usage: kgmon [-Bbhrp] [-M core] [-N system]\n");
185 * Check that profiling is enabled and open any necessary files.
188 openfiles(const char *systemname, char *kmemf, struct kvmvars *kvp)
191 int mib[3], state, openmode;
192 char errbuf[_POSIX2_LINE_MAX];
197 mib[2] = GPROF_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 ||
203 (state == GMON_PROF_HIRES || state == GMON_PROF_ON))))
206 if (sysctl(mib, 3, NULL, NULL, &state, size) >= 0)
208 (void)seteuid(getuid());
209 kern_readonly(state);
212 openmode = (Bflag || bflag || hflag || pflag || rflag)
214 kvp->kd = kvm_openfiles(systemname, kmemf, NULL, openmode, errbuf);
215 if (kvp->kd == NULL) {
216 if (openmode == O_RDWR) {
218 kvp->kd = kvm_openfiles(systemname, kmemf, NULL, O_RDONLY,
222 errx(2, "kvm_openfiles: %s", errbuf);
223 kern_readonly(GMON_PROF_ON);
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");
233 * Suppress options that require a writable kernel.
236 kern_readonly(int mode)
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");
243 (void)fprintf(stderr, "-r suppressed\n");
245 (void)fprintf(stderr, "-B suppressed\n");
247 (void)fprintf(stderr, "-b suppressed\n");
249 (void)fprintf(stderr, "-h suppressed\n");
250 rflag = Bflag = bflag = hflag = 0;
254 * Get the state of kernel profiling.
257 getprof(struct kvmvars *kvp)
263 size = kvm_read(kvp->kd, nl[N_GMONPARAM].n_value, &kvp->gpm,
268 mib[2] = GPROF_GMONPARAM;
269 size = sizeof kvp->gpm;
270 if (sysctl(mib, 3, &kvp->gpm, &size, NULL, 0) < 0)
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.
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);
291 if (kvp->gpm.histcounter_type == 0) {
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.
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;
304 return (kvp->gpm.state);
308 * Enable or disable kernel profiling according to the state variable.
311 setprof(struct kvmvars *kvp, int state)
313 struct gmonparam *p = (struct gmonparam *)nl[N_GMONPARAM].n_value;
315 int mib[3], oldstate;
321 mib[2] = GPROF_STATE;
322 if (sysctl(mib, 3, &oldstate, &sz, NULL, 0) < 0)
324 if (oldstate == state)
327 if (sysctl(mib, 3, NULL, NULL, &state, sz) >= 0) {
328 (void)seteuid(getuid());
331 (void)seteuid(getuid());
332 } else if (kvm_write(kvp->kd, (u_long)&p->state, (void *)&state, sz)
336 warnx("warning: cannot turn profiling %s",
337 state == GMON_PROF_OFF ? "off" : "on");
341 * Build the gmon.out file.
344 dumpstate(struct kvmvars *kvp)
347 struct rawarc rawarc;
348 struct tostruct *tos;
350 u_short *froms, *tickbuf;
354 int fromindex, endfrom, toindex;
356 setprof(kvp, GMON_PROF_OFF);
357 fp = fopen("gmon.out", "w");
364 * Build the gmon header and write it to a file.
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);
376 * Write out the tick buffer.
380 if ((tickbuf = (u_short *)malloc(kvp->gpm.kcountsize)) == NULL)
381 errx(5, "cannot allocate kcount space");
383 i = kvm_read(kvp->kd, (u_long)kvp->gpm.kcount, (void *)tickbuf,
384 kvp->gpm.kcountsize);
386 mib[2] = GPROF_COUNT;
387 i = kvp->gpm.kcountsize;
388 if (sysctl(mib, 3, tickbuf, &i, NULL, 0) < 0)
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");
400 * Write out the arc info.
402 if ((froms = (u_short *)malloc(kvp->gpm.fromssize)) == NULL)
403 errx(8, "cannot allocate froms space");
405 i = kvm_read(kvp->kd, (u_long)kvp->gpm.froms, (void *)froms,
408 mib[2] = GPROF_FROMS;
409 i = kvp->gpm.fromssize;
410 if (sysctl(mib, 3, froms, &i, NULL, 0) < 0)
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");
420 i = kvm_read(kvp->kd, (u_long)kvp->gpm.tos, (void *)tos,
424 i = kvp->gpm.tossize;
425 if (sysctl(mib, 3, tos, &i, NULL, 0) < 0)
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));
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)
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) {
444 warnx("[mcleanup] frompc 0x%lx selfpc 0x%lx "
445 "count %ld", frompc, tos[toindex].selfpc,
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);
457 * Get the profiling rate.
460 getprofhz(struct kvmvars *kvp)
463 int mib[2], profrate;
464 struct clockinfo clockrate;
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));
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);
483 * Reset the kernel profiling date structures.
486 reset(struct kvmvars *kvp)
492 setprof(kvp, GMON_PROF_OFF);
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);
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));
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");
524 if (sysctl(mib, 3, NULL, NULL, zbuf, kvp->gpm.tossize) < 0)
526 (void)seteuid(getuid());