1/*	$OpenBSD: db_prof.c,v 1.5 2021/09/03 16:45:45 jasper Exp $	*/
2
3/*-
4 * Copyright (c) 1983, 1992, 1993
5 *	The Regents of the University of California.  All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 *    notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 *    notice, this list of conditions and the following disclaimer in the
14 *    documentation and/or other materials provided with the distribution.
15 * 3. Neither the name of the University nor the names of its contributors
16 *    may be used to endorse or promote products derived from this software
17 *    without specific prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29 * SUCH DAMAGE.
30 */
31#include <sys/param.h>
32#include <sys/systm.h>
33#include <sys/proc.h>
34#include <sys/queue.h>
35#include <sys/exec_elf.h>
36#include <sys/malloc.h>
37#include <sys/gmon.h>
38
39#include <machine/db_machdep.h>
40#include <ddb/db_extern.h>
41#include <ddb/db_sym.h>
42
43#include "dt.h" /* for NDT */
44
45#if NDT > 0
46#include <dev/dt/dtvar.h>
47#endif
48
49extern int db_profile;			/* Allow dynamic profiling */
50int db_prof_on;				/* Profiling state On/Off */
51
52void dt_prov_kprobe_patch_all_entry(void);
53void dt_prov_kprobe_depatch_all_entry(void);
54
55vaddr_t db_get_probe_addr(struct trapframe *);
56vaddr_t db_get_pc(struct trapframe *);
57
58int
59db_prof_enable(void)
60{
61#if NDT > 0
62	if (!db_profile)
63		return EPERM;
64
65	dt_prov_kprobe_patch_all_entry();
66	db_prof_on = 1;
67	return 0;
68#else
69	return ENOENT;
70#endif /* NDT > 0 */
71}
72
73void
74db_prof_disable(void)
75{
76#if NDT > 0
77	db_prof_on = 0;
78	dt_prov_kprobe_depatch_all_entry();
79#endif /* NDT > 0 */
80}
81
82/*
83 * Equivalent to mcount(), must be called with interrupt disabled.
84 */
85void
86db_prof_count(struct trapframe *frame)
87{
88	unsigned short *frompcindex;
89	struct tostruct *top, *prevtop;
90	struct gmonparam *p;
91	long toindex;
92	unsigned long frompc, selfpc;
93
94	if ((p = curcpu()->ci_gmon) == NULL)
95		return;
96
97	/*
98	 * check that we are profiling
99	 * and that we aren't recursively invoked.
100	 */
101	if (p->state != GMON_PROF_ON)
102		return;
103
104	frompc = db_get_pc(frame);
105	selfpc = db_get_probe_addr(frame);
106
107	/*
108	 * check that frompcindex is a reasonable pc value.
109	 * for example:	signal catchers get called from the stack,
110	 *		not from text space.  too bad.
111	 */
112	frompc -= p->lowpc;
113	if (frompc > p->textsize)
114		return;
115
116#if (HASHFRACTION & (HASHFRACTION - 1)) == 0
117	if (p->hashfraction == HASHFRACTION)
118		frompcindex =
119		    &p->froms[frompc / (HASHFRACTION * sizeof(*p->froms))];
120	else
121#endif
122		frompcindex =
123		    &p->froms[frompc / (p->hashfraction * sizeof(*p->froms))];
124	toindex = *frompcindex;
125	if (toindex == 0) {
126		/*
127		 *	first time traversing this arc
128		 */
129		toindex = ++p->tos[0].link;
130		if (toindex >= p->tolimit)
131			/* halt further profiling */
132			goto overflow;
133
134		*frompcindex = toindex;
135		top = &p->tos[toindex];
136		top->selfpc = selfpc;
137		top->count = 1;
138		top->link = 0;
139		return;
140	}
141	top = &p->tos[toindex];
142	if (top->selfpc == selfpc) {
143		/*
144		 * arc at front of chain; usual case.
145		 */
146		top->count++;
147		return;
148	}
149	/*
150	 * have to go looking down chain for it.
151	 * top points to what we are looking at,
152	 * prevtop points to previous top.
153	 * we know it is not at the head of the chain.
154	 */
155	for (; /* return */; ) {
156		if (top->link == 0) {
157			/*
158			 * top is end of the chain and none of the chain
159			 * had top->selfpc == selfpc.
160			 * so we allocate a new tostruct
161			 * and link it to the head of the chain.
162			 */
163			toindex = ++p->tos[0].link;
164			if (toindex >= p->tolimit)
165				goto overflow;
166
167			top = &p->tos[toindex];
168			top->selfpc = selfpc;
169			top->count = 1;
170			top->link = *frompcindex;
171			*frompcindex = toindex;
172			return;
173		}
174		/*
175		 * otherwise, check the next arc on the chain.
176		 */
177		prevtop = top;
178		top = &p->tos[top->link];
179		if (top->selfpc == selfpc) {
180			/*
181			 * there it is.
182			 * increment its count
183			 * move it to the head of the chain.
184			 */
185			top->count++;
186			toindex = prevtop->link;
187			prevtop->link = top->link;
188			top->link = *frompcindex;
189			*frompcindex = toindex;
190			return;
191		}
192	}
193
194overflow:
195	p->state = GMON_PROF_ERROR;
196}
197