xref: /openbsd-src/sys/ddb/db_prof.c (revision f2da64fbbbf1b03f09f390ab01267c93dfd77c4c)
1 /*	$OpenBSD: db_prof.c,v 1.1 2016/09/04 09:22:29 mpi Exp $	*/
2 
3 /*
4  * Copyright (c) 2016 Martin Pieuchot
5  *
6  * Permission to use, copy, modify, and distribute this software for any
7  * purpose with or without fee is hereby granted, provided that the above
8  * copyright notice and this permission notice appear in all copies.
9  *
10  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17  */
18 /*-
19  * Copyright (c) 1983, 1992, 1993
20  *	The Regents of the University of California.  All rights reserved.
21  *
22  * Redistribution and use in source and binary forms, with or without
23  * modification, are permitted provided that the following conditions
24  * are met:
25  * 1. Redistributions of source code must retain the above copyright
26  *    notice, this list of conditions and the following disclaimer.
27  * 2. Redistributions in binary form must reproduce the above copyright
28  *    notice, this list of conditions and the following disclaimer in the
29  *    documentation and/or other materials provided with the distribution.
30  * 3. Neither the name of the University nor the names of its contributors
31  *    may be used to endorse or promote products derived from this software
32  *    without specific prior written permission.
33  *
34  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
35  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
36  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
37  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
38  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
39  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
40  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
41  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
42  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
43  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
44  * SUCH DAMAGE.
45  */
46 
47 #include <sys/param.h>
48 #include <sys/systm.h>
49 #include <sys/proc.h>
50 #include <sys/queue.h>
51 #include <sys/exec_elf.h>
52 #include <sys/malloc.h>
53 #include <sys/gmon.h>
54 
55 #include <machine/db_machdep.h>
56 #include <ddb/db_extern.h>
57 #include <ddb/db_access.h> /* for db_write_bytes() */
58 #include <ddb/db_sym.h>
59 
60 extern char etext[];
61 
62 struct prof_probe {
63 	const char		*pp_name;
64 	vaddr_t			 pp_inst;
65 	Elf_Sym			*pp_symb;
66 	SLIST_ENTRY(prof_probe)	 pp_next;
67 };
68 
69 #define PPTSIZE		PAGE_SIZE
70 #define	PPTMASK		((PPTSIZE / sizeof(struct prof_probe)) - 1)
71 #define INSTTOIDX(inst)	((((unsigned long)(inst)) >> 4) & PPTMASK)
72 SLIST_HEAD(, prof_probe) *pp_table;
73 
74 extern int db_profile;			/* Allow dynamic profiling */
75 
76 vaddr_t db_get_pc(struct trapframe *);
77 vaddr_t db_get_probe_addr(struct trapframe *);
78 
79 void db_prof_forall(db_sym_t, char *, char *, int, void *);
80 void db_prof_count(unsigned long, unsigned long);
81 
82 void
83 db_prof_init(void)
84 {
85 	unsigned long nentries;
86 
87 	pp_table = malloc(PPTSIZE, M_TEMP, M_NOWAIT|M_ZERO);
88 	if (pp_table == NULL)
89 		return;
90 
91 	db_elf_sym_forall(db_prof_forall, &nentries);
92 	printf("ddb probe table references %lu entry points\n", nentries);
93 }
94 
95 void
96 db_prof_forall(db_sym_t sym, char *name, char *suff, int pre, void *xarg)
97 {
98 	Elf_Sym *symb = (Elf_Sym *)sym;
99 	unsigned long *nentries = xarg;
100 	struct prof_probe *pp;
101 	vaddr_t inst;
102 
103 	if (ELFDEFNNAME(ST_TYPE)(symb->st_info) != STT_FUNC)
104 		return;
105 
106 	inst = symb->st_value;
107 	if (inst < KERNBASE || inst >= (vaddr_t)&etext)
108 		return;
109 
110 	if (*((uint8_t *)inst) != SSF_INST)
111 		return;
112 
113 	if (strncmp(name, "db_", 3) == 0 || strncmp(name, "trap", 4) == 0)
114 		return;
115 
116 	pp = malloc(sizeof(struct prof_probe), M_TEMP, M_NOWAIT|M_ZERO);
117 	if (pp == NULL)
118 		return;
119 
120 	pp->pp_name = name;
121 	pp->pp_inst = inst;
122 	pp->pp_symb = symb;
123 
124 	SLIST_INSERT_HEAD(&pp_table[INSTTOIDX(pp->pp_inst)], pp, pp_next);
125 
126 	(*nentries)++;
127 }
128 
129 int
130 db_prof_enable(void)
131 {
132 #ifdef __amd64__
133 	struct prof_probe *pp;
134 	uint8_t patch = BKPT_INST;
135 	unsigned long s;
136 	int i;
137 
138 	if (!db_profile)
139 		return EPERM;
140 
141 	if (pp_table == NULL)
142 		return ENOENT;
143 
144 	KASSERT(BKPT_SIZE == SSF_SIZE);
145 
146 	s = intr_disable();
147 	for (i = 0; i < (PPTSIZE / sizeof(*pp)); i++) {
148 		SLIST_FOREACH(pp, &pp_table[i], pp_next) {
149 			db_write_bytes(pp->pp_inst, BKPT_SIZE, &patch);
150 		}
151 	}
152 	intr_restore(s);
153 
154 	return 0;
155 #else
156 	return ENOENT;
157 #endif
158 }
159 
160 void
161 db_prof_disable(void)
162 {
163 	struct prof_probe *pp;
164 	uint8_t patch = SSF_INST;
165 	unsigned long s;
166 	int i;
167 
168 	s = intr_disable();
169 	for (i = 0; i < (PPTSIZE / sizeof(*pp)); i++) {
170 		SLIST_FOREACH(pp, &pp_table[i], pp_next)
171 			db_write_bytes(pp->pp_inst, SSF_SIZE, &patch);
172 	}
173 	intr_restore(s);
174 }
175 
176 int
177 db_prof_hook(struct trapframe *frame)
178 {
179 	struct prof_probe *pp;
180 	vaddr_t pc, inst;
181 
182 	if (pp_table == NULL)
183 		return 0;
184 
185 	pc = db_get_pc(frame);
186 	inst = db_get_probe_addr(frame);
187 
188 	SLIST_FOREACH(pp, &pp_table[INSTTOIDX(inst)], pp_next) {
189 		if (pp->pp_inst == inst) {
190 			db_prof_count(pc, inst);
191 			return 1;
192 		}
193 	}
194 	return 0;
195 }
196 
197 /*
198  * Equivalent to mcount(), must be called with interrupt disabled.
199  */
200 void
201 db_prof_count(unsigned long frompc, unsigned long selfpc)
202 {
203 	unsigned short *frompcindex;
204 	struct tostruct *top, *prevtop;
205 	struct gmonparam *p;
206 	long toindex;
207 
208 	if ((p = curcpu()->ci_gmon) == NULL)
209 		return;
210 
211 	/*
212 	 * check that we are profiling
213 	 * and that we aren't recursively invoked.
214 	 */
215 	if (p->state != GMON_PROF_ON)
216 		return;
217 
218 	/*
219 	 * check that frompcindex is a reasonable pc value.
220 	 * for example:	signal catchers get called from the stack,
221 	 *		not from text space.  too bad.
222 	 */
223 	frompc -= p->lowpc;
224 	if (frompc > p->textsize)
225 		return;
226 
227 #if (HASHFRACTION & (HASHFRACTION - 1)) == 0
228 	if (p->hashfraction == HASHFRACTION)
229 		frompcindex =
230 		    &p->froms[frompc / (HASHFRACTION * sizeof(*p->froms))];
231 	else
232 #endif
233 		frompcindex =
234 		    &p->froms[frompc / (p->hashfraction * sizeof(*p->froms))];
235 	toindex = *frompcindex;
236 	if (toindex == 0) {
237 		/*
238 		 *	first time traversing this arc
239 		 */
240 		toindex = ++p->tos[0].link;
241 		if (toindex >= p->tolimit)
242 			/* halt further profiling */
243 			goto overflow;
244 
245 		*frompcindex = toindex;
246 		top = &p->tos[toindex];
247 		top->selfpc = selfpc;
248 		top->count = 1;
249 		top->link = 0;
250 		return;
251 	}
252 	top = &p->tos[toindex];
253 	if (top->selfpc == selfpc) {
254 		/*
255 		 * arc at front of chain; usual case.
256 		 */
257 		top->count++;
258 		return;
259 	}
260 	/*
261 	 * have to go looking down chain for it.
262 	 * top points to what we are looking at,
263 	 * prevtop points to previous top.
264 	 * we know it is not at the head of the chain.
265 	 */
266 	for (; /* return */; ) {
267 		if (top->link == 0) {
268 			/*
269 			 * top is end of the chain and none of the chain
270 			 * had top->selfpc == selfpc.
271 			 * so we allocate a new tostruct
272 			 * and link it to the head of the chain.
273 			 */
274 			toindex = ++p->tos[0].link;
275 			if (toindex >= p->tolimit)
276 				goto overflow;
277 
278 			top = &p->tos[toindex];
279 			top->selfpc = selfpc;
280 			top->count = 1;
281 			top->link = *frompcindex;
282 			*frompcindex = toindex;
283 			return;
284 		}
285 		/*
286 		 * otherwise, check the next arc on the chain.
287 		 */
288 		prevtop = top;
289 		top = &p->tos[top->link];
290 		if (top->selfpc == selfpc) {
291 			/*
292 			 * there it is.
293 			 * increment its count
294 			 * move it to the head of the chain.
295 			 */
296 			top->count++;
297 			toindex = prevtop->link;
298 			prevtop->link = top->link;
299 			top->link = *frompcindex;
300 			*frompcindex = toindex;
301 			return;
302 		}
303 	}
304 
305 overflow:
306 	p->state = GMON_PROF_ERROR;
307 }
308