1 /* $NetBSD: gmon.c,v 1.36 2021/07/03 14:08:55 christos 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.36 2021/07/03 14:08:55 christos 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)(p->kcountsize + p->fromssize + p->tossize)); 145 if (cp == (char *)-1) { 146 warnx("%s: out of memory", __func__); 147 return; 148 } 149 #ifdef notdef 150 (void)memset(cp, 0, p->kcountsize + p->fromssize + p->tossize); 151 #endif 152 p->tos = (struct tostruct *)(void *)cp; 153 cp += (size_t)p->tossize; 154 p->kcount = (u_short *)(void *)cp; 155 cp += (size_t)p->kcountsize; 156 p->froms = (u_short *)(void *)cp; 157 158 __minbrk = sbrk((intptr_t)0); 159 p->tos[0].link = 0; 160 161 o = p->highpc - p->lowpc; 162 if (p->kcountsize < o) { 163 #ifndef notdef 164 s_scale = ((float)p->kcountsize / o ) * SCALE_1_TO_1; 165 #else /* avoid floating point */ 166 u_long quot = o / p->kcountsize; 167 168 if (quot >= 0x10000) 169 s_scale = 1; 170 else if (quot >= 0x100) 171 s_scale = 0x10000 / quot; 172 else if (o >= 0x800000) 173 s_scale = 0x1000000 / (o / (p->kcountsize >> 8)); 174 else 175 s_scale = 0x1000000 / ((o << 8) / p->kcountsize); 176 #endif 177 } else 178 s_scale = SCALE_1_TO_1; 179 180 #ifdef _REENTRANT 181 _gmondummy.state = GMON_PROF_BUSY; 182 thr_keycreate(&_gmonkey, _m_gmon_destructor); 183 #endif 184 moncontrol(1); 185 } 186 187 #ifdef _REENTRANT 188 static void 189 _m_gmon_destructor(void *arg) 190 { 191 struct gmonparam *p = arg, *q, **prev; 192 193 if (p == &_gmondummy) 194 return; 195 196 thr_setspecific(_gmonkey, &_gmondummy); 197 198 mutex_lock(&_gmonlock); 199 /* XXX eww, linear list traversal. */ 200 for (q = _gmoninuse, prev = &_gmoninuse; 201 q != NULL; 202 prev = (struct gmonparam **)(void *)&q->kcount, /* XXX */ 203 q = (struct gmonparam *)(void *)q->kcount) { 204 if (q == p) 205 *prev = (struct gmonparam *)(void *)q->kcount; 206 } 207 p->kcount = (u_short *)(void *)_gmonfree; 208 _gmonfree = p; 209 mutex_unlock(&_gmonlock); 210 211 thr_setspecific(_gmonkey, NULL); 212 } 213 214 struct gmonparam * 215 _m_gmon_alloc(void) 216 { 217 struct gmonparam *p; 218 char *cp; 219 220 mutex_lock(&_gmonlock); 221 if (_gmonfree != NULL) { 222 p = _gmonfree; 223 _gmonfree = (struct gmonparam *)(void *)p->kcount; 224 p->kcount = (u_short *)(void *)_gmoninuse; 225 _gmoninuse = p; 226 } else { 227 mutex_unlock(&_gmonlock); 228 cp = mmap(NULL, 229 (size_t)(sizeof (struct gmonparam) + 230 _gmonparam.fromssize + _gmonparam.tossize), 231 PROT_READ|PROT_WRITE, MAP_ANON|MAP_PRIVATE, -1, (off_t)0); 232 p = (void *)cp; 233 *p = _gmonparam; 234 p->state = GMON_PROF_ON; 235 p->kcount = NULL; 236 cp += sizeof (struct gmonparam); 237 memset(cp, 0, (size_t)(p->fromssize + p->tossize)); 238 p->froms = (u_short *)(void *)cp; 239 p->tos = (struct tostruct *)(void *)(cp + p->fromssize); 240 mutex_lock(&_gmonlock); 241 p->kcount = (u_short *)(void *)_gmoninuse; 242 _gmoninuse = p; 243 } 244 mutex_unlock(&_gmonlock); 245 thr_setspecific(_gmonkey, p); 246 247 return p; 248 } 249 250 static void 251 _m_gmon_merge_two(struct gmonparam *p, struct gmonparam *q) 252 { 253 u_long fromindex; 254 u_short *frompcindex, qtoindex, toindex; 255 u_long selfpc; 256 u_long endfrom; 257 long count; 258 struct tostruct *top; 259 260 endfrom = (q->fromssize / sizeof(*q->froms)); 261 for (fromindex = 0; fromindex < endfrom; fromindex++) { 262 if (q->froms[fromindex] == 0) 263 continue; 264 for (qtoindex = q->froms[fromindex]; qtoindex != 0; 265 qtoindex = q->tos[qtoindex].link) { 266 selfpc = q->tos[qtoindex].selfpc; 267 count = q->tos[qtoindex].count; 268 /* cribbed from mcount */ 269 frompcindex = &p->froms[fromindex]; 270 toindex = *frompcindex; 271 if (toindex == 0) { 272 /* 273 * first time traversing this arc 274 */ 275 toindex = ++p->tos[0].link; 276 if (toindex >= p->tolimit) 277 /* halt further profiling */ 278 goto overflow; 279 280 *frompcindex = (u_short)toindex; 281 top = &p->tos[(size_t)toindex]; 282 top->selfpc = selfpc; 283 top->count = count; 284 top->link = 0; 285 goto done; 286 } 287 top = &p->tos[(size_t)toindex]; 288 if (top->selfpc == selfpc) { 289 /* 290 * arc at front of chain; usual case. 291 */ 292 top->count+= count; 293 goto done; 294 } 295 /* 296 * have to go looking down chain for it. 297 * top points to what we are looking at, 298 * we know it is not at the head of the chain. 299 */ 300 for (; /* goto done */; ) { 301 if (top->link == 0) { 302 /* 303 * top is end of the chain and 304 * none of the chain had 305 * top->selfpc == selfpc. so 306 * we allocate a new tostruct 307 * and link it to the head of 308 * the chain. 309 */ 310 toindex = ++p->tos[0].link; 311 if (toindex >= p->tolimit) 312 goto overflow; 313 314 top = &p->tos[(size_t)toindex]; 315 top->selfpc = selfpc; 316 top->count = count; 317 top->link = *frompcindex; 318 *frompcindex = (u_short)toindex; 319 goto done; 320 } 321 /* 322 * otherwise, check the next arc on the chain. 323 */ 324 top = &p->tos[top->link]; 325 if (top->selfpc == selfpc) { 326 /* 327 * there it is. 328 * add to its count. 329 */ 330 top->count += count; 331 goto done; 332 } 333 334 } 335 336 done: ; 337 } 338 339 } 340 overflow: ; 341 342 } 343 344 static void 345 _m_gmon_merge(void) 346 { 347 struct gmonparam *q; 348 349 mutex_lock(&_gmonlock); 350 351 for (q = _gmonfree; q != NULL; 352 q = (struct gmonparam *)(void *)q->kcount) 353 _m_gmon_merge_two(&_gmonparam, q); 354 355 for (q = _gmoninuse; q != NULL; 356 q = (struct gmonparam *)(void *)q->kcount) { 357 q->state = GMON_PROF_OFF; 358 _m_gmon_merge_two(&_gmonparam, q); 359 } 360 361 mutex_unlock(&_gmonlock); 362 } 363 #endif 364 365 void 366 _mcleanup(void) 367 { 368 int fd; 369 int fromindex; 370 int endfrom; 371 u_long frompc; 372 int toindex; 373 struct rawarc rawarc; 374 struct gmonparam *p = &_gmonparam; 375 struct gmonhdr gmonhdr, *hdr; 376 struct clockinfo clockinfo; 377 int mib[2]; 378 size_t size; 379 char *profdir; 380 const char *proffile; 381 char buf[PATH_MAX]; 382 #ifdef DEBUG 383 int logfd, len; 384 char buf2[200]; 385 #endif 386 387 /* 388 * We disallow writing to the profiling file, if we are a 389 * set{u,g}id program and our effective {u,g}id does not match 390 * our real one. 391 */ 392 if (issetugid() && (geteuid() != getuid() || getegid() != getgid())) { 393 warnx("%s: Profiling of set{u,g}id binaries is not" 394 " allowed", __func__); 395 return; 396 } 397 398 if (p->state == GMON_PROF_ERROR) 399 warnx("%s: tos overflow", __func__); 400 401 size = sizeof(clockinfo); 402 mib[0] = CTL_KERN; 403 mib[1] = KERN_CLOCKRATE; 404 if (sysctl(mib, 2, &clockinfo, &size, NULL, 0) < 0) { 405 /* 406 * Best guess 407 */ 408 clockinfo.profhz = hertz(); 409 } else if (clockinfo.profhz == 0) { 410 if (clockinfo.hz != 0) 411 clockinfo.profhz = clockinfo.hz; 412 else 413 clockinfo.profhz = hertz(); 414 } 415 416 moncontrol(0); 417 418 if ((profdir = getenv("PROFDIR")) != NULL) { 419 /* If PROFDIR contains a null value, no profiling 420 output is produced */ 421 if (*profdir == '\0') 422 return; 423 424 if (snprintf(buf, sizeof buf, "%s/%d.%s", 425 profdir, getpid(), getprogname()) >= (int)(sizeof buf)) { 426 warnx("%s: internal buffer overflow, PROFDIR too long", 427 __func__); 428 return; 429 } 430 431 proffile = buf; 432 } else { 433 proffile = "gmon.out"; 434 } 435 436 #define OPEN_FLAGS (O_CREAT | O_TRUNC | O_WRONLY | O_CLOEXEC) 437 fd = open(proffile, OPEN_FLAGS, 0666); 438 if (fd < 0) { 439 warn("%s: Cannot open `%s'", __func__, proffile); 440 return; 441 } 442 #ifdef DEBUG 443 logfd = open("gmon.log", OPEN_FLAGS, 0664); 444 if (logfd < 0) { 445 warn("%s: Cannot open `%s'", __func__, "gmon.log"); 446 (void)close(fd); 447 return; 448 } 449 len = snprintf(buf2, sizeof buf2, "[mcleanup1] kcount %p ssiz %lu\n", 450 p->kcount, p->kcountsize); 451 (void)write(logfd, buf2, (size_t)len); 452 #endif 453 #ifdef _REENTRANT 454 _m_gmon_merge(); 455 #endif 456 hdr = (struct gmonhdr *)&gmonhdr; 457 hdr->lpc = p->lowpc; 458 hdr->hpc = p->highpc; 459 hdr->ncnt = (int)(p->kcountsize + sizeof(gmonhdr)); 460 hdr->version = GMONVERSION; 461 hdr->profrate = clockinfo.profhz; 462 (void)write(fd, hdr, sizeof *hdr); 463 (void)write(fd, p->kcount, (size_t)p->kcountsize); 464 endfrom = (int)(p->fromssize / sizeof(*p->froms)); 465 for (fromindex = 0; fromindex < endfrom; fromindex++) { 466 if (p->froms[fromindex] == 0) 467 continue; 468 469 frompc = p->lowpc; 470 frompc += fromindex * p->hashfraction * sizeof(*p->froms); 471 for (toindex = p->froms[fromindex]; toindex != 0; 472 toindex = p->tos[toindex].link) { 473 #ifdef DEBUG 474 len = snprintf(buf2, sizeof buf2, 475 "[mcleanup2] frompc 0x%lx selfpc 0x%lx count %lu\n" , 476 (u_long)frompc, (u_long)p->tos[toindex].selfpc, 477 (u_long)p->tos[toindex].count); 478 (void)write(logfd, buf2, (size_t)len); 479 #endif 480 rawarc.raw_frompc = frompc; 481 rawarc.raw_selfpc = p->tos[toindex].selfpc; 482 rawarc.raw_count = p->tos[toindex].count; 483 (void)write(fd, &rawarc, sizeof rawarc); 484 } 485 } 486 (void)close(fd); 487 #ifdef DEBUG 488 (void)close(logfd); 489 #endif 490 } 491 492 /* 493 * Control profiling 494 * profiling is what mcount checks to see if 495 * all the data structures are ready. 496 */ 497 void 498 moncontrol(int mode) 499 { 500 struct gmonparam *p = &_gmonparam; 501 502 if (mode) { 503 /* start */ 504 profil((char *)(void *)p->kcount, (size_t)p->kcountsize, 505 p->lowpc, s_scale); 506 p->state = GMON_PROF_ON; 507 } else { 508 /* stop */ 509 profil(NULL, 0, (u_long)0, 0); 510 p->state = GMON_PROF_OFF; 511 } 512 } 513 514 /* 515 * discover the tick frequency of the machine 516 * if something goes wrong, we return 0, an impossible hertz. 517 */ 518 static int 519 hertz(void) 520 { 521 struct itimerspec tim; 522 timer_t t; 523 int rv = 0; 524 525 tim.it_interval.tv_sec = 0; 526 tim.it_interval.tv_nsec = 1; 527 tim.it_value.tv_sec = 0; 528 tim.it_value.tv_nsec = 0; 529 530 if (timer_create(CLOCK_REALTIME, NULL, &t) == -1) 531 return 0; 532 533 if (timer_settime(t, 0, &tim, NULL) == -1) 534 goto out; 535 536 if (timer_gettime(t, &tim) == -1) 537 goto out; 538 539 if (tim.it_interval.tv_nsec < 2) 540 goto out; 541 542 rv = (int)(1000000000LL / tim.it_interval.tv_nsec); 543 out: 544 (void)timer_delete(t); 545 return rv; 546 } 547