1/* $NetBSD: kgmon.c,v 1.27 2021/08/14 17:51:20 ryo 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\ 35 The Regents of the University of California. All rights reserved."); 36#endif /* not lint */ 37 38#ifndef lint 39#if 0 40static char sccsid[] = "from: @(#)kgmon.c 8.1 (Berkeley) 6/6/93"; 41#else 42__RCSID("$NetBSD: kgmon.c,v 1.27 2021/08/14 17:51:20 ryo 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 <err.h> 55#include <limits.h> 56#include <nlist.h> 57#include <stdio.h> 58#include <stdlib.h> 59#include <string.h> 60#include <unistd.h> 61 62static struct nlist nl[] = { 63#define N_GMONPARAM 0 64 { "__gmonparam", 0, 0, 0, 0 }, 65#define N_PROFHZ 1 66 { "_profhz", 0, 0, 0, 0 }, 67 { 0, 0, 0, 0, 0 } 68}; 69 70struct kvmvars { 71 kvm_t *kd; 72 struct gmonparam gpm; 73}; 74 75static int ncpu_mib[2] = { CTL_HW, HW_NCPU }; 76static int bflag, cflag_all, hflag, kflag, rflag, pflag; 77static int debug = 0; 78static void setprof(struct kvmvars *kvp, int state, int cpuid); 79static void dumpstate(struct kvmvars *kvp, int cpuid); 80static void reset(struct kvmvars *kvp, int cpuid); 81static int openfiles(char *, char *, struct kvmvars *); 82static int getprof(struct kvmvars *, int); 83static void kern_readonly(int); 84static int getprofhz(struct kvmvars *); 85 86int 87main(int argc, char **argv) 88{ 89 int ch, mode, disp, accessmode, ncpu, cpuid = -1; 90 struct kvmvars kvmvars; 91 size_t size; 92 char *sys, *kmemf; 93 char on_cpu[sizeof(" on cpuXXXXXXXXX")]; 94 95 size = sizeof(ncpu); 96 if (sysctl(ncpu_mib, 2, &ncpu, &size, NULL, 0) < 0) 97 ncpu = 1; 98 99 setprogname(argv[0]); 100 (void)seteuid(getuid()); 101 kmemf = NULL; 102 sys = NULL; 103 while ((ch = getopt(argc, argv, "M:N:bc:dhpr")) != -1) { 104 switch((char)ch) { 105 106 case 'M': 107 kmemf = optarg; 108 kflag = 1; 109 break; 110 111 case 'N': 112 sys = optarg; 113 break; 114 115 case 'b': 116 bflag = 1; 117 break; 118 119 case 'c': 120 if (strcmp(optarg, "all") == 0) { 121 cflag_all = 1; 122 cpuid = 0; 123 } else { 124 cpuid = strtol(optarg, NULL, 10); 125 if (cpuid >= ncpu) 126 errx(1, "illegal CPU id %s", optarg); 127 } 128 break; 129 130 case 'h': 131 hflag = 1; 132 break; 133 134 case 'p': 135 pflag = 1; 136 break; 137 138 case 'r': 139 rflag = 1; 140 break; 141 142 case 'd': 143 debug = 1; 144 break; 145 146 default: 147 (void)fprintf(stderr, 148 "usage: %s [-bdhrp] [-c cpuid] [-M core] [-N system]\n", 149 getprogname()); 150 exit(1); 151 } 152 } 153 argc -= optind; 154 argv += optind; 155 156#define BACKWARD_COMPATIBILITY 157#ifdef BACKWARD_COMPATIBILITY 158 if (*argv) { 159 sys = *argv; 160 if (*++argv) { 161 kmemf = *argv; 162 ++kflag; 163 } 164 } 165#endif 166 accessmode = openfiles(sys, kmemf, &kvmvars); 167 168 do { 169 if (cpuid == -1) 170 on_cpu[0] = '\0'; 171 else 172 snprintf(on_cpu, sizeof(on_cpu), " on cpu%d", cpuid); 173 174 mode = getprof(&kvmvars, cpuid); 175 if (hflag) 176 disp = GMON_PROF_OFF; 177 else if (bflag) 178 disp = GMON_PROF_ON; 179 else 180 disp = mode; 181 if (pflag) 182 dumpstate(&kvmvars, cpuid); 183 if (rflag) 184 reset(&kvmvars, cpuid); 185 if (accessmode == O_RDWR) 186 setprof(&kvmvars, disp, cpuid); 187 (void)fprintf(stdout, "%s: kernel profiling is %s%s.\n", 188 getprogname(), disp == GMON_PROF_OFF ? "off" : "running", 189 on_cpu); 190 191 } while (cflag_all && ++cpuid < ncpu); 192 return (0); 193} 194 195/* 196 * Check that profiling is enabled and open any ncessary files. 197 */ 198static int 199openfiles(char *sys, char *kmemf, struct kvmvars *kvp) 200{ 201 int mib[3], state, openmode; 202 size_t size; 203 char errbuf[_POSIX2_LINE_MAX]; 204 205 if (!kflag) { 206 mib[0] = CTL_KERN; 207 mib[1] = KERN_PROF; 208 mib[2] = GPROF_STATE; 209 size = sizeof state; 210 if (sysctl(mib, 3, &state, &size, NULL, 0) < 0) 211 err(EXIT_FAILURE, "profiling not defined in kernel"); 212 if (!(bflag || hflag || rflag || 213 (pflag && state == GMON_PROF_ON))) 214 return (O_RDONLY); 215 (void)seteuid(0); 216 if (sysctl(mib, 3, NULL, NULL, &state, size) >= 0) 217 return (O_RDWR); 218 (void)seteuid(getuid()); 219 kern_readonly(state); 220 return (O_RDONLY); 221 } 222 openmode = (bflag || hflag || pflag || rflag) ? O_RDWR : O_RDONLY; 223 kvp->kd = kvm_openfiles(sys, kmemf, NULL, openmode, errbuf); 224 if (kvp->kd == NULL) { 225 if (openmode == O_RDWR) { 226 openmode = O_RDONLY; 227 kvp->kd = kvm_openfiles(sys, kmemf, NULL, O_RDONLY, 228 errbuf); 229 } 230 if (kvp->kd == NULL) 231 errx(EXIT_FAILURE, "kvm_openfiles: %s", errbuf); 232 kern_readonly(GMON_PROF_ON); 233 } 234 if (kvm_nlist(kvp->kd, nl) < 0) 235 errx(EXIT_FAILURE, "%s: no namelist", sys); 236 if (!nl[N_GMONPARAM].n_value) 237 errx(EXIT_FAILURE, "profiling not defined in kernel"); 238 return (openmode); 239} 240 241/* 242 * Suppress options that require a writable kernel. 243 */ 244static void 245kern_readonly(int mode) 246{ 247 248 (void)fprintf(stderr, "%s: kernel read-only: ", getprogname()); 249 if (pflag && mode == GMON_PROF_ON) 250 (void)fprintf(stderr, "data may be inconsistent\n"); 251 if (rflag) 252 (void)fprintf(stderr, "-r suppressed\n"); 253 if (bflag) 254 (void)fprintf(stderr, "-b suppressed\n"); 255 if (hflag) 256 (void)fprintf(stderr, "-h suppressed\n"); 257 rflag = bflag = hflag = 0; 258} 259 260/* 261 * Get the state of kernel profiling. 262 */ 263static int 264getprof(struct kvmvars *kvp, int cpuid) 265{ 266 int mib[5], miblen, mibparam; 267 size_t size; 268 269 if (kflag) { 270 size = kvm_read(kvp->kd, nl[N_GMONPARAM].n_value, &kvp->gpm, 271 sizeof kvp->gpm); 272 } else { 273 mib[0] = CTL_KERN; 274 mib[1] = KERN_PROF; 275 if (cpuid < 0) { 276 mibparam = 2; 277 miblen = 3; 278 } else { 279 mib[2] = GPROF_PERCPU; 280 mib[3] = cpuid; 281 mibparam = 4; 282 miblen = 5; 283 } 284 mib[mibparam] = GPROF_GMONPARAM; 285 size = sizeof kvp->gpm; 286 if (sysctl(mib, miblen, &kvp->gpm, &size, NULL, 0) < 0) 287 size = 0; 288 } 289 if (size != sizeof kvp->gpm) 290 errx(EXIT_FAILURE, "cannot get gmonparam: %s", 291 kflag ? kvm_geterr(kvp->kd) : strerror(errno)); 292 return (kvp->gpm.state); 293} 294 295/* 296 * Enable or disable kernel profiling according to the state variable. 297 */ 298static void 299setprof(struct kvmvars *kvp, int state, int cpuid) 300{ 301 struct gmonparam *p = (struct gmonparam *)nl[N_GMONPARAM].n_value; 302 int mib[5], miblen, mibparam, oldstate; 303 size_t sz; 304 305 sz = sizeof(state); 306 if (!kflag) { 307 mib[0] = CTL_KERN; 308 mib[1] = KERN_PROF; 309 if (cpuid < 0) { 310 mibparam = 2; 311 miblen = 3; 312 } else { 313 mib[2] = GPROF_PERCPU; 314 mib[3] = cpuid; 315 mibparam = 4; 316 miblen = 5; 317 } 318 mib[mibparam] = GPROF_STATE; 319 if (sysctl(mib, miblen, &oldstate, &sz, NULL, 0) < 0) 320 goto bad; 321 if (oldstate == state) 322 return; 323 (void)seteuid(0); 324 if (sysctl(mib, miblen, NULL, NULL, &state, sz) >= 0) { 325 (void)seteuid(getuid()); 326 return; 327 } 328 (void)seteuid(getuid()); 329 } else if ((size_t)kvm_write(kvp->kd, (u_long)&p->state, (void *)&state, sz) 330 == sz) 331 return; 332bad: 333 warnx("cannot turn profiling %s", state == GMON_PROF_OFF ? 334 "off" : "on"); 335} 336 337/* 338 * Build the gmon.out file. 339 */ 340static void 341dumpstate(struct kvmvars *kvp, int cpuid) 342{ 343 FILE *fp; 344 struct rawarc rawarc; 345 struct tostruct *tos; 346 u_long frompc; 347 u_short *froms, *tickbuf; 348 int mib[5], miblen, mibparam; 349 size_t i; 350 struct gmonhdr h; 351 int fromindex, endfrom, toindex; 352 size_t kcountsize; 353 char gmon_out[sizeof("gmon-XXXXXXXXXXX.out")]; 354 355 mib[0] = CTL_KERN; 356 mib[1] = KERN_PROF; 357 if (cpuid < 0) { 358 mibparam = 2; 359 miblen = 3; 360 } else { 361 mib[2] = GPROF_PERCPU; 362 mib[3] = cpuid; 363 mibparam = 4; 364 miblen = 5; 365 } 366 367 setprof(kvp, GMON_PROF_OFF, cpuid); 368 if (cpuid < 0) 369 strlcpy(gmon_out, "gmon.out", sizeof(gmon_out)); 370 else 371 snprintf(gmon_out, sizeof(gmon_out), "gmon-%d.out", cpuid); 372 373 fp = fopen(gmon_out, "w"); 374 if (fp == NULL) { 375 warn("cannot open `%s'", gmon_out); 376 return; 377 } 378 379 /* 380 * Build the gmon header and write it to a file. 381 */ 382 bzero(&h, sizeof(h)); 383 h.lpc = kvp->gpm.lowpc; 384 h.hpc = kvp->gpm.highpc; 385 h.ncnt = kvp->gpm.kcountsize + sizeof(h); 386 h.version = GMONVERSION; 387 h.profrate = getprofhz(kvp); 388 if (fwrite(&h, sizeof(h), 1, fp) != 1) 389 err(EXIT_FAILURE, "writing header to gmon.out"); 390 391 kcountsize = (size_t)kvp->gpm.kcountsize; 392 393 /* 394 * Write out the tick buffer. 395 */ 396 if ((tickbuf = malloc(kcountsize)) == NULL) 397 err(EXIT_FAILURE, "Cannot allocate %zu kcount space", 398 kcountsize); 399 if (kflag) { 400 i = kvm_read(kvp->kd, (u_long)kvp->gpm.kcount, tickbuf, 401 kcountsize); 402 } else { 403 mib[mibparam] = GPROF_COUNT; 404 i = kcountsize; 405 if (sysctl(mib, miblen, tickbuf, &i, NULL, 0) < 0) 406 i = 0; 407 } 408 if (i != kcountsize) 409 errx(EXIT_FAILURE, "read ticks: read %zu, got %zu: %s", 410 kcountsize, i, 411 kflag ? kvm_geterr(kvp->kd) : strerror(errno)); 412 if ((fwrite(tickbuf, kcountsize, 1, fp)) != 1) 413 err(EXIT_FAILURE, "writing ticks to gmon.out"); 414 free(tickbuf); 415 416 /* 417 * Write out the arc info. 418 */ 419 if ((froms = malloc((size_t)kvp->gpm.fromssize)) == NULL) 420 err(EXIT_FAILURE, "cannot allocate %zu froms space", 421 (size_t)kvp->gpm.fromssize); 422 if (kflag) { 423 i = kvm_read(kvp->kd, (u_long)kvp->gpm.froms, froms, 424 (size_t)kvp->gpm.fromssize); 425 } else { 426 mib[mibparam] = GPROF_FROMS; 427 i = kvp->gpm.fromssize; 428 if (sysctl(mib, miblen, froms, &i, NULL, 0) < 0) 429 i = 0; 430 } 431 if (i != kvp->gpm.fromssize) 432 errx(EXIT_FAILURE, "read froms: read %lu, got %lu: %s", 433 kvp->gpm.fromssize, (u_long)i, 434 kflag ? kvm_geterr(kvp->kd) : strerror(errno)); 435 if ((tos = malloc((size_t)kvp->gpm.tossize)) == NULL) 436 err(EXIT_FAILURE, "cannot allocate %zu tos space", 437 (size_t)kvp->gpm.tossize); 438 if (kflag) { 439 i = kvm_read(kvp->kd, (u_long)kvp->gpm.tos, (void *)tos, 440 (size_t)kvp->gpm.tossize); 441 } else { 442 mib[mibparam] = GPROF_TOS; 443 i = kvp->gpm.tossize; 444 if (sysctl(mib, miblen, tos, &i, NULL, 0) < 0) 445 i = 0; 446 } 447 if (i != kvp->gpm.tossize) 448 errx(EXIT_FAILURE, "read tos: read %zu, got %zu: %s", 449 (size_t)kvp->gpm.tossize, i, 450 kflag ? kvm_geterr(kvp->kd) : strerror(errno)); 451 if (debug) 452 (void)fprintf(stderr, "%s: lowpc 0x%lx, textsize 0x%lx\n", 453 getprogname(), kvp->gpm.lowpc, kvp->gpm.textsize); 454 endfrom = kvp->gpm.fromssize / sizeof(*froms); 455 for (fromindex = 0; fromindex < endfrom; ++fromindex) { 456 if (froms[fromindex] == 0) 457 continue; 458 frompc = (u_long)kvp->gpm.lowpc + 459 (fromindex * kvp->gpm.hashfraction * sizeof(*froms)); 460 for (toindex = froms[fromindex]; toindex != 0; 461 toindex = tos[toindex].link) { 462 if (debug) 463 (void)fprintf(stderr, 464 "%s: [mcleanup] frompc 0x%lx selfpc 0x%lx count %ld\n", 465 getprogname(), frompc, tos[toindex].selfpc, 466 tos[toindex].count); 467 rawarc.raw_frompc = frompc; 468 rawarc.raw_selfpc = (u_long)tos[toindex].selfpc; 469 rawarc.raw_count = tos[toindex].count; 470 if (fwrite(&rawarc, sizeof(rawarc), 1,fp) != 1){ 471 err(EXIT_FAILURE, 472 "writing raw arc to gmon.out"); 473 } 474 } 475 } 476 free(tos); 477 (void)fclose(fp); 478} 479 480/* 481 * Get the profiling rate. 482 */ 483static int 484getprofhz(struct kvmvars *kvp) 485{ 486 int mib[2], profrate; 487 size_t size; 488 struct clockinfo clockrate; 489 490 if (kflag) { 491 profrate = 1; 492 if (kvm_read(kvp->kd, nl[N_PROFHZ].n_value, &profrate, 493 sizeof profrate) != sizeof profrate) 494 warnx("get clockrate: %s", kvm_geterr(kvp->kd)); 495 return (profrate); 496 } 497 mib[0] = CTL_KERN; 498 mib[1] = KERN_CLOCKRATE; 499 clockrate.profhz = 1; 500 size = sizeof clockrate; 501 if (sysctl(mib, 2, &clockrate, &size, NULL, 0) < 0) 502 warn("get clockrate"); 503 return (clockrate.profhz); 504} 505 506/* 507 * Reset the kernel profiling date structures. 508 */ 509static void 510reset(struct kvmvars *kvp, int cpuid) 511{ 512 char *zbuf; 513 size_t biggest; 514 int mib[5], miblen, mibparam; 515 516 mib[0] = CTL_KERN; 517 mib[1] = KERN_PROF; 518 if (cpuid < 0) { 519 mibparam = 2; 520 miblen = 3; 521 } else { 522 mib[2] = GPROF_PERCPU; 523 mib[3] = cpuid; 524 mibparam = 4; 525 miblen = 5; 526 } 527 528 setprof(kvp, GMON_PROF_OFF, cpuid); 529 530 biggest = (size_t)kvp->gpm.kcountsize; 531 if ((size_t)kvp->gpm.fromssize > biggest) 532 biggest = (size_t)kvp->gpm.fromssize; 533 if ((size_t)kvp->gpm.tossize > biggest) 534 biggest = (size_t)kvp->gpm.tossize; 535 if ((zbuf = malloc(biggest)) == NULL) 536 err(EXIT_FAILURE, "cannot allocate zbuf space"); 537 (void)memset(zbuf, 0, biggest); 538 if (kflag) { 539 if ((size_t)kvm_write(kvp->kd, (u_long)kvp->gpm.kcount, zbuf, 540 (size_t)kvp->gpm.kcountsize) != kvp->gpm.kcountsize) 541 errx(EXIT_FAILURE, "tickbuf zero: %s", 542 kvm_geterr(kvp->kd)); 543 if ((size_t)kvm_write(kvp->kd, (u_long)kvp->gpm.froms, zbuf, 544 (size_t)kvp->gpm.fromssize) != kvp->gpm.fromssize) 545 errx(EXIT_FAILURE, "froms zero: %s", 546 kvm_geterr(kvp->kd)); 547 if ((size_t)kvm_write(kvp->kd, (u_long)kvp->gpm.tos, zbuf, 548 (size_t)kvp->gpm.tossize) != kvp->gpm.tossize) 549 errx(EXIT_FAILURE, "tos zero: %s", kvm_geterr(kvp->kd)); 550 free(zbuf); 551 return; 552 } 553 (void)seteuid(0); 554 mib[mibparam] = GPROF_COUNT; 555 if (sysctl(mib, miblen, NULL, NULL, zbuf, (size_t)kvp->gpm.kcountsize) < 0) 556 err(EXIT_FAILURE, "tickbuf zero"); 557 mib[mibparam] = GPROF_FROMS; 558 if (sysctl(mib, miblen, NULL, NULL, zbuf, (size_t)kvp->gpm.fromssize) < 0) 559 err(EXIT_FAILURE, "froms zero"); 560 mib[mibparam] = GPROF_TOS; 561 if (sysctl(mib, miblen, NULL, NULL, zbuf, (size_t)kvp->gpm.tossize) < 0) 562 err(EXIT_FAILURE, "tos zero"); 563 (void)seteuid(getuid()); 564 free(zbuf); 565} 566