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