1 /* $NetBSD: gmon.c,v 1.33 2011/01/05 00:03:52 wiz Exp $ */
4 * Copyright (c) 2003, 2004 Wasabi Systems, Inc.
7 * Written by Nathan J. Williams for Wasabi Systems, Inc.
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
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
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.
39 * Copyright (c) 1983, 1992, 1993
40 * The Regents of the University of California. All rights reserved.
42 * Redistribution and use in source and binary forms, with or without
43 * modification, are permitted provided that the following conditions
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.
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
67 #include <sys/cdefs.h>
68 #if !defined(lint) && defined(LIBC_SCCS)
70 static char sccsid
[] = "@(#)gmon.c 8.1 (Berkeley) 6/4/93";
72 __RCSID("$NetBSD: gmon.c,v 1.33 2011/01/05 00:03:52 wiz Exp $");
76 #include "namespace.h"
77 #include <sys/param.h>
81 #include <sys/sysctl.h>
91 #include "reentrant.h"
93 struct gmonparam _gmonparam
= { .state
= GMON_PROF_OFF
};
96 struct gmonparam
*_gmonfree
;
97 struct gmonparam
*_gmoninuse
;
98 mutex_t _gmonlock
= MUTEX_INITIALIZER
;
99 thread_key_t _gmonkey
;
100 struct gmonparam _gmondummy
;
103 static u_int s_scale
;
104 /* see profil(2) where this is describe (incorrectly) */
105 #define SCALE_1_TO_1 0x10000L
107 void moncontrol(int);
108 void monstartup(u_long
, u_long
);
109 void _mcleanup(void);
110 static int hertz(void);
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
*);
121 monstartup(u_long lowpc
, u_long highpc
)
125 struct gmonparam
*p
= &_gmonparam
;
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.
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
);
144 cp
= sbrk((intptr_t)(p
->kcountsize
+ p
->fromssize
+ p
->tossize
));
145 if (cp
== (char *)-1) {
146 warnx("%s: out of memory", __func__
);
150 (void)memset(cp
, 0, p
->kcountsize
+ p
->fromssize
+ p
->tossize
);
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
;
158 __minbrk
= sbrk((intptr_t)0);
161 o
= p
->highpc
- p
->lowpc
;
162 if (p
->kcountsize
< o
) {
164 s_scale
= ((float)p
->kcountsize
/ o
) * SCALE_1_TO_1
;
165 #else /* avoid floating point */
166 u_long quot
= o
/ p
->kcountsize
;
170 else if (quot
>= 0x100)
171 s_scale
= 0x10000 / quot
;
172 else if (o
>= 0x800000)
173 s_scale
= 0x1000000 / (o
/ (p
->kcountsize
>> 8));
175 s_scale
= 0x1000000 / ((o
<< 8) / p
->kcountsize
);
178 s_scale
= SCALE_1_TO_1
;
181 _gmondummy
.state
= GMON_PROF_BUSY
;
182 thr_keycreate(&_gmonkey
, _m_gmon_destructor
);
189 _m_gmon_destructor(void *arg
)
191 struct gmonparam
*p
= arg
, *q
, **prev
;
193 if (p
== &_gmondummy
)
196 thr_setspecific(_gmonkey
, &_gmondummy
);
198 mutex_lock(&_gmonlock
);
199 /* XXX eww, linear list traversal. */
200 for (q
= _gmoninuse
, prev
= &_gmoninuse
;
202 prev
= (struct gmonparam
**)(void *)&q
->kcount
, /* XXX */
203 q
= (struct gmonparam
*)(void *)q
->kcount
) {
205 *prev
= (struct gmonparam
*)(void *)q
->kcount
;
207 p
->kcount
= (u_short
*)(void *)_gmonfree
;
209 mutex_unlock(&_gmonlock
);
211 thr_setspecific(_gmonkey
, NULL
);
220 mutex_lock(&_gmonlock
);
221 if (_gmonfree
!= NULL
) {
223 _gmonfree
= (struct gmonparam
*)(void *)p
->kcount
;
224 p
->kcount
= (u_short
*)(void *)_gmoninuse
;
227 mutex_unlock(&_gmonlock
);
229 (size_t)(sizeof (struct gmonparam
) +
230 _gmonparam
.fromssize
+ _gmonparam
.tossize
),
231 PROT_READ
|PROT_WRITE
, MAP_ANON
|MAP_PRIVATE
, -1, 0LL);
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
;
243 mutex_unlock(&_gmonlock
);
244 thr_setspecific(_gmonkey
, p
);
250 _m_gmon_merge_two(struct gmonparam
*p
, struct gmonparam
*q
)
253 u_short
*frompcindex
, qtoindex
, toindex
;
257 struct tostruct
*top
;
259 endfrom
= (q
->fromssize
/ sizeof(*q
->froms
));
260 for (fromindex
= 0; fromindex
< endfrom
; fromindex
++) {
261 if (q
->froms
[fromindex
] == 0)
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
;
272 * first time traversing this arc
274 toindex
= ++p
->tos
[0].link
;
275 if (toindex
>= p
->tolimit
)
276 /* halt further profiling */
279 *frompcindex
= (u_short
)toindex
;
280 top
= &p
->tos
[(size_t)toindex
];
281 top
->selfpc
= selfpc
;
286 top
= &p
->tos
[(size_t)toindex
];
287 if (top
->selfpc
== selfpc
) {
289 * arc at front of chain; usual case.
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.
299 for (; /* goto done */; ) {
300 if (top
->link
== 0) {
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
309 toindex
= ++p
->tos
[0].link
;
310 if (toindex
>= p
->tolimit
)
313 top
= &p
->tos
[(size_t)toindex
];
314 top
->selfpc
= selfpc
;
316 top
->link
= *frompcindex
;
317 *frompcindex
= (u_short
)toindex
;
321 * otherwise, check the next arc on the chain.
323 top
= &p
->tos
[top
->link
];
324 if (top
->selfpc
== selfpc
) {
348 mutex_lock(&_gmonlock
);
350 for (q
= _gmonfree
; q
!= NULL
;
351 q
= (struct gmonparam
*)(void *)q
->kcount
)
352 _m_gmon_merge_two(&_gmonparam
, q
);
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
);
360 mutex_unlock(&_gmonlock
);
372 struct rawarc rawarc
;
373 struct gmonparam
*p
= &_gmonparam
;
374 struct gmonhdr gmonhdr
, *hdr
;
375 struct clockinfo clockinfo
;
381 const char *proffile
;
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
393 if (issetugid() && (geteuid() != getuid() || getegid() != getgid())) {
394 warnx("%s: Profiling of set{u,g}id binaries is not"
395 " allowed", __func__
);
399 if (p
->state
== GMON_PROF_ERROR
)
400 warnx("%s: tos overflow", __func__
);
403 clockinfo
.profhz
= sysconf(_SC_CLK_TCK
);
405 size
= sizeof(clockinfo
);
407 mib
[1] = KERN_CLOCKRATE
;
408 if (sysctl(mib
, 2, &clockinfo
, &size
, NULL
, 0) < 0) {
412 clockinfo
.profhz
= hertz();
413 } else if (clockinfo
.profhz
== 0) {
414 if (clockinfo
.hz
!= 0)
415 clockinfo
.profhz
= clockinfo
.hz
;
417 clockinfo
.profhz
= hertz();
419 #endif /* !__minix */
423 if ((profdir
= getenv("PROFDIR")) != NULL
) {
424 /* If PROFDIR contains a null value, no profiling
425 output is produced */
426 if (*profdir
== '\0')
429 if (snprintf(buf
, sizeof buf
, "%s/%d.%s",
430 profdir
, getpid(), getprogname()) >= (int)(sizeof buf
)) {
431 warnx("%s: internal buffer overflow, PROFDIR too long",
438 proffile
= "gmon.out";
441 fd
= open(proffile
, O_CREAT
|O_TRUNC
|O_WRONLY
, 0666);
443 warn("%s: Cannot open `%s'", __func__
, proffile
);
447 logfd
= open("gmon.log", O_CREAT
|O_TRUNC
|O_WRONLY
, 0664);
449 warn("%s: Cannot open `%s'", __func__
, "gmon.log");
453 len
= snprintf(buf2
, sizeof buf2
, "[mcleanup1] kcount %p ssiz %lu\n",
454 p
->kcount
, p
->kcountsize
);
455 (void)write(logfd
, buf2
, (size_t)len
);
460 hdr
= (struct gmonhdr
*)&gmonhdr
;
462 hdr
->hpc
= p
->highpc
;
463 hdr
->ncnt
= (int)(p
->kcountsize
+ sizeof(gmonhdr
));
464 hdr
->version
= GMONVERSION
;
465 hdr
->profrate
= clockinfo
.profhz
;
466 (void)write(fd
, hdr
, sizeof *hdr
);
467 (void)write(fd
, p
->kcount
, (size_t)p
->kcountsize
);
468 endfrom
= (int)(p
->fromssize
/ sizeof(*p
->froms
));
469 for (fromindex
= 0; fromindex
< endfrom
; fromindex
++) {
470 if (p
->froms
[fromindex
] == 0)
474 frompc
+= fromindex
* p
->hashfraction
* sizeof(*p
->froms
);
475 for (toindex
= p
->froms
[fromindex
]; toindex
!= 0;
476 toindex
= p
->tos
[toindex
].link
) {
478 len
= snprintf(buf2
, sizeof buf2
,
479 "[mcleanup2] frompc 0x%lx selfpc 0x%lx count %lu\n" ,
480 (u_long
)frompc
, (u_long
)p
->tos
[toindex
].selfpc
,
481 (u_long
)p
->tos
[toindex
].count
);
482 (void)write(logfd
, buf2
, (size_t)len
);
484 rawarc
.raw_frompc
= frompc
;
485 rawarc
.raw_selfpc
= p
->tos
[toindex
].selfpc
;
486 rawarc
.raw_count
= p
->tos
[toindex
].count
;
487 (void)write(fd
, &rawarc
, sizeof rawarc
);
498 * profiling is what mcount checks to see if
499 * all the data structures are ready.
504 struct gmonparam
*p
= &_gmonparam
;
508 profil((char *)(void *)p
->kcount
, (size_t)p
->kcountsize
,
510 p
->state
= GMON_PROF_ON
;
513 profil(NULL
, 0, (u_long
)0, 0);
514 p
->state
= GMON_PROF_OFF
;
520 * discover the tick frequency of the machine
521 * if something goes wrong, we return 0, an impossible hertz.
526 struct itimerspec tim
;
530 tim
.it_interval
.tv_sec
= 0;
531 tim
.it_interval
.tv_nsec
= 1;
532 tim
.it_value
.tv_sec
= 0;
533 tim
.it_value
.tv_nsec
= 0;
535 if (timer_create(CLOCK_REALTIME
, NULL
, &t
) == -1)
538 if (timer_settime(t
, 0, &tim
, NULL
) == -1)
541 if (timer_gettime(t
, &tim
) == -1)
544 if (tim
.it_interval
.tv_nsec
< 2)
547 rv
= (int)(1000000000LL / tim
.it_interval
.tv_nsec
);
549 (void)timer_delete(t
);
552 #endif /* !__minix */