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