1 /* $NetBSD: gmon.c,v 1.37 2022/05/06 04:49:13 rin Exp $ */ 2 3 /* 4 * Copyright (c) 2003, 2004 Wasabi Systems, Inc. 5 * All rights reserved. 6 * 7 * Written by Nathan J. Williams for Wasabi Systems, Inc. 8 * 9 * Redistribution and use in source and binary forms, with or without 10 * modification, are permitted provided that the following conditions 11 * are met: 12 * 1. Redistributions of source code must retain the above copyright 13 * notice, this list of conditions and the following disclaimer. 14 * 2. Redistributions in binary form must reproduce the above copyright 15 * notice, this list of conditions and the following disclaimer in the 16 * documentation and/or other materials provided with the distribution. 17 * 3. All advertising materials mentioning features or use of this software 18 * must display the following acknowledgement: 19 * This product includes software developed for the NetBSD Project by 20 * Wasabi Systems, Inc. 21 * 4. The name of Wasabi Systems, Inc. may not be used to endorse 22 * or promote products derived from this software without specific prior 23 * written permission. 24 * 25 * THIS SOFTWARE IS PROVIDED BY WASABI SYSTEMS, INC. ``AS IS'' AND 26 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 27 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 28 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL WASABI SYSTEMS, INC 29 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 30 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 31 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 32 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 33 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 34 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 35 * POSSIBILITY OF SUCH DAMAGE. 36 */ 37 38 /*- 39 * Copyright (c) 1983, 1992, 1993 40 * The Regents of the University of California. All rights reserved. 41 * 42 * Redistribution and use in source and binary forms, with or without 43 * modification, are permitted provided that the following conditions 44 * are met: 45 * 1. Redistributions of source code must retain the above copyright 46 * notice, this list of conditions and the following disclaimer. 47 * 2. Redistributions in binary form must reproduce the above copyright 48 * notice, this list of conditions and the following disclaimer in the 49 * documentation and/or other materials provided with the distribution. 50 * 3. Neither the name of the University nor the names of its contributors 51 * may be used to endorse or promote products derived from this software 52 * without specific prior written permission. 53 * 54 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 55 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 56 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 57 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 58 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 59 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 60 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 61 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 62 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 63 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 64 * SUCH DAMAGE. 65 */ 66 67 #include <sys/cdefs.h> 68 #if !defined(lint) && defined(LIBC_SCCS) 69 #if 0 70 static char sccsid[] = "@(#)gmon.c 8.1 (Berkeley) 6/4/93"; 71 #else 72 __RCSID("$NetBSD: gmon.c,v 1.37 2022/05/06 04:49:13 rin Exp $"); 73 #endif 74 #endif 75 76 #include "namespace.h" 77 #include <sys/param.h> 78 #include <sys/time.h> 79 #include <sys/gmon.h> 80 #include <sys/mman.h> 81 #include <sys/sysctl.h> 82 83 #include <stdio.h> 84 #include <stdlib.h> 85 #include <string.h> 86 #include <fcntl.h> 87 #include <limits.h> 88 #include <unistd.h> 89 #include <err.h> 90 #include "extern.h" 91 #include "reentrant.h" 92 93 struct gmonparam _gmonparam = { .state = GMON_PROF_OFF }; 94 95 #ifdef _REENTRANT 96 struct gmonparam *_gmonfree; 97 struct gmonparam *_gmoninuse; 98 mutex_t _gmonlock = MUTEX_INITIALIZER; 99 thread_key_t _gmonkey; 100 struct gmonparam _gmondummy; 101 #endif 102 103 static u_int s_scale; 104 /* see profil(2) where this is describe (incorrectly) */ 105 #define SCALE_1_TO_1 0x10000L 106 107 void moncontrol(int); 108 void monstartup(u_long, u_long); 109 void _mcleanup(void); 110 static int hertz(void); 111 112 #ifdef _REENTRANT 113 static void _m_gmon_destructor(void *); 114 struct gmonparam *_m_gmon_alloc(void) 115 __attribute__((__no_instrument_function__)); 116 static void _m_gmon_merge(void); 117 static void _m_gmon_merge_two(struct gmonparam *, struct gmonparam *); 118 #endif 119 120 void 121 monstartup(u_long lowpc, u_long highpc) 122 { 123 u_long o; 124 char *cp; 125 struct gmonparam *p = &_gmonparam; 126 127 /* 128 * round lowpc and highpc to multiples of the density we're using 129 * so the rest of the scaling (here and in gprof) stays in ints. 130 */ 131 p->lowpc = rounddown(lowpc, HISTFRACTION * sizeof(HISTCOUNTER)); 132 p->highpc = roundup(highpc, HISTFRACTION * sizeof(HISTCOUNTER)); 133 p->textsize = p->highpc - p->lowpc; 134 p->kcountsize = p->textsize / HISTFRACTION; 135 p->hashfraction = HASHFRACTION; 136 p->fromssize = p->textsize / p->hashfraction; 137 p->tolimit = p->textsize * ARCDENSITY / 100; 138 if (p->tolimit < MINARCS) 139 p->tolimit = MINARCS; 140 else if (p->tolimit > MAXARCS) 141 p->tolimit = MAXARCS; 142 p->tossize = p->tolimit * sizeof(struct tostruct); 143 144 cp = sbrk((intptr_t)0); 145 146 #define GMON_ALLOC(buf, ptr, size) \ 147 do { \ 148 (buf) = (void *)roundup((uintptr_t)(buf), __alignof(*(ptr))); \ 149 (ptr) = (void *)(buf); \ 150 (buf) += (size); \ 151 } while (0) 152 153 GMON_ALLOC(cp, p->kcount, p->kcountsize); 154 GMON_ALLOC(cp, p->froms, p->fromssize); 155 GMON_ALLOC(cp, p->tos, p->tossize); 156 157 if (brk(cp)) { 158 warnx("%s: out of memory", __func__); 159 return; 160 } 161 __minbrk = cp; 162 163 p->tos[0].link = 0; 164 165 o = p->highpc - p->lowpc; 166 if (p->kcountsize < o) { 167 #ifndef notdef 168 s_scale = ((float)p->kcountsize / o ) * SCALE_1_TO_1; 169 #else /* avoid floating point */ 170 u_long quot = o / p->kcountsize; 171 172 if (quot >= 0x10000) 173 s_scale = 1; 174 else if (quot >= 0x100) 175 s_scale = 0x10000 / quot; 176 else if (o >= 0x800000) 177 s_scale = 0x1000000 / (o / (p->kcountsize >> 8)); 178 else 179 s_scale = 0x1000000 / ((o << 8) / p->kcountsize); 180 #endif 181 } else 182 s_scale = SCALE_1_TO_1; 183 184 #ifdef _REENTRANT 185 _gmondummy.state = GMON_PROF_BUSY; 186 thr_keycreate(&_gmonkey, _m_gmon_destructor); 187 #endif 188 moncontrol(1); 189 } 190 191 #ifdef _REENTRANT 192 static void 193 _m_gmon_destructor(void *arg) 194 { 195 struct gmonparam *p = arg, *q, **prev; 196 197 if (p == &_gmondummy) 198 return; 199 200 thr_setspecific(_gmonkey, &_gmondummy); 201 202 mutex_lock(&_gmonlock); 203 /* XXX eww, linear list traversal. */ 204 for (q = _gmoninuse, prev = &_gmoninuse; 205 q != NULL; 206 prev = (struct gmonparam **)(void *)&q->kcount, /* XXX */ 207 q = (struct gmonparam *)(void *)q->kcount) { 208 if (q == p) 209 *prev = (struct gmonparam *)(void *)q->kcount; 210 } 211 p->kcount = (u_short *)(void *)_gmonfree; 212 _gmonfree = p; 213 mutex_unlock(&_gmonlock); 214 215 thr_setspecific(_gmonkey, NULL); 216 } 217 218 struct gmonparam * 219 _m_gmon_alloc(void) 220 { 221 struct gmonparam *p; 222 char *cp; 223 224 mutex_lock(&_gmonlock); 225 if (_gmonfree != NULL) { 226 p = _gmonfree; 227 _gmonfree = (struct gmonparam *)(void *)p->kcount; 228 p->kcount = (u_short *)(void *)_gmoninuse; 229 _gmoninuse = p; 230 } else { 231 mutex_unlock(&_gmonlock); 232 cp = mmap(NULL, 233 (size_t)(__alignof(*p) + sizeof(*p) + 234 __alignof(*_gmonparam.froms) + _gmonparam.fromssize + 235 __alignof(*_gmonparam.tos) + _gmonparam.tossize), 236 PROT_READ|PROT_WRITE, MAP_ANON|MAP_PRIVATE, -1, (off_t)0); 237 238 GMON_ALLOC(cp, p, sizeof(*p)); 239 *p = _gmonparam; 240 p->state = GMON_PROF_ON; 241 p->kcount = NULL; 242 243 GMON_ALLOC(cp, p->froms, p->fromssize); 244 memset(p->froms, 0, p->fromssize); 245 246 GMON_ALLOC(cp, p->tos, p->tossize); 247 memset(p->tos, 0, p->tossize); 248 249 mutex_lock(&_gmonlock); 250 p->kcount = (u_short *)(void *)_gmoninuse; 251 _gmoninuse = p; 252 } 253 mutex_unlock(&_gmonlock); 254 thr_setspecific(_gmonkey, p); 255 256 return p; 257 } 258 259 static void 260 _m_gmon_merge_two(struct gmonparam *p, struct gmonparam *q) 261 { 262 u_long fromindex; 263 u_short *frompcindex, qtoindex, toindex; 264 u_long selfpc; 265 u_long endfrom; 266 long count; 267 struct tostruct *top; 268 269 endfrom = (q->fromssize / sizeof(*q->froms)); 270 for (fromindex = 0; fromindex < endfrom; fromindex++) { 271 if (q->froms[fromindex] == 0) 272 continue; 273 for (qtoindex = q->froms[fromindex]; qtoindex != 0; 274 qtoindex = q->tos[qtoindex].link) { 275 selfpc = q->tos[qtoindex].selfpc; 276 count = q->tos[qtoindex].count; 277 /* cribbed from mcount */ 278 frompcindex = &p->froms[fromindex]; 279 toindex = *frompcindex; 280 if (toindex == 0) { 281 /* 282 * first time traversing this arc 283 */ 284 toindex = ++p->tos[0].link; 285 if (toindex >= p->tolimit) 286 /* halt further profiling */ 287 goto overflow; 288 289 *frompcindex = (u_short)toindex; 290 top = &p->tos[(size_t)toindex]; 291 top->selfpc = selfpc; 292 top->count = count; 293 top->link = 0; 294 goto done; 295 } 296 top = &p->tos[(size_t)toindex]; 297 if (top->selfpc == selfpc) { 298 /* 299 * arc at front of chain; usual case. 300 */ 301 top->count+= count; 302 goto done; 303 } 304 /* 305 * have to go looking down chain for it. 306 * top points to what we are looking at, 307 * we know it is not at the head of the chain. 308 */ 309 for (; /* goto done */; ) { 310 if (top->link == 0) { 311 /* 312 * top is end of the chain and 313 * none of the chain had 314 * top->selfpc == selfpc. so 315 * we allocate a new tostruct 316 * and link it to the head of 317 * the chain. 318 */ 319 toindex = ++p->tos[0].link; 320 if (toindex >= p->tolimit) 321 goto overflow; 322 323 top = &p->tos[(size_t)toindex]; 324 top->selfpc = selfpc; 325 top->count = count; 326 top->link = *frompcindex; 327 *frompcindex = (u_short)toindex; 328 goto done; 329 } 330 /* 331 * otherwise, check the next arc on the chain. 332 */ 333 top = &p->tos[top->link]; 334 if (top->selfpc == selfpc) { 335 /* 336 * there it is. 337 * add to its count. 338 */ 339 top->count += count; 340 goto done; 341 } 342 343 } 344 345 done: ; 346 } 347 348 } 349 overflow: ; 350 351 } 352 353 static void 354 _m_gmon_merge(void) 355 { 356 struct gmonparam *q; 357 358 mutex_lock(&_gmonlock); 359 360 for (q = _gmonfree; q != NULL; 361 q = (struct gmonparam *)(void *)q->kcount) 362 _m_gmon_merge_two(&_gmonparam, q); 363 364 for (q = _gmoninuse; q != NULL; 365 q = (struct gmonparam *)(void *)q->kcount) { 366 q->state = GMON_PROF_OFF; 367 _m_gmon_merge_two(&_gmonparam, q); 368 } 369 370 mutex_unlock(&_gmonlock); 371 } 372 #endif 373 374 void 375 _mcleanup(void) 376 { 377 int fd; 378 int fromindex; 379 int endfrom; 380 u_long frompc; 381 int toindex; 382 struct rawarc rawarc; 383 struct gmonparam *p = &_gmonparam; 384 struct gmonhdr gmonhdr, *hdr; 385 struct clockinfo clockinfo; 386 int mib[2]; 387 size_t size; 388 char *profdir; 389 const char *proffile; 390 char buf[PATH_MAX]; 391 #ifdef DEBUG 392 int logfd, len; 393 char buf2[200]; 394 #endif 395 396 /* 397 * We disallow writing to the profiling file, if we are a 398 * set{u,g}id program and our effective {u,g}id does not match 399 * our real one. 400 */ 401 if (issetugid() && (geteuid() != getuid() || getegid() != getgid())) { 402 warnx("%s: Profiling of set{u,g}id binaries is not" 403 " allowed", __func__); 404 return; 405 } 406 407 if (p->state == GMON_PROF_ERROR) 408 warnx("%s: tos overflow", __func__); 409 410 size = sizeof(clockinfo); 411 mib[0] = CTL_KERN; 412 mib[1] = KERN_CLOCKRATE; 413 if (sysctl(mib, 2, &clockinfo, &size, NULL, 0) < 0) { 414 /* 415 * Best guess 416 */ 417 clockinfo.profhz = hertz(); 418 } else if (clockinfo.profhz == 0) { 419 if (clockinfo.hz != 0) 420 clockinfo.profhz = clockinfo.hz; 421 else 422 clockinfo.profhz = hertz(); 423 } 424 425 moncontrol(0); 426 427 if ((profdir = getenv("PROFDIR")) != NULL) { 428 /* If PROFDIR contains a null value, no profiling 429 output is produced */ 430 if (*profdir == '\0') 431 return; 432 433 if (snprintf(buf, sizeof buf, "%s/%d.%s", 434 profdir, getpid(), getprogname()) >= (int)(sizeof buf)) { 435 warnx("%s: internal buffer overflow, PROFDIR too long", 436 __func__); 437 return; 438 } 439 440 proffile = buf; 441 } else { 442 proffile = "gmon.out"; 443 } 444 445 #define OPEN_FLAGS (O_CREAT | O_TRUNC | O_WRONLY | O_CLOEXEC) 446 fd = open(proffile, OPEN_FLAGS, 0666); 447 if (fd < 0) { 448 warn("%s: Cannot open `%s'", __func__, proffile); 449 return; 450 } 451 #ifdef DEBUG 452 logfd = open("gmon.log", OPEN_FLAGS, 0664); 453 if (logfd < 0) { 454 warn("%s: Cannot open `%s'", __func__, "gmon.log"); 455 (void)close(fd); 456 return; 457 } 458 len = snprintf(buf2, sizeof buf2, "[mcleanup1] kcount %p ssiz %lu\n", 459 p->kcount, p->kcountsize); 460 (void)write(logfd, buf2, (size_t)len); 461 #endif 462 #ifdef _REENTRANT 463 _m_gmon_merge(); 464 #endif 465 hdr = (struct gmonhdr *)&gmonhdr; 466 hdr->lpc = p->lowpc; 467 hdr->hpc = p->highpc; 468 hdr->ncnt = (int)(p->kcountsize + sizeof(gmonhdr)); 469 hdr->version = GMONVERSION; 470 hdr->profrate = clockinfo.profhz; 471 (void)write(fd, hdr, sizeof *hdr); 472 (void)write(fd, p->kcount, (size_t)p->kcountsize); 473 endfrom = (int)(p->fromssize / sizeof(*p->froms)); 474 for (fromindex = 0; fromindex < endfrom; fromindex++) { 475 if (p->froms[fromindex] == 0) 476 continue; 477 478 frompc = p->lowpc; 479 frompc += fromindex * p->hashfraction * sizeof(*p->froms); 480 for (toindex = p->froms[fromindex]; toindex != 0; 481 toindex = p->tos[toindex].link) { 482 #ifdef DEBUG 483 len = snprintf(buf2, sizeof buf2, 484 "[mcleanup2] frompc 0x%lx selfpc 0x%lx count %lu\n" , 485 (u_long)frompc, (u_long)p->tos[toindex].selfpc, 486 (u_long)p->tos[toindex].count); 487 (void)write(logfd, buf2, (size_t)len); 488 #endif 489 rawarc.raw_frompc = frompc; 490 rawarc.raw_selfpc = p->tos[toindex].selfpc; 491 rawarc.raw_count = p->tos[toindex].count; 492 (void)write(fd, &rawarc, sizeof rawarc); 493 } 494 } 495 (void)close(fd); 496 #ifdef DEBUG 497 (void)close(logfd); 498 #endif 499 } 500 501 /* 502 * Control profiling 503 * profiling is what mcount checks to see if 504 * all the data structures are ready. 505 */ 506 void 507 moncontrol(int mode) 508 { 509 struct gmonparam *p = &_gmonparam; 510 511 if (mode) { 512 /* start */ 513 profil((char *)(void *)p->kcount, (size_t)p->kcountsize, 514 p->lowpc, s_scale); 515 p->state = GMON_PROF_ON; 516 } else { 517 /* stop */ 518 profil(NULL, 0, (u_long)0, 0); 519 p->state = GMON_PROF_OFF; 520 } 521 } 522 523 /* 524 * discover the tick frequency of the machine 525 * if something goes wrong, we return 0, an impossible hertz. 526 */ 527 static int 528 hertz(void) 529 { 530 struct itimerspec tim; 531 timer_t t; 532 int rv = 0; 533 534 tim.it_interval.tv_sec = 0; 535 tim.it_interval.tv_nsec = 1; 536 tim.it_value.tv_sec = 0; 537 tim.it_value.tv_nsec = 0; 538 539 if (timer_create(CLOCK_REALTIME, NULL, &t) == -1) 540 return 0; 541 542 if (timer_settime(t, 0, &tim, NULL) == -1) 543 goto out; 544 545 if (timer_gettime(t, &tim) == -1) 546 goto out; 547 548 if (tim.it_interval.tv_nsec < 2) 549 goto out; 550 551 rv = (int)(1000000000LL / tim.it_interval.tv_nsec); 552 out: 553 (void)timer_delete(t); 554 return rv; 555 } 556