gmon.c revision 1.34
1/* $NetBSD: gmon.c,v 1.34 2012/03/13 21:13:37 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 70static char sccsid[] = "@(#)gmon.c 8.1 (Berkeley) 6/4/93"; 71#else 72__RCSID("$NetBSD: gmon.c,v 1.34 2012/03/13 21:13:37 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 93struct gmonparam _gmonparam = { .state = GMON_PROF_OFF }; 94 95#ifdef _REENTRANT 96struct gmonparam *_gmonfree; 97struct gmonparam *_gmoninuse; 98mutex_t _gmonlock = MUTEX_INITIALIZER; 99thread_key_t _gmonkey; 100struct gmonparam _gmondummy; 101#endif 102 103static u_int s_scale; 104/* see profil(2) where this is describe (incorrectly) */ 105#define SCALE_1_TO_1 0x10000L 106 107void moncontrol(int); 108void monstartup(u_long, u_long); 109void _mcleanup(void); 110static int hertz(void); 111 112#ifdef _REENTRANT 113static void _m_gmon_destructor(void *); 114struct gmonparam *_m_gmon_alloc(void) 115 __attribute__((__no_instrument_function__)); 116static void _m_gmon_merge(void); 117static void _m_gmon_merge_two(struct gmonparam *, struct gmonparam *); 118#endif 119 120void 121monstartup(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 188static 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 214struct 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->kcount = NULL; 235 cp += sizeof (struct gmonparam); 236 memset(cp, 0, (size_t)(p->fromssize + p->tossize)); 237 p->froms = (u_short *)(void *)cp; 238 p->tos = (struct tostruct *)(void *)(cp + p->fromssize); 239 mutex_lock(&_gmonlock); 240 p->kcount = (u_short *)(void *)_gmoninuse; 241 _gmoninuse = p; 242 } 243 mutex_unlock(&_gmonlock); 244 thr_setspecific(_gmonkey, p); 245 246 return p; 247} 248 249static void 250_m_gmon_merge_two(struct gmonparam *p, struct gmonparam *q) 251{ 252 u_long fromindex; 253 u_short *frompcindex, qtoindex, toindex; 254 u_long selfpc; 255 u_long endfrom; 256 long count; 257 struct tostruct *top; 258 259 endfrom = (q->fromssize / sizeof(*q->froms)); 260 for (fromindex = 0; fromindex < endfrom; fromindex++) { 261 if (q->froms[fromindex] == 0) 262 continue; 263 for (qtoindex = q->froms[fromindex]; qtoindex != 0; 264 qtoindex = q->tos[qtoindex].link) { 265 selfpc = q->tos[qtoindex].selfpc; 266 count = q->tos[qtoindex].count; 267 /* cribbed from mcount */ 268 frompcindex = &p->froms[fromindex]; 269 toindex = *frompcindex; 270 if (toindex == 0) { 271 /* 272 * first time traversing this arc 273 */ 274 toindex = ++p->tos[0].link; 275 if (toindex >= p->tolimit) 276 /* halt further profiling */ 277 goto overflow; 278 279 *frompcindex = (u_short)toindex; 280 top = &p->tos[(size_t)toindex]; 281 top->selfpc = selfpc; 282 top->count = count; 283 top->link = 0; 284 goto done; 285 } 286 top = &p->tos[(size_t)toindex]; 287 if (top->selfpc == selfpc) { 288 /* 289 * arc at front of chain; usual case. 290 */ 291 top->count+= count; 292 goto done; 293 } 294 /* 295 * have to go looking down chain for it. 296 * top points to what we are looking at, 297 * we know it is not at the head of the chain. 298 */ 299 for (; /* goto done */; ) { 300 if (top->link == 0) { 301 /* 302 * top is end of the chain and 303 * none of the chain had 304 * top->selfpc == selfpc. so 305 * we allocate a new tostruct 306 * and link it to the head of 307 * the chain. 308 */ 309 toindex = ++p->tos[0].link; 310 if (toindex >= p->tolimit) 311 goto overflow; 312 313 top = &p->tos[(size_t)toindex]; 314 top->selfpc = selfpc; 315 top->count = count; 316 top->link = *frompcindex; 317 *frompcindex = (u_short)toindex; 318 goto done; 319 } 320 /* 321 * otherwise, check the next arc on the chain. 322 */ 323 top = &p->tos[top->link]; 324 if (top->selfpc == selfpc) { 325 /* 326 * there it is. 327 * add to its count. 328 */ 329 top->count += count; 330 goto done; 331 } 332 333 } 334 335 done: ; 336 } 337 338 } 339 overflow: ; 340 341} 342 343static void 344_m_gmon_merge(void) 345{ 346 struct gmonparam *q; 347 348 mutex_lock(&_gmonlock); 349 350 for (q = _gmonfree; q != NULL; 351 q = (struct gmonparam *)(void *)q->kcount) 352 _m_gmon_merge_two(&_gmonparam, q); 353 354 for (q = _gmoninuse; q != NULL; 355 q = (struct gmonparam *)(void *)q->kcount) { 356 q->state = GMON_PROF_OFF; 357 _m_gmon_merge_two(&_gmonparam, q); 358 } 359 360 mutex_unlock(&_gmonlock); 361} 362#endif 363 364void 365_mcleanup(void) 366{ 367 int fd; 368 int fromindex; 369 int endfrom; 370 u_long frompc; 371 int toindex; 372 struct rawarc rawarc; 373 struct gmonparam *p = &_gmonparam; 374 struct gmonhdr gmonhdr, *hdr; 375 struct clockinfo clockinfo; 376 int mib[2]; 377 size_t size; 378 char *profdir; 379 const char *proffile; 380 char buf[PATH_MAX]; 381#ifdef DEBUG 382 int logfd, len; 383 char buf2[200]; 384#endif 385 386 /* 387 * We disallow writing to the profiling file, if we are a 388 * set{u,g}id program and our effective {u,g}id does not match 389 * our real one. 390 */ 391 if (issetugid() && (geteuid() != getuid() || getegid() != getgid())) { 392 warnx("%s: Profiling of set{u,g}id binaries is not" 393 " allowed", __func__); 394 return; 395 } 396 397 if (p->state == GMON_PROF_ERROR) 398 warnx("%s: tos overflow", __func__); 399 400 size = sizeof(clockinfo); 401 mib[0] = CTL_KERN; 402 mib[1] = KERN_CLOCKRATE; 403 if (sysctl(mib, 2, &clockinfo, &size, NULL, 0) < 0) { 404 /* 405 * Best guess 406 */ 407 clockinfo.profhz = hertz(); 408 } else if (clockinfo.profhz == 0) { 409 if (clockinfo.hz != 0) 410 clockinfo.profhz = clockinfo.hz; 411 else 412 clockinfo.profhz = hertz(); 413 } 414 415 moncontrol(0); 416 417 if ((profdir = getenv("PROFDIR")) != NULL) { 418 /* If PROFDIR contains a null value, no profiling 419 output is produced */ 420 if (*profdir == '\0') 421 return; 422 423 if (snprintf(buf, sizeof buf, "%s/%d.%s", 424 profdir, getpid(), getprogname()) >= (int)(sizeof buf)) { 425 warnx("%s: internal buffer overflow, PROFDIR too long", 426 __func__); 427 return; 428 } 429 430 proffile = buf; 431 } else { 432 proffile = "gmon.out"; 433 } 434 435 fd = open(proffile , O_CREAT|O_TRUNC|O_WRONLY, 0666); 436 if (fd < 0) { 437 warn("%s: Cannot open `%s'", __func__, proffile); 438 return; 439 } 440#ifdef DEBUG 441 logfd = open("gmon.log", O_CREAT|O_TRUNC|O_WRONLY, 0664); 442 if (logfd < 0) { 443 warn("%s: Cannot open `%s'", __func__, "gmon.log"); 444 (void)close(fd); 445 return; 446 } 447 len = snprintf(buf2, sizeof buf2, "[mcleanup1] kcount %p ssiz %lu\n", 448 p->kcount, p->kcountsize); 449 (void)write(logfd, buf2, (size_t)len); 450#endif 451#ifdef _REENTRANT 452 _m_gmon_merge(); 453#endif 454 hdr = (struct gmonhdr *)&gmonhdr; 455 hdr->lpc = p->lowpc; 456 hdr->hpc = p->highpc; 457 hdr->ncnt = (int)(p->kcountsize + sizeof(gmonhdr)); 458 hdr->version = GMONVERSION; 459 hdr->profrate = clockinfo.profhz; 460 (void)write(fd, hdr, sizeof *hdr); 461 (void)write(fd, p->kcount, (size_t)p->kcountsize); 462 endfrom = (int)(p->fromssize / sizeof(*p->froms)); 463 for (fromindex = 0; fromindex < endfrom; fromindex++) { 464 if (p->froms[fromindex] == 0) 465 continue; 466 467 frompc = p->lowpc; 468 frompc += fromindex * p->hashfraction * sizeof(*p->froms); 469 for (toindex = p->froms[fromindex]; toindex != 0; 470 toindex = p->tos[toindex].link) { 471#ifdef DEBUG 472 len = snprintf(buf2, sizeof buf2, 473 "[mcleanup2] frompc 0x%lx selfpc 0x%lx count %lu\n" , 474 (u_long)frompc, (u_long)p->tos[toindex].selfpc, 475 (u_long)p->tos[toindex].count); 476 (void)write(logfd, buf2, (size_t)len); 477#endif 478 rawarc.raw_frompc = frompc; 479 rawarc.raw_selfpc = p->tos[toindex].selfpc; 480 rawarc.raw_count = p->tos[toindex].count; 481 (void)write(fd, &rawarc, sizeof rawarc); 482 } 483 } 484 (void)close(fd); 485#ifdef DEBUG 486 (void)close(logfd); 487#endif 488} 489 490/* 491 * Control profiling 492 * profiling is what mcount checks to see if 493 * all the data structures are ready. 494 */ 495void 496moncontrol(int mode) 497{ 498 struct gmonparam *p = &_gmonparam; 499 500 if (mode) { 501 /* start */ 502 profil((char *)(void *)p->kcount, (size_t)p->kcountsize, 503 p->lowpc, s_scale); 504 p->state = GMON_PROF_ON; 505 } else { 506 /* stop */ 507 profil(NULL, 0, (u_long)0, 0); 508 p->state = GMON_PROF_OFF; 509 } 510} 511 512/* 513 * discover the tick frequency of the machine 514 * if something goes wrong, we return 0, an impossible hertz. 515 */ 516static int 517hertz(void) 518{ 519 struct itimerspec tim; 520 timer_t t; 521 int rv = 0; 522 523 tim.it_interval.tv_sec = 0; 524 tim.it_interval.tv_nsec = 1; 525 tim.it_value.tv_sec = 0; 526 tim.it_value.tv_nsec = 0; 527 528 if (timer_create(CLOCK_REALTIME, NULL, &t) == -1) 529 return 0; 530 531 if (timer_settime(t, 0, &tim, NULL) == -1) 532 goto out; 533 534 if (timer_gettime(t, &tim) == -1) 535 goto out; 536 537 if (tim.it_interval.tv_nsec < 2) 538 goto out; 539 540 rv = (int)(1000000000LL / tim.it_interval.tv_nsec); 541out: 542 (void)timer_delete(t); 543 return rv; 544} 545