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