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