gmon.c revision 1.24
1/* $NetBSD: gmon.c,v 1.24 2006/01/24 17:30:51 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.24 2006/01/24 17:30:51 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 = { 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 107#define ERR(s) write(STDERR_FILENO, s, sizeof(s)) 108 109void moncontrol __P((int)); 110void monstartup __P((u_long, u_long)); 111void _mcleanup __P((void)); 112static int hertz __P((void)); 113 114#ifdef _REENTRANT 115static void _m_gmon_destructor(void *); 116struct gmonparam *_m_gmon_alloc(void) __attribute__((__no_instrument_function__)); 117static void _m_gmon_merge(void); 118static void _m_gmon_merge_two(struct gmonparam *, struct gmonparam *); 119#endif 120 121void 122monstartup(lowpc, highpc) 123 u_long lowpc; 124 u_long highpc; 125{ 126 u_long o; 127 char *cp; 128 struct gmonparam *p = &_gmonparam; 129 130 /* 131 * round lowpc and highpc to multiples of the density we're using 132 * so the rest of the scaling (here and in gprof) stays in ints. 133 */ 134 p->lowpc = ROUNDDOWN(lowpc, HISTFRACTION * sizeof(HISTCOUNTER)); 135 p->highpc = ROUNDUP(highpc, HISTFRACTION * sizeof(HISTCOUNTER)); 136 p->textsize = p->highpc - p->lowpc; 137 p->kcountsize = p->textsize / HISTFRACTION; 138 p->hashfraction = HASHFRACTION; 139 p->fromssize = p->textsize / p->hashfraction; 140 p->tolimit = p->textsize * ARCDENSITY / 100; 141 if (p->tolimit < MINARCS) 142 p->tolimit = MINARCS; 143 else if (p->tolimit > MAXARCS) 144 p->tolimit = MAXARCS; 145 p->tossize = p->tolimit * sizeof(struct tostruct); 146 147 cp = sbrk((intptr_t)(p->kcountsize + p->fromssize + p->tossize)); 148 if (cp == (char *)-1) { 149 ERR("monstartup: out of memory\n"); 150 return; 151 } 152#ifdef notdef 153 memset(cp, 0, p->kcountsize + p->fromssize + p->tossize); 154#endif 155 p->tos = (struct tostruct *)(void *)cp; 156 cp += (size_t)p->tossize; 157 p->kcount = (u_short *)(void *)cp; 158 cp += (size_t)p->kcountsize; 159 p->froms = (u_short *)(void *)cp; 160 161 __minbrk = sbrk((intptr_t)0); 162 p->tos[0].link = 0; 163 164 o = p->highpc - p->lowpc; 165 if (p->kcountsize < o) { 166#ifndef notdef 167 s_scale = ((float)p->kcountsize / o ) * SCALE_1_TO_1; 168#else /* avoid floating point */ 169 u_long quot = o / p->kcountsize; 170 171 if (quot >= 0x10000) 172 s_scale = 1; 173 else if (quot >= 0x100) 174 s_scale = 0x10000 / quot; 175 else if (o >= 0x800000) 176 s_scale = 0x1000000 / (o / (p->kcountsize >> 8)); 177 else 178 s_scale = 0x1000000 / ((o << 8) / p->kcountsize); 179#endif 180 } else 181 s_scale = SCALE_1_TO_1; 182 183#ifdef _REENTRANT 184 _gmondummy.state = GMON_PROF_BUSY; 185 thr_keycreate(&_gmonkey, _m_gmon_destructor); 186#endif 187 moncontrol(1); 188} 189 190#ifdef _REENTRANT 191static void 192_m_gmon_destructor(void *arg) 193{ 194 struct gmonparam *p = arg, *q, **prev; 195 196 if (p == &_gmondummy) 197 return; 198 199 thr_setspecific(_gmonkey, &_gmondummy); 200 201 mutex_lock(&_gmonlock); 202 /* XXX eww, linear list traversal. */ 203 for (q = _gmoninuse, prev = &_gmoninuse; 204 q != NULL; 205 prev = (struct gmonparam **)&q->kcount, 206 q = (struct gmonparam *)(void *)q->kcount) { 207 if (q == p) 208 *prev = (struct gmonparam *)(void *)q->kcount; 209 } 210 p->kcount = (u_short *)(void *)_gmonfree; 211 _gmonfree = p; 212 mutex_unlock(&_gmonlock); 213 214 thr_setspecific(_gmonkey, NULL); 215} 216 217struct gmonparam * 218_m_gmon_alloc(void) 219{ 220 struct gmonparam *p; 221 char *cp; 222 223 mutex_lock(&_gmonlock); 224 if (_gmonfree != NULL) { 225 p = _gmonfree; 226 _gmonfree = (struct gmonparam *)(void *)p->kcount; 227 p->kcount = (u_short *)(void *)_gmoninuse; 228 _gmoninuse = p; 229 } else { 230 mutex_unlock(&_gmonlock); 231 cp = mmap(NULL, 232 (size_t)(sizeof (struct gmonparam) + 233 _gmonparam.fromssize + _gmonparam.tossize), 234 PROT_READ|PROT_WRITE, MAP_ANON|MAP_PRIVATE, -1, 0LL); 235 p = (void *)cp; 236 *p = _gmonparam; 237 p->kcount = NULL; 238 cp += sizeof (struct gmonparam); 239 memset(cp, 0, (size_t)(p->fromssize + p->tossize)); 240 p->froms = (u_short *)(void *)cp; 241 p->tos = (struct tostruct *)(void *)(cp + p->fromssize); 242 mutex_lock(&_gmonlock); 243 p->kcount = (u_short *)(void *)_gmoninuse; 244 _gmoninuse = p; 245 } 246 mutex_unlock(&_gmonlock); 247 thr_setspecific(_gmonkey, p); 248 249 return p; 250} 251 252static void 253_m_gmon_merge_two(struct gmonparam *p, struct gmonparam *q) 254{ 255 u_long fromindex; 256 u_short *frompcindex, qtoindex, toindex; 257 u_long selfpc; 258 int endfrom; 259 long count; 260 struct tostruct *top; 261 262 endfrom = (int)(q->fromssize / sizeof(*q->froms)); 263 for (fromindex = 0; fromindex < endfrom; fromindex++) { 264 if (q->froms[fromindex] == 0) 265 continue; 266 for (qtoindex = q->froms[fromindex]; qtoindex != 0; 267 qtoindex = q->tos[qtoindex].link) { 268 selfpc = q->tos[qtoindex].selfpc; 269 count = q->tos[qtoindex].count; 270 /* cribbed from mcount */ 271 frompcindex = &p->froms[fromindex]; 272 toindex = *frompcindex; 273 if (toindex == 0) { 274 /* 275 * first time traversing this arc 276 */ 277 toindex = ++p->tos[0].link; 278 if (toindex >= p->tolimit) 279 /* halt further profiling */ 280 goto overflow; 281 282 *frompcindex = (u_short)toindex; 283 top = &p->tos[(size_t)toindex]; 284 top->selfpc = selfpc; 285 top->count = count; 286 top->link = 0; 287 goto done; 288 } 289 top = &p->tos[(size_t)toindex]; 290 if (top->selfpc == selfpc) { 291 /* 292 * arc at front of chain; usual case. 293 */ 294 top->count+= count; 295 goto done; 296 } 297 /* 298 * have to go looking down chain for it. 299 * top points to what we are looking at, 300 * we know it is not at the head of the chain. 301 */ 302 for (; /* goto done */; ) { 303 if (top->link == 0) { 304 /* 305 * top is end of the chain and 306 * none of the chain had 307 * top->selfpc == selfpc. so 308 * we allocate a new tostruct 309 * and link it to the head of 310 * the chain. 311 */ 312 toindex = ++p->tos[0].link; 313 if (toindex >= p->tolimit) 314 goto overflow; 315 316 top = &p->tos[(size_t)toindex]; 317 top->selfpc = selfpc; 318 top->count = count; 319 top->link = *frompcindex; 320 *frompcindex = (u_short)toindex; 321 goto done; 322 } 323 /* 324 * otherwise, check the next arc on the chain. 325 */ 326 top = &p->tos[top->link]; 327 if (top->selfpc == selfpc) { 328 /* 329 * there it is. 330 * add to its count. 331 */ 332 top->count += count; 333 goto done; 334 } 335 336 } 337 338 done: ; 339 } 340 341 } 342 overflow: ; 343 344} 345 346static void 347_m_gmon_merge(void) 348{ 349 struct gmonparam *q; 350 351 mutex_lock(&_gmonlock); 352 353 for (q = _gmonfree; q != NULL; q = (struct gmonparam *)(void *)q->kcount) 354 _m_gmon_merge_two(&_gmonparam, q); 355 356 for (q = _gmoninuse; q != NULL; 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 365void 366_mcleanup() 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 log, 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("mcount: Profiling of set{u,g}id binaries is not" 394 " allowed"); 395 return; 396 } 397 398 if (p->state == GMON_PROF_ERROR) 399 ERR("_mcleanup: tos overflow\n"); 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()) >= sizeof buf) { 426 warnx("_mcleanup: internal buffer overflow, PROFDIR too long"); 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("mcount: Cannot open `%s'", proffile); 438 return; 439 } 440#ifdef DEBUG 441 log = open("gmon.log", O_CREAT|O_TRUNC|O_WRONLY, 0664); 442 if (log < 0) { 443 warn("mcount: Cannot open `gmon.log'"); 444 return; 445 } 446 len = snprintf(buf2, sizeof buf2, "[mcleanup1] kcount 0x%x ssiz %d\n", 447 p->kcount, p->kcountsize); 448 (void)write(log, buf2, (size_t)len); 449#endif 450#ifdef _REENTRANT 451 _m_gmon_merge(); 452#endif 453 hdr = (struct gmonhdr *)&gmonhdr; 454 hdr->lpc = p->lowpc; 455 hdr->hpc = p->highpc; 456 hdr->ncnt = (int)(p->kcountsize + sizeof(gmonhdr)); 457 hdr->version = GMONVERSION; 458 hdr->profrate = clockinfo.profhz; 459 (void)write(fd, hdr, sizeof *hdr); 460 (void)write(fd, p->kcount, (size_t)p->kcountsize); 461 endfrom = (int)(p->fromssize / sizeof(*p->froms)); 462 for (fromindex = 0; fromindex < endfrom; fromindex++) { 463 if (p->froms[fromindex] == 0) 464 continue; 465 466 frompc = p->lowpc; 467 frompc += fromindex * p->hashfraction * sizeof(*p->froms); 468 for (toindex = p->froms[fromindex]; toindex != 0; 469 toindex = p->tos[toindex].link) { 470#ifdef DEBUG 471 len = snprintf(buf2, sizeof buf2, 472 "[mcleanup2] frompc 0x%x selfpc 0x%x count %d\n" , 473 frompc, p->tos[toindex].selfpc, 474 p->tos[toindex].count); 475 (void)write(log, buf2, (size_t)len); 476#endif 477 rawarc.raw_frompc = frompc; 478 rawarc.raw_selfpc = p->tos[toindex].selfpc; 479 rawarc.raw_count = p->tos[toindex].count; 480 write(fd, &rawarc, sizeof rawarc); 481 } 482 } 483 close(fd); 484} 485 486/* 487 * Control profiling 488 * profiling is what mcount checks to see if 489 * all the data structures are ready. 490 */ 491void 492moncontrol(mode) 493 int mode; 494{ 495 struct gmonparam *p = &_gmonparam; 496 497 if (mode) { 498 /* start */ 499 profil((char *)(void *)p->kcount, (size_t)p->kcountsize, 500 p->lowpc, s_scale); 501 p->state = GMON_PROF_ON; 502 } else { 503 /* stop */ 504 profil(NULL, 0, (u_long)0, 0); 505 p->state = GMON_PROF_OFF; 506 } 507} 508 509/* 510 * discover the tick frequency of the machine 511 * if something goes wrong, we return 0, an impossible hertz. 512 */ 513static int 514hertz() 515{ 516 struct itimerval tim; 517 518 tim.it_interval.tv_sec = 0; 519 tim.it_interval.tv_usec = 1; 520 tim.it_value.tv_sec = 0; 521 tim.it_value.tv_usec = 0; 522 setitimer(ITIMER_REAL, &tim, 0); 523 setitimer(ITIMER_REAL, 0, &tim); 524 if (tim.it_interval.tv_usec < 2) 525 return(0); 526 return (int)(1000000 / tim.it_interval.tv_usec); 527} 528