Annotation of src/lib/libc/gmon/gmon.c, Revision 1.29
1.29 ! christos 1: /* $NetBSD: gmon.c,v 1.28 2006/10/04 21:23:56 christos Exp $ */
1.22 thorpej 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: */
1.3 cgd 37:
1.1 cgd 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.
1.21 agc 50: * 3. Neither the name of the University nor the names of its contributors
1.1 cgd 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:
1.10 christos 67: #include <sys/cdefs.h>
1.1 cgd 68: #if !defined(lint) && defined(LIBC_SCCS)
1.3 cgd 69: #if 0
70: static char sccsid[] = "@(#)gmon.c 8.1 (Berkeley) 6/4/93";
71: #else
1.29 ! christos 72: __RCSID("$NetBSD: gmon.c,v 1.28 2006/10/04 21:23:56 christos Exp $");
1.3 cgd 73: #endif
1.1 cgd 74: #endif
75:
1.10 christos 76: #include "namespace.h"
1.1 cgd 77: #include <sys/param.h>
78: #include <sys/time.h>
79: #include <sys/gmon.h>
1.22 thorpej 80: #include <sys/mman.h>
1.1 cgd 81: #include <sys/sysctl.h>
82:
83: #include <stdio.h>
1.5 jtc 84: #include <stdlib.h>
1.22 thorpej 85: #include <string.h>
1.1 cgd 86: #include <fcntl.h>
1.5 jtc 87: #include <limits.h>
1.1 cgd 88: #include <unistd.h>
1.10 christos 89: #include <err.h>
1.15 kleink 90: #include "extern.h"
1.22 thorpej 91: #include "reentrant.h"
1.1 cgd 92:
1.29 ! christos 93: struct gmonparam _gmonparam = { .state = GMON_PROF_OFF };
1.1 cgd 94:
1.22 thorpej 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:
1.14 christos 103: static u_int s_scale;
1.1 cgd 104: /* see profil(2) where this is describe (incorrectly) */
105: #define SCALE_1_TO_1 0x10000L
106:
1.12 kleink 107: #define ERR(s) write(STDERR_FILENO, s, sizeof(s))
1.1 cgd 108:
109: void moncontrol __P((int));
1.10 christos 110: void monstartup __P((u_long, u_long));
111: void _mcleanup __P((void));
1.1 cgd 112: static int hertz __P((void));
113:
1.22 thorpej 114: #ifdef _REENTRANT
115: static void _m_gmon_destructor(void *);
116: struct gmonparam *_m_gmon_alloc(void) __attribute__((__no_instrument_function__));
117: static void _m_gmon_merge(void);
118: static void _m_gmon_merge_two(struct gmonparam *, struct gmonparam *);
119: #endif
1.16 christos 120:
1.1 cgd 121: void
122: monstartup(lowpc, highpc)
123: u_long lowpc;
124: u_long highpc;
125: {
1.19 thorpej 126: u_long o;
1.1 cgd 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: */
1.27 dogcow 134: p->lowpc = rounddown(lowpc, HISTFRACTION * sizeof(HISTCOUNTER));
135: p->highpc = roundup(highpc, HISTFRACTION * sizeof(HISTCOUNTER));
1.1 cgd 136: p->textsize = p->highpc - p->lowpc;
137: p->kcountsize = p->textsize / HISTFRACTION;
138: p->hashfraction = HASHFRACTION;
1.6 cgd 139: p->fromssize = p->textsize / p->hashfraction;
1.1 cgd 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:
1.16 christos 147: cp = sbrk((intptr_t)(p->kcountsize + p->fromssize + p->tossize));
1.1 cgd 148: if (cp == (char *)-1) {
149: ERR("monstartup: out of memory\n");
150: return;
151: }
152: #ifdef notdef
1.13 perry 153: memset(cp, 0, p->kcountsize + p->fromssize + p->tossize);
1.1 cgd 154: #endif
1.14 christos 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;
1.1 cgd 160:
1.16 christos 161: __minbrk = sbrk((intptr_t)0);
1.1 cgd 162: p->tos[0].link = 0;
163:
164: o = p->highpc - p->lowpc;
165: if (p->kcountsize < o) {
1.2 cgd 166: #ifndef notdef
1.1 cgd 167: s_scale = ((float)p->kcountsize / o ) * SCALE_1_TO_1;
168: #else /* avoid floating point */
1.19 thorpej 169: u_long quot = o / p->kcountsize;
1.1 cgd 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:
1.22 thorpej 183: #ifdef _REENTRANT
184: _gmondummy.state = GMON_PROF_BUSY;
185: thr_keycreate(&_gmonkey, _m_gmon_destructor);
186: #endif
1.1 cgd 187: moncontrol(1);
188: }
189:
1.22 thorpej 190: #ifdef _REENTRANT
191: static 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;
1.26 mrg 205: prev = (struct gmonparam **)(void *)&q->kcount, /* XXX */
1.22 thorpej 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:
217: struct 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:
252: static 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:
346: static 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:
1.1 cgd 365: void
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;
1.5 jtc 379: char *profdir;
1.23 christos 380: const char *proffile;
1.5 jtc 381: char buf[PATH_MAX];
1.1 cgd 382: #ifdef DEBUG
1.25 christos 383: int logfd, len;
1.9 mrg 384: char buf2[200];
1.1 cgd 385: #endif
1.18 christos 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: }
1.1 cgd 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);
1.5 jtc 417:
418: if ((profdir = getenv("PROFDIR")) != NULL) {
419: /* If PROFDIR contains a null value, no profiling
420: output is produced */
1.20 dsl 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");
1.5 jtc 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);
1.1 cgd 436: if (fd < 0) {
1.10 christos 437: warn("mcount: Cannot open `%s'", proffile);
1.1 cgd 438: return;
439: }
440: #ifdef DEBUG
1.25 christos 441: logfd = open("gmon.log", O_CREAT|O_TRUNC|O_WRONLY, 0664);
442: if (logfd < 0) {
1.10 christos 443: warn("mcount: Cannot open `gmon.log'");
1.1 cgd 444: return;
445: }
1.25 christos 446: len = snprintf(buf2, sizeof buf2, "[mcleanup1] kcount %p ssiz %lu\n",
1.1 cgd 447: p->kcount, p->kcountsize);
1.25 christos 448: (void)write(logfd, buf2, (size_t)len);
1.1 cgd 449: #endif
1.22 thorpej 450: #ifdef _REENTRANT
451: _m_gmon_merge();
452: #endif
1.1 cgd 453: hdr = (struct gmonhdr *)&gmonhdr;
454: hdr->lpc = p->lowpc;
455: hdr->hpc = p->highpc;
1.14 christos 456: hdr->ncnt = (int)(p->kcountsize + sizeof(gmonhdr));
1.1 cgd 457: hdr->version = GMONVERSION;
458: hdr->profrate = clockinfo.profhz;
1.14 christos 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));
1.1 cgd 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
1.9 mrg 471: len = snprintf(buf2, sizeof buf2,
1.25 christos 472: "[mcleanup2] frompc 0x%lx selfpc 0x%lx count %lu\n" ,
473: (u_long)frompc, (u_long)p->tos[toindex].selfpc,
474: (u_long)p->tos[toindex].count);
475: (void)write(logfd, buf2, (size_t)len);
1.1 cgd 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: */
491: void
492: moncontrol(mode)
493: int mode;
494: {
495: struct gmonparam *p = &_gmonparam;
496:
497: if (mode) {
498: /* start */
1.14 christos 499: profil((char *)(void *)p->kcount, (size_t)p->kcountsize,
500: p->lowpc, s_scale);
1.1 cgd 501: p->state = GMON_PROF_ON;
502: } else {
503: /* stop */
1.14 christos 504: profil(NULL, 0, (u_long)0, 0);
1.1 cgd 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: */
513: static int
514: hertz()
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);
1.14 christos 526: return (int)(1000000 / tim.it_interval.tv_usec);
1.1 cgd 527: }
CVSweb <webmaster@jp.NetBSD.org>