1*c68df8acSchristos /* $NetBSD: mcount.c,v 1.18 2024/02/23 13:32:28 christos Exp $ */
237c9f0a6Schristos
337c9f0a6Schristos /*
437c9f0a6Schristos * Copyright (c) 2003, 2004 Wasabi Systems, Inc.
537c9f0a6Schristos * All rights reserved.
637c9f0a6Schristos *
737c9f0a6Schristos * Written by Nathan J. Williams for Wasabi Systems, Inc.
837c9f0a6Schristos *
937c9f0a6Schristos * Redistribution and use in source and binary forms, with or without
1037c9f0a6Schristos * modification, are permitted provided that the following conditions
1137c9f0a6Schristos * are met:
1237c9f0a6Schristos * 1. Redistributions of source code must retain the above copyright
1337c9f0a6Schristos * notice, this list of conditions and the following disclaimer.
1437c9f0a6Schristos * 2. Redistributions in binary form must reproduce the above copyright
1537c9f0a6Schristos * notice, this list of conditions and the following disclaimer in the
1637c9f0a6Schristos * documentation and/or other materials provided with the distribution.
1737c9f0a6Schristos * 3. All advertising materials mentioning features or use of this software
1837c9f0a6Schristos * must display the following acknowledgement:
1937c9f0a6Schristos * This product includes software developed for the NetBSD Project by
2037c9f0a6Schristos * Wasabi Systems, Inc.
2137c9f0a6Schristos * 4. The name of Wasabi Systems, Inc. may not be used to endorse
2237c9f0a6Schristos * or promote products derived from this software without specific prior
2337c9f0a6Schristos * written permission.
2437c9f0a6Schristos *
2537c9f0a6Schristos * THIS SOFTWARE IS PROVIDED BY WASABI SYSTEMS, INC. ``AS IS'' AND
2637c9f0a6Schristos * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
2737c9f0a6Schristos * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
2837c9f0a6Schristos * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL WASABI SYSTEMS, INC
2937c9f0a6Schristos * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
3037c9f0a6Schristos * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
3137c9f0a6Schristos * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
3237c9f0a6Schristos * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
3337c9f0a6Schristos * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
3437c9f0a6Schristos * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
3537c9f0a6Schristos * POSSIBILITY OF SUCH DAMAGE.
3637c9f0a6Schristos */
3737c9f0a6Schristos
3837c9f0a6Schristos /*-
3937c9f0a6Schristos * Copyright (c) 1983, 1992, 1993
4037c9f0a6Schristos * The Regents of the University of California. All rights reserved.
4137c9f0a6Schristos *
4237c9f0a6Schristos * Redistribution and use in source and binary forms, with or without
4337c9f0a6Schristos * modification, are permitted provided that the following conditions
4437c9f0a6Schristos * are met:
4537c9f0a6Schristos * 1. Redistributions of source code must retain the above copyright
4637c9f0a6Schristos * notice, this list of conditions and the following disclaimer.
4737c9f0a6Schristos * 2. Redistributions in binary form must reproduce the above copyright
4837c9f0a6Schristos * notice, this list of conditions and the following disclaimer in the
4937c9f0a6Schristos * documentation and/or other materials provided with the distribution.
5037c9f0a6Schristos * 3. Neither the name of the University nor the names of its contributors
5137c9f0a6Schristos * may be used to endorse or promote products derived from this software
5237c9f0a6Schristos * without specific prior written permission.
5337c9f0a6Schristos *
5437c9f0a6Schristos * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
5537c9f0a6Schristos * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
5637c9f0a6Schristos * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
5737c9f0a6Schristos * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
5837c9f0a6Schristos * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
5937c9f0a6Schristos * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
6037c9f0a6Schristos * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
6137c9f0a6Schristos * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
6237c9f0a6Schristos * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
6337c9f0a6Schristos * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
6437c9f0a6Schristos * SUCH DAMAGE.
6537c9f0a6Schristos */
6637c9f0a6Schristos
6737c9f0a6Schristos #ifdef _KERNEL_OPT
6837c9f0a6Schristos #include "opt_multiprocessor.h"
6937c9f0a6Schristos #endif
7037c9f0a6Schristos
711979ff4aSryo /* If building a standalone libkern, don't include mcount. */
721979ff4aSryo #if (!defined(_KERNEL) || defined(GPROF)) && !defined(_STANDALONE)
731979ff4aSryo
7437c9f0a6Schristos #include <sys/cdefs.h>
7537c9f0a6Schristos #if !defined(lint) && !defined(_KERNEL) && defined(LIBC_SCCS)
7637c9f0a6Schristos #if 0
7737c9f0a6Schristos static char sccsid[] = "@(#)mcount.c 8.1 (Berkeley) 6/4/93";
7837c9f0a6Schristos #else
79*c68df8acSchristos __RCSID("$NetBSD: mcount.c,v 1.18 2024/02/23 13:32:28 christos Exp $");
8037c9f0a6Schristos #endif
8137c9f0a6Schristos #endif
8237c9f0a6Schristos
8337c9f0a6Schristos #include <sys/param.h>
8437c9f0a6Schristos #include <sys/gmon.h>
85c2acaf5fSryo #include <sys/lock.h>
86567a3a02Sryo #include <sys/proc.h>
8737c9f0a6Schristos
8891165a00Schristos #ifndef _KERNEL
8937c9f0a6Schristos #include "reentrant.h"
9091165a00Schristos #endif
9137c9f0a6Schristos
921a5f018bSkamil #if defined(_REENTRANT) && !defined(_RUMPKERNEL)
9337c9f0a6Schristos extern thread_key_t _gmonkey;
9437c9f0a6Schristos extern struct gmonparam _gmondummy;
9537c9f0a6Schristos struct gmonparam *_m_gmon_alloc(void);
9637c9f0a6Schristos #endif
9737c9f0a6Schristos
98567219e1Smatt _MCOUNT_DECL(u_long, u_long)
9937c9f0a6Schristos #ifdef _KERNEL
1005dd5d540Suwe __attribute__((__no_instrument_function__))
101088af57aSuwe #endif
1025dd5d540Suwe __used;
10337c9f0a6Schristos
10498e9d5f9Schristos /* XXX: make these interfaces */
10598e9d5f9Schristos #ifdef _RUMPKERNEL
10698e9d5f9Schristos #undef MCOUNT_ENTER
10798e9d5f9Schristos #define MCOUNT_ENTER
10898e9d5f9Schristos #undef MCOUNT_EXIT
10998e9d5f9Schristos #define MCOUNT_EXIT
11098e9d5f9Schristos #undef MCOUNT
11198e9d5f9Schristos #define MCOUNT
11298e9d5f9Schristos #endif
11398e9d5f9Schristos
11437c9f0a6Schristos /*
11537c9f0a6Schristos * mcount is called on entry to each function compiled with the profiling
11637c9f0a6Schristos * switch set. _mcount(), which is declared in a machine-dependent way
11737c9f0a6Schristos * with _MCOUNT_DECL, does the actual work and is either inlined into a
11837c9f0a6Schristos * C routine or called by an assembly stub. In any case, this magic is
11937c9f0a6Schristos * taken care of by the MCOUNT definition in <machine/profile.h>.
12037c9f0a6Schristos *
12137c9f0a6Schristos * _mcount updates data structures that represent traversals of the
12237c9f0a6Schristos * program's call graph edges. frompc and selfpc are the return
12337c9f0a6Schristos * address and function address that represents the given call graph edge.
12437c9f0a6Schristos *
12537c9f0a6Schristos * Note: the original BSD code used the same variable (frompcindex) for
12637c9f0a6Schristos * both frompcindex and frompc. Any reasonable, modern compiler will
12737c9f0a6Schristos * perform this optimization.
12837c9f0a6Schristos */
129567219e1Smatt /* _mcount; may be static, inline, etc */
130*c68df8acSchristos /*LINTED unused*/
_MCOUNT_DECL(u_long frompc,u_long selfpc)131567219e1Smatt _MCOUNT_DECL(u_long frompc, u_long selfpc)
13237c9f0a6Schristos {
13337c9f0a6Schristos u_short *frompcindex;
13437c9f0a6Schristos struct tostruct *top, *prevtop;
13537c9f0a6Schristos struct gmonparam *p;
13637c9f0a6Schristos long toindex;
1371ce85336Spooka #if defined(_KERNEL) && !defined(_RUMPKERNEL)
13837c9f0a6Schristos int s;
13937c9f0a6Schristos #endif
14037c9f0a6Schristos
1411a5f018bSkamil #if defined(_REENTRANT) && !defined(_KERNEL) && !defined(_RUMPKERNEL)
14237c9f0a6Schristos if (__isthreaded) {
14352ae8af9Schristos /* prevent re-entry via thr_getspecific */
14452ae8af9Schristos if (_gmonparam.state != GMON_PROF_ON)
14552ae8af9Schristos return;
14652ae8af9Schristos _gmonparam.state = GMON_PROF_BUSY;
14737c9f0a6Schristos p = thr_getspecific(_gmonkey);
14837c9f0a6Schristos if (p == NULL) {
14937c9f0a6Schristos /* Prevent recursive calls while allocating */
15037c9f0a6Schristos thr_setspecific(_gmonkey, &_gmondummy);
15137c9f0a6Schristos p = _m_gmon_alloc();
15237c9f0a6Schristos }
15352ae8af9Schristos _gmonparam.state = GMON_PROF_ON;
15437c9f0a6Schristos } else
15537c9f0a6Schristos #endif
15637c9f0a6Schristos p = &_gmonparam;
15737c9f0a6Schristos /*
15837c9f0a6Schristos * check that we are profiling
15937c9f0a6Schristos * and that we aren't recursively invoked.
16037c9f0a6Schristos */
16137c9f0a6Schristos if (p->state != GMON_PROF_ON)
16237c9f0a6Schristos return;
163c2acaf5fSryo #if defined(_KERNEL) && !defined(_RUMPKERNEL)
16437c9f0a6Schristos MCOUNT_ENTER;
165c2acaf5fSryo #ifdef MULTIPROCESSOR
166567a3a02Sryo p = curcpu()->ci_gmon;
167567a3a02Sryo if (p == NULL || p->state != GMON_PROF_ON) {
168567a3a02Sryo MCOUNT_EXIT;
169567a3a02Sryo return;
170567a3a02Sryo }
171c2acaf5fSryo #endif
17237c9f0a6Schristos #endif
17337c9f0a6Schristos p->state = GMON_PROF_BUSY;
17437c9f0a6Schristos /*
17537c9f0a6Schristos * check that frompcindex is a reasonable pc value.
17637c9f0a6Schristos * for example: signal catchers get called from the stack,
17737c9f0a6Schristos * not from text space. too bad.
17837c9f0a6Schristos */
17937c9f0a6Schristos frompc -= p->lowpc;
18037c9f0a6Schristos if (frompc > p->textsize)
18137c9f0a6Schristos goto done;
18237c9f0a6Schristos
18337c9f0a6Schristos #if (HASHFRACTION & (HASHFRACTION - 1)) == 0
18437c9f0a6Schristos if (p->hashfraction == HASHFRACTION)
18537c9f0a6Schristos frompcindex =
18637c9f0a6Schristos &p->froms[
18737c9f0a6Schristos (size_t)(frompc / (HASHFRACTION * sizeof(*p->froms)))];
18837c9f0a6Schristos else
18937c9f0a6Schristos #endif
19037c9f0a6Schristos frompcindex =
19137c9f0a6Schristos &p->froms[
19237c9f0a6Schristos (size_t)(frompc / (p->hashfraction * sizeof(*p->froms)))];
19337c9f0a6Schristos toindex = *frompcindex;
19437c9f0a6Schristos if (toindex == 0) {
19537c9f0a6Schristos /*
19637c9f0a6Schristos * first time traversing this arc
19737c9f0a6Schristos */
19837c9f0a6Schristos toindex = ++p->tos[0].link;
19937c9f0a6Schristos if (toindex >= p->tolimit)
20037c9f0a6Schristos /* halt further profiling */
20137c9f0a6Schristos goto overflow;
20237c9f0a6Schristos
20337c9f0a6Schristos *frompcindex = (u_short)toindex;
20437c9f0a6Schristos top = &p->tos[(size_t)toindex];
20537c9f0a6Schristos top->selfpc = selfpc;
20637c9f0a6Schristos top->count = 1;
20737c9f0a6Schristos top->link = 0;
20837c9f0a6Schristos goto done;
20937c9f0a6Schristos }
21037c9f0a6Schristos top = &p->tos[(size_t)toindex];
21137c9f0a6Schristos if (top->selfpc == selfpc) {
21237c9f0a6Schristos /*
21337c9f0a6Schristos * arc at front of chain; usual case.
21437c9f0a6Schristos */
21537c9f0a6Schristos top->count++;
21637c9f0a6Schristos goto done;
21737c9f0a6Schristos }
21837c9f0a6Schristos /*
21937c9f0a6Schristos * have to go looking down chain for it.
22037c9f0a6Schristos * top points to what we are looking at,
22137c9f0a6Schristos * prevtop points to previous top.
22237c9f0a6Schristos * we know it is not at the head of the chain.
22337c9f0a6Schristos */
22437c9f0a6Schristos for (; /* goto done */; ) {
22537c9f0a6Schristos if (top->link == 0) {
22637c9f0a6Schristos /*
22737c9f0a6Schristos * top is end of the chain and none of the chain
22837c9f0a6Schristos * had top->selfpc == selfpc.
22937c9f0a6Schristos * so we allocate a new tostruct
23037c9f0a6Schristos * and link it to the head of the chain.
23137c9f0a6Schristos */
23237c9f0a6Schristos toindex = ++p->tos[0].link;
23337c9f0a6Schristos if (toindex >= p->tolimit)
23437c9f0a6Schristos goto overflow;
23537c9f0a6Schristos
23637c9f0a6Schristos top = &p->tos[(size_t)toindex];
23737c9f0a6Schristos top->selfpc = selfpc;
23837c9f0a6Schristos top->count = 1;
23937c9f0a6Schristos top->link = *frompcindex;
24037c9f0a6Schristos *frompcindex = (u_short)toindex;
24137c9f0a6Schristos goto done;
24237c9f0a6Schristos }
24337c9f0a6Schristos /*
24437c9f0a6Schristos * otherwise, check the next arc on the chain.
24537c9f0a6Schristos */
24637c9f0a6Schristos prevtop = top;
24737c9f0a6Schristos top = &p->tos[top->link];
24837c9f0a6Schristos if (top->selfpc == selfpc) {
24937c9f0a6Schristos /*
25037c9f0a6Schristos * there it is.
25137c9f0a6Schristos * increment its count
25237c9f0a6Schristos * move it to the head of the chain.
25337c9f0a6Schristos */
25437c9f0a6Schristos top->count++;
25537c9f0a6Schristos toindex = prevtop->link;
25637c9f0a6Schristos prevtop->link = top->link;
25737c9f0a6Schristos top->link = *frompcindex;
25837c9f0a6Schristos *frompcindex = (u_short)toindex;
25937c9f0a6Schristos goto done;
26037c9f0a6Schristos }
26137c9f0a6Schristos }
26237c9f0a6Schristos done:
26337c9f0a6Schristos p->state = GMON_PROF_ON;
264c2acaf5fSryo #if defined(_KERNEL) && !defined(_RUMPKERNEL)
26537c9f0a6Schristos MCOUNT_EXIT;
26637c9f0a6Schristos #endif
26737c9f0a6Schristos return;
268c2acaf5fSryo
26937c9f0a6Schristos overflow:
27037c9f0a6Schristos p->state = GMON_PROF_ERROR;
271c2acaf5fSryo #if defined(_KERNEL) && !defined(_RUMPKERNEL)
27237c9f0a6Schristos MCOUNT_EXIT;
27337c9f0a6Schristos #endif
27437c9f0a6Schristos return;
27537c9f0a6Schristos }
27637c9f0a6Schristos
27737c9f0a6Schristos #ifdef MCOUNT
27837c9f0a6Schristos /*
27937c9f0a6Schristos * Actual definition of mcount function. Defined in <machine/profile.h>,
28037c9f0a6Schristos * which is included by <sys/gmon.h>.
28137c9f0a6Schristos */
28237c9f0a6Schristos MCOUNT
28337c9f0a6Schristos #endif
28437c9f0a6Schristos
285567a3a02Sryo #if defined(_KERNEL) && !defined(_RUMPKERNEL) && defined(MULTIPROCESSOR)
286567a3a02Sryo void _gmonparam_merge(struct gmonparam *, struct gmonparam *);
287567a3a02Sryo
288567a3a02Sryo void
_gmonparam_merge(struct gmonparam * p,struct gmonparam * q)289567a3a02Sryo _gmonparam_merge(struct gmonparam *p, struct gmonparam *q)
290567a3a02Sryo {
291567a3a02Sryo u_long fromindex;
292567a3a02Sryo u_short *frompcindex, qtoindex, toindex;
293567a3a02Sryo u_long selfpc;
294567a3a02Sryo u_long endfrom;
295567a3a02Sryo long count;
296567a3a02Sryo struct tostruct *top;
297567a3a02Sryo int i;
298567a3a02Sryo
299567a3a02Sryo count = q->kcountsize / sizeof(*q->kcount);
300567a3a02Sryo for (i = 0; i < count; i++)
301567a3a02Sryo p->kcount[i] += q->kcount[i];
302567a3a02Sryo
303567a3a02Sryo endfrom = (q->fromssize / sizeof(*q->froms));
304567a3a02Sryo for (fromindex = 0; fromindex < endfrom; fromindex++) {
305567a3a02Sryo if (q->froms[fromindex] == 0)
306567a3a02Sryo continue;
307567a3a02Sryo for (qtoindex = q->froms[fromindex]; qtoindex != 0;
308567a3a02Sryo qtoindex = q->tos[qtoindex].link) {
309567a3a02Sryo selfpc = q->tos[qtoindex].selfpc;
310567a3a02Sryo count = q->tos[qtoindex].count;
311567a3a02Sryo /* cribbed from mcount */
312567a3a02Sryo frompcindex = &p->froms[fromindex];
313567a3a02Sryo toindex = *frompcindex;
314567a3a02Sryo if (toindex == 0) {
315567a3a02Sryo /*
316567a3a02Sryo * first time traversing this arc
317567a3a02Sryo */
318567a3a02Sryo toindex = ++p->tos[0].link;
319567a3a02Sryo if (toindex >= p->tolimit)
320567a3a02Sryo /* halt further profiling */
321567a3a02Sryo goto overflow;
322567a3a02Sryo
323567a3a02Sryo *frompcindex = (u_short)toindex;
324567a3a02Sryo top = &p->tos[(size_t)toindex];
325567a3a02Sryo top->selfpc = selfpc;
326567a3a02Sryo top->count = count;
327567a3a02Sryo top->link = 0;
328567a3a02Sryo goto done;
329567a3a02Sryo }
330567a3a02Sryo top = &p->tos[(size_t)toindex];
331567a3a02Sryo if (top->selfpc == selfpc) {
332567a3a02Sryo /*
333567a3a02Sryo * arc at front of chain; usual case.
334567a3a02Sryo */
335567a3a02Sryo top->count+= count;
336567a3a02Sryo goto done;
337567a3a02Sryo }
338567a3a02Sryo /*
339567a3a02Sryo * have to go looking down chain for it.
340567a3a02Sryo * top points to what we are looking at,
341567a3a02Sryo * we know it is not at the head of the chain.
342567a3a02Sryo */
343567a3a02Sryo for (; /* goto done */; ) {
344567a3a02Sryo if (top->link == 0) {
345567a3a02Sryo /*
346567a3a02Sryo * top is end of the chain and
347567a3a02Sryo * none of the chain had
348567a3a02Sryo * top->selfpc == selfpc. so
349567a3a02Sryo * we allocate a new tostruct
350567a3a02Sryo * and link it to the head of
351567a3a02Sryo * the chain.
352567a3a02Sryo */
353567a3a02Sryo toindex = ++p->tos[0].link;
354567a3a02Sryo if (toindex >= p->tolimit)
355567a3a02Sryo goto overflow;
356567a3a02Sryo
357567a3a02Sryo top = &p->tos[(size_t)toindex];
358567a3a02Sryo top->selfpc = selfpc;
359567a3a02Sryo top->count = count;
360567a3a02Sryo top->link = *frompcindex;
361567a3a02Sryo *frompcindex = (u_short)toindex;
362567a3a02Sryo goto done;
363567a3a02Sryo }
364567a3a02Sryo /*
365567a3a02Sryo * otherwise, check the next arc on the chain.
366567a3a02Sryo */
367567a3a02Sryo top = &p->tos[top->link];
368567a3a02Sryo if (top->selfpc == selfpc) {
369567a3a02Sryo /*
370567a3a02Sryo * there it is.
371567a3a02Sryo * add to its count.
372567a3a02Sryo */
373567a3a02Sryo top->count += count;
374567a3a02Sryo goto done;
375567a3a02Sryo }
376567a3a02Sryo }
377567a3a02Sryo
378567a3a02Sryo done: ;
379567a3a02Sryo }
380567a3a02Sryo
381567a3a02Sryo }
382567a3a02Sryo overflow: ;
383567a3a02Sryo
384567a3a02Sryo }
385567a3a02Sryo #endif
386567a3a02Sryo
3878ca2029dSchristos #endif /* (!_KERNEL || GPROF) && !_STANDALONE */
388