gmon.c revision 1.27
1/* $NetBSD: gmon.c,v 1.27 2006/10/04 20:22:14 dogcow 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.27 2006/10/04 20:22:14 dogcow 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/param.h> 82#include <sys/sysctl.h> 83 84#include <stdio.h> 85#include <stdlib.h> 86#include <string.h> 87#include <fcntl.h> 88#include <limits.h> 89#include <unistd.h> 90#include <err.h> 91#include "extern.h" 92#include "reentrant.h" 93 94struct gmonparam _gmonparam = { GMON_PROF_OFF }; 95 96#ifdef _REENTRANT 97struct gmonparam *_gmonfree; 98struct gmonparam *_gmoninuse; 99mutex_t _gmonlock = MUTEX_INITIALIZER; 100thread_key_t _gmonkey; 101struct gmonparam _gmondummy; 102#endif 103 104static u_int s_scale; 105/* see profil(2) where this is describe (incorrectly) */ 106#define SCALE_1_TO_1 0x10000L 107 108#define ERR(s) write(STDERR_FILENO, s, sizeof(s)) 109 110void moncontrol __P((int)); 111void monstartup __P((u_long, u_long)); 112void _mcleanup __P((void)); 113static int hertz __P((void)); 114 115#ifdef _REENTRANT 116static void _m_gmon_destructor(void *); 117struct gmonparam *_m_gmon_alloc(void) __attribute__((__no_instrument_function__)); 118static void _m_gmon_merge(void); 119static void _m_gmon_merge_two(struct gmonparam *, struct gmonparam *); 120#endif 121 122void 123monstartup(lowpc, highpc) 124 u_long lowpc; 125 u_long highpc; 126{ 127 u_long o; 128 char *cp; 129 struct gmonparam *p = &_gmonparam; 130 131 /* 132 * round lowpc and highpc to multiples of the density we're using 133 * so the rest of the scaling (here and in gprof) stays in ints. 134 */ 135 p->lowpc = rounddown(lowpc, HISTFRACTION * sizeof(HISTCOUNTER)); 136 p->highpc = roundup(highpc, HISTFRACTION * sizeof(HISTCOUNTER)); 137 p->textsize = p->highpc - p->lowpc; 138 p->kcountsize = p->textsize / HISTFRACTION; 139 p->hashfraction = HASHFRACTION; 140 p->fromssize = p->textsize / p->hashfraction; 141 p->tolimit = p->textsize * ARCDENSITY / 100; 142 if (p->tolimit < MINARCS) 143 p->tolimit = MINARCS; 144 else if (p->tolimit > MAXARCS) 145 p->tolimit = MAXARCS; 146 p->tossize = p->tolimit * sizeof(struct tostruct); 147 148 cp = sbrk((intptr_t)(p->kcountsize + p->fromssize + p->tossize)); 149 if (cp == (char *)-1) { 150 ERR("monstartup: out of memory\n"); 151 return; 152 } 153#ifdef notdef 154 memset(cp, 0, p->kcountsize + p->fromssize + p->tossize); 155#endif 156 p->tos = (struct tostruct *)(void *)cp; 157 cp += (size_t)p->tossize; 158 p->kcount = (u_short *)(void *)cp; 159 cp += (size_t)p->kcountsize; 160 p->froms = (u_short *)(void *)cp; 161 162 __minbrk = sbrk((intptr_t)0); 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 192static 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 218struct 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)(sizeof (struct gmonparam) + 234 _gmonparam.fromssize + _gmonparam.tossize), 235 PROT_READ|PROT_WRITE, MAP_ANON|MAP_PRIVATE, -1, 0LL); 236 p = (void *)cp; 237 *p = _gmonparam; 238 p->kcount = NULL; 239 cp += sizeof (struct gmonparam); 240 memset(cp, 0, (size_t)(p->fromssize + p->tossize)); 241 p->froms = (u_short *)(void *)cp; 242 p->tos = (struct tostruct *)(void *)(cp + p->fromssize); 243 mutex_lock(&_gmonlock); 244 p->kcount = (u_short *)(void *)_gmoninuse; 245 _gmoninuse = p; 246 } 247 mutex_unlock(&_gmonlock); 248 thr_setspecific(_gmonkey, p); 249 250 return p; 251} 252 253static void 254_m_gmon_merge_two(struct gmonparam *p, struct gmonparam *q) 255{ 256 u_long fromindex; 257 u_short *frompcindex, qtoindex, toindex; 258 u_long selfpc; 259 int endfrom; 260 long count; 261 struct tostruct *top; 262 263 endfrom = (int)(q->fromssize / sizeof(*q->froms)); 264 for (fromindex = 0; fromindex < endfrom; fromindex++) { 265 if (q->froms[fromindex] == 0) 266 continue; 267 for (qtoindex = q->froms[fromindex]; qtoindex != 0; 268 qtoindex = q->tos[qtoindex].link) { 269 selfpc = q->tos[qtoindex].selfpc; 270 count = q->tos[qtoindex].count; 271 /* cribbed from mcount */ 272 frompcindex = &p->froms[fromindex]; 273 toindex = *frompcindex; 274 if (toindex == 0) { 275 /* 276 * first time traversing this arc 277 */ 278 toindex = ++p->tos[0].link; 279 if (toindex >= p->tolimit) 280 /* halt further profiling */ 281 goto overflow; 282 283 *frompcindex = (u_short)toindex; 284 top = &p->tos[(size_t)toindex]; 285 top->selfpc = selfpc; 286 top->count = count; 287 top->link = 0; 288 goto done; 289 } 290 top = &p->tos[(size_t)toindex]; 291 if (top->selfpc == selfpc) { 292 /* 293 * arc at front of chain; usual case. 294 */ 295 top->count+= count; 296 goto done; 297 } 298 /* 299 * have to go looking down chain for it. 300 * top points to what we are looking at, 301 * we know it is not at the head of the chain. 302 */ 303 for (; /* goto done */; ) { 304 if (top->link == 0) { 305 /* 306 * top is end of the chain and 307 * none of the chain had 308 * top->selfpc == selfpc. so 309 * we allocate a new tostruct 310 * and link it to the head of 311 * the chain. 312 */ 313 toindex = ++p->tos[0].link; 314 if (toindex >= p->tolimit) 315 goto overflow; 316 317 top = &p->tos[(size_t)toindex]; 318 top->selfpc = selfpc; 319 top->count = count; 320 top->link = *frompcindex; 321 *frompcindex = (u_short)toindex; 322 goto done; 323 } 324 /* 325 * otherwise, check the next arc on the chain. 326 */ 327 top = &p->tos[top->link]; 328 if (top->selfpc == selfpc) { 329 /* 330 * there it is. 331 * add to its count. 332 */ 333 top->count += count; 334 goto done; 335 } 336 337 } 338 339 done: ; 340 } 341 342 } 343 overflow: ; 344 345} 346 347static void 348_m_gmon_merge(void) 349{ 350 struct gmonparam *q; 351 352 mutex_lock(&_gmonlock); 353 354 for (q = _gmonfree; q != NULL; q = (struct gmonparam *)(void *)q->kcount) 355 _m_gmon_merge_two(&_gmonparam, q); 356 357 for (q = _gmoninuse; q != NULL; q = (struct gmonparam *)(void *)q->kcount) { 358 q->state = GMON_PROF_OFF; 359 _m_gmon_merge_two(&_gmonparam, q); 360 } 361 362 mutex_unlock(&_gmonlock); 363} 364#endif 365 366void 367_mcleanup() 368{ 369 int fd; 370 int fromindex; 371 int endfrom; 372 u_long frompc; 373 int toindex; 374 struct rawarc rawarc; 375 struct gmonparam *p = &_gmonparam; 376 struct gmonhdr gmonhdr, *hdr; 377 struct clockinfo clockinfo; 378 int mib[2]; 379 size_t size; 380 char *profdir; 381 const char *proffile; 382 char buf[PATH_MAX]; 383#ifdef DEBUG 384 int logfd, len; 385 char buf2[200]; 386#endif 387 388 /* 389 * We disallow writing to the profiling file, if we are a 390 * set{u,g}id program and our effective {u,g}id does not match 391 * our real one. 392 */ 393 if (issetugid() && (geteuid() != getuid() || getegid() != getgid())) { 394 warnx("mcount: Profiling of set{u,g}id binaries is not" 395 " allowed"); 396 return; 397 } 398 399 if (p->state == GMON_PROF_ERROR) 400 ERR("_mcleanup: tos overflow\n"); 401 402 size = sizeof(clockinfo); 403 mib[0] = CTL_KERN; 404 mib[1] = KERN_CLOCKRATE; 405 if (sysctl(mib, 2, &clockinfo, &size, NULL, 0) < 0) { 406 /* 407 * Best guess 408 */ 409 clockinfo.profhz = hertz(); 410 } else if (clockinfo.profhz == 0) { 411 if (clockinfo.hz != 0) 412 clockinfo.profhz = clockinfo.hz; 413 else 414 clockinfo.profhz = hertz(); 415 } 416 417 moncontrol(0); 418 419 if ((profdir = getenv("PROFDIR")) != NULL) { 420 /* If PROFDIR contains a null value, no profiling 421 output is produced */ 422 if (*profdir == '\0') 423 return; 424 425 if (snprintf(buf, sizeof buf, "%s/%d.%s", 426 profdir, getpid(), getprogname()) >= sizeof buf) { 427 warnx("_mcleanup: internal buffer overflow, PROFDIR too long"); 428 return; 429 } 430 431 proffile = buf; 432 } else { 433 proffile = "gmon.out"; 434 } 435 436 fd = open(proffile , O_CREAT|O_TRUNC|O_WRONLY, 0666); 437 if (fd < 0) { 438 warn("mcount: Cannot open `%s'", proffile); 439 return; 440 } 441#ifdef DEBUG 442 logfd = open("gmon.log", O_CREAT|O_TRUNC|O_WRONLY, 0664); 443 if (logfd < 0) { 444 warn("mcount: Cannot open `gmon.log'"); 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 write(fd, &rawarc, sizeof rawarc); 482 } 483 } 484 close(fd); 485} 486 487/* 488 * Control profiling 489 * profiling is what mcount checks to see if 490 * all the data structures are ready. 491 */ 492void 493moncontrol(mode) 494 int mode; 495{ 496 struct gmonparam *p = &_gmonparam; 497 498 if (mode) { 499 /* start */ 500 profil((char *)(void *)p->kcount, (size_t)p->kcountsize, 501 p->lowpc, s_scale); 502 p->state = GMON_PROF_ON; 503 } else { 504 /* stop */ 505 profil(NULL, 0, (u_long)0, 0); 506 p->state = GMON_PROF_OFF; 507 } 508} 509 510/* 511 * discover the tick frequency of the machine 512 * if something goes wrong, we return 0, an impossible hertz. 513 */ 514static int 515hertz() 516{ 517 struct itimerval tim; 518 519 tim.it_interval.tv_sec = 0; 520 tim.it_interval.tv_usec = 1; 521 tim.it_value.tv_sec = 0; 522 tim.it_value.tv_usec = 0; 523 setitimer(ITIMER_REAL, &tim, 0); 524 setitimer(ITIMER_REAL, 0, &tim); 525 if (tim.it_interval.tv_usec < 2) 526 return(0); 527 return (int)(1000000 / tim.it_interval.tv_usec); 528} 529