1 /* $NetBSD: db_trace.c,v 1.26 2019/05/09 16:48:31 ryo Exp $ */ 2 3 /*- 4 * Copyright (c) 2000 Tsubai Masanari. All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 3. The name of the author may not be used to endorse or promote products 15 * derived from this software without specific prior written permission. 16 * 17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 26 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 */ 28 29 #include <sys/cdefs.h> 30 __KERNEL_RCSID(0, "$NetBSD: db_trace.c,v 1.26 2019/05/09 16:48:31 ryo Exp $"); 31 32 #include <sys/param.h> 33 #include <sys/systm.h> 34 35 #include <machine/db_machdep.h> 36 37 #include <ddb/db_access.h> 38 #include <ddb/db_interface.h> 39 #include <ddb/db_output.h> 40 #include <ddb/db_proc.h> 41 #include <ddb/db_sym.h> 42 #include <ddb/db_variables.h> 43 44 volatile int db_trace_debug = 0; /* settabble from ddb */ 45 #define DPRINTF(level, fmt, args...) \ 46 do { \ 47 if (__predict_false(db_trace_debug > (level))) { \ 48 print(fmt, ## args); \ 49 } \ 50 } while (0 /* CONSTCOND*/) 51 52 extern char start[], etext[]; 53 static bool db_nextframe(db_addr_t, db_addr_t, db_addr_t *, db_addr_t *, 54 db_addr_t *, void (*)(const char *, ...) __printflike(1, 2)); 55 56 const struct db_variable db_regs[] = { 57 { "r0", (long *)&ddb_regs.tf_r0, FCN_NULL }, 58 { "r1", (long *)&ddb_regs.tf_r1, FCN_NULL }, 59 { "r2", (long *)&ddb_regs.tf_r2, FCN_NULL }, 60 { "r3", (long *)&ddb_regs.tf_r3, FCN_NULL }, 61 { "r4", (long *)&ddb_regs.tf_r4, FCN_NULL }, 62 { "r5", (long *)&ddb_regs.tf_r5, FCN_NULL }, 63 { "r6", (long *)&ddb_regs.tf_r6, FCN_NULL }, 64 { "r7", (long *)&ddb_regs.tf_r7, FCN_NULL }, 65 { "r8", (long *)&ddb_regs.tf_r8, FCN_NULL }, 66 { "r9", (long *)&ddb_regs.tf_r9, FCN_NULL }, 67 { "r10", (long *)&ddb_regs.tf_r10, FCN_NULL }, 68 { "r11", (long *)&ddb_regs.tf_r11, FCN_NULL }, 69 { "r12", (long *)&ddb_regs.tf_r12, FCN_NULL }, 70 { "r13", (long *)&ddb_regs.tf_r13, FCN_NULL }, 71 { "r14", (long *)&ddb_regs.tf_r14, FCN_NULL }, 72 { "r15", (long *)&ddb_regs.tf_r15, FCN_NULL }, 73 { "pr", (long *)&ddb_regs.tf_pr, FCN_NULL }, 74 { "pc", (long *)&ddb_regs.tf_spc, FCN_NULL }, 75 { "sr", (long *)&ddb_regs.tf_ssr, FCN_NULL }, 76 { "mach", (long *)&ddb_regs.tf_mach, FCN_NULL }, 77 { "macl", (long *)&ddb_regs.tf_macl, FCN_NULL }, 78 }; 79 80 const struct db_variable * const db_eregs = db_regs + __arraycount(db_regs); 81 82 static void 83 dump_trapframe(struct trapframe *tf, 84 void (*print)(const char *, ...) __printflike(1, 2)) 85 { 86 print(" sr=%08x gbr=%08x pc=%08x pr=%08x\n", 87 tf->tf_ssr, tf->tf_gbr, tf->tf_spc, tf->tf_pr); 88 print(" r0=%08x r1=%08x r2=%08x r3=%08x\n", 89 tf->tf_r0, tf->tf_r1, tf->tf_r2, tf->tf_r3); 90 print(" r4=%08x r6=%08x r7=%08x r8=%08x\n", 91 tf->tf_r4, tf->tf_r5, tf->tf_r6, tf->tf_r7); 92 print(" r5=%08x r9=%08x r10=%08x r11=%08x\n", 93 tf->tf_r8, tf->tf_r9, tf->tf_r10, tf->tf_r11); 94 print(" r12=%08x r13=%08x r14=%08x sp=r15=%08x\n", 95 tf->tf_r12, tf->tf_r13, tf->tf_r14, tf->tf_r15); 96 } 97 98 void 99 db_stack_trace_print(db_expr_t addr, bool have_addr, db_expr_t count, 100 const char *modif, void (*print)(const char *, ...) __printflike(1, 2)) 101 { 102 struct trapframe *tf; 103 db_addr_t func, pc, lastpc, pr, sp, fp; 104 uint32_t vbr; 105 bool lwpid = false; 106 bool lwpaddr = false; 107 const char *cp; 108 char c; 109 110 __asm volatile("stc vbr, %0" : "=r"(vbr)); 111 112 cp = modif; 113 while ((c = *cp++) != 0) { 114 if (c == 'a') 115 lwpaddr = true; 116 else if (c == 't') 117 lwpid = true; 118 } 119 120 if (lwpaddr && lwpid) { 121 db_printf("only one of /a or /t can be specified\n"); 122 return; 123 } 124 if ((lwpaddr || lwpid) && !have_addr) { 125 db_printf("%s required\n", lwpaddr ? "address" : "pid"); 126 return; 127 } 128 129 if (!have_addr) { 130 tf = &ddb_regs; 131 fp = tf->tf_r14; 132 sp = tf->tf_r15; 133 pr = tf->tf_pr; 134 pc = tf->tf_spc; 135 if (pc == 0) { 136 print("calling through null pointer?\n"); 137 pc = tf->tf_pr; 138 } 139 DPRINTF(1, "# trapframe: pc=%lx pr=%lx fp=%lx sp=%lx\n", 140 pc, pr, fp, sp); 141 142 } else if (lwpaddr || lwpid) { 143 struct proc *p; 144 struct lwp *l; 145 struct pcb *pcb; 146 147 if (lwpaddr) { 148 l = (struct lwp *)addr; 149 p = l->l_proc; 150 print("trace: lwp addr %p pid %d ", 151 (void *)addr, p->p_pid); 152 } else { 153 pid_t pid = (pid_t)addr; 154 print("trace: pid %d ", pid); 155 p = db_proc_find(pid); 156 if (p == NULL) { 157 print("not found\n"); 158 return; 159 } 160 l = LIST_FIRST(&p->p_lwps); 161 } 162 KASSERT(l != NULL); 163 print("lid %d", l->l_lid); 164 pcb = lwp_getpcb(l); 165 tf = (struct trapframe *)pcb->pcb_sf.sf_r6_bank; 166 fp = pcb->pcb_sf.sf_r14; 167 sp = pcb->pcb_sf.sf_r15; 168 pr = pcb->pcb_sf.sf_pr; 169 pc = pcb->pcb_sf.sf_pr; 170 print(", fp=%lx, sp=%lx\n", fp, sp); 171 DPRINTF(1, "# lwp: pc=%lx pr=%lx fp=%lx sp=%lx\n", 172 pc, pr, fp, sp); 173 } else { 174 fp = 0; 175 sp = addr; 176 pr = 0; 177 /* 178 * Assume that the frame address (__builtin_frame_address) 179 * passed as an argument is the same level 180 * as this __builtin_return_address(). 181 */ 182 pc = (db_addr_t)__builtin_return_address(0); 183 } 184 185 lastpc = 0; 186 while (count > 0 && pc != 0 && sp != 0) { 187 DPRINTF(2, "# trace: pc=%lx sp=%lx fp=%lx\n", pc, sp, fp); 188 189 /* Are we crossing a trap frame? */ 190 if ((pc & ~PAGE_MASK) == vbr) { 191 struct trapframe trapframe; 192 tf = &trapframe; 193 194 /* r14 in exception vectors points to trap frame */ 195 db_read_bytes((db_addr_t)fp, sizeof(*tf), (char *)tf); 196 pc = tf->tf_spc; 197 pr = tf->tf_pr; 198 fp = tf->tf_r14; 199 sp = tf->tf_r15; 200 201 print("<EXPEVT %03x; SSR=%08x> at ", 202 tf->tf_expevt, tf->tf_ssr); 203 db_printsym(pc, DB_STGY_PROC, print); 204 print("\n"); 205 206 print("[trapframe 0x%lx]\n", fp); 207 dump_trapframe(tf, print); 208 209 /* XXX: don't venture into the userland yet */ 210 if ((tf->tf_ssr & PSL_MD) == 0) 211 break; 212 } else { 213 const char *name; 214 db_expr_t offset; 215 db_sym_t sym; 216 bool found; 217 218 sym = db_search_symbol(pc, DB_STGY_ANY, &offset); 219 if (sym == 0) { 220 print("symbol not found\n"); 221 break; 222 } 223 db_symbol_values(sym, &name, NULL); 224 225 func = pc - offset; 226 227 DPRINTF(1, 228 " (1) func=%lx+%lx, pc=%lx, sp=%lx, fp=%lx\n", 229 func, offset, pc, sp, fp); 230 231 found = db_nextframe(func, pc, &fp, &pr, &sp, print); 232 if (!found && lastpc == pc) 233 break; 234 lastpc = pc; 235 236 DPRINTF(1, " (2) newpc=%lx, newsp=%lx, newfp=%lx\n", 237 pr, sp, fp); 238 239 DPRINTF(1, "sp=%lx ", sp); 240 print("%s() at ", name ? name : ""); 241 db_printsym(pr, DB_STGY_PROC, print); 242 print("\n"); 243 244 pc = pr; 245 pr = 0; 246 } 247 248 count--; 249 } 250 } 251 252 static bool 253 db_nextframe( 254 db_addr_t func, /* in: entry address of current function */ 255 db_addr_t curpc, /* in: current pc in the function */ 256 db_addr_t *fp, /* out: parent fp */ 257 db_addr_t *pr, /* out: parent pr */ 258 db_addr_t *sp, /* in: current sp, out: parent sp */ 259 void (*print)(const char *, ...) __printflike(1, 2)) 260 { 261 int *stack = (void *)*sp; 262 int i, inst, inst2; 263 int depth, prdepth, fpdepth; 264 db_addr_t pc; 265 266 if (__predict_false(db_trace_debug >= 2)) { 267 DPRINTF(2, "%s:%d: START: func=%lx=", __func__, __LINE__, func); 268 db_printsym(func, DB_STGY_PROC, print); 269 DPRINTF(2, " pc=%lx fp=%lx pr=%lx, sp=%lx\n", 270 curpc, *fp, *pr, *sp); 271 } 272 273 pc = func; 274 depth = 0; 275 prdepth = fpdepth = -1; 276 277 if (pc < (db_addr_t)start || pc > (db_addr_t)etext) 278 goto out; 279 280 for (i = 0; i < 30 && pc < curpc; i++) { 281 inst = db_get_value(pc, 2, false); 282 DPRINTF(2, "%s:%d: %lx insn=%04x depth=%d\n", 283 __func__, __LINE__, pc, inst, depth); 284 pc += 2; 285 286 if (inst == 0x000b) /* rts - asm routines w/out frame */ 287 break; 288 289 if (inst == 0x6ef3) /* mov r15,r14 -- end of prologue */ 290 break; 291 292 if (inst == 0x4f22) { /* sts.l pr,@-r15 */ 293 prdepth = depth; 294 depth++; 295 continue; 296 } 297 if (inst == 0x2fe6) { /* mov.l r14,@-r15 */ 298 fpdepth = depth; 299 depth++; 300 continue; 301 } 302 if ((inst & 0xff0f) == 0x2f06) { /* mov.l r?,@-r15 */ 303 depth++; 304 continue; 305 } 306 if ((inst & 0xff00) == 0x7f00) { /* add #n,r15 */ 307 int8_t n = inst & 0xff; 308 309 if (n >= 0) { 310 /* XXX: in epilogue? ignore it */ 311 DPRINTF(2, 312 "%s:%d: %lx: add #%d,r15 (n > 0) ignored\n", 313 __func__, __LINE__, pc - 2, n); 314 break; 315 } 316 317 depth += -n / 4; 318 continue; 319 } 320 if ((inst & 0xf000) == 0x9000) { 321 inst2 = db_get_value(pc, 2, false); 322 if (((inst2 & 0xff0f) == 0x3f08) && 323 ((inst & 0x0f00) == ((inst2 & 0x00f0) << 4))) { 324 325 /* mov <disp>,r?; sub r?,r15 */ 326 unsigned int disp = (int)(inst & 0xff); 327 vaddr_t addr; 328 int val; 329 330 addr = pc + (4 - 2) + (disp << 1); 331 db_read_bytes(addr, sizeof(val), (char *)&val); 332 if ((val & 0x00008000) == 0) 333 val &= 0x0000ffff; 334 else 335 val |= 0xffff0000; 336 depth += (val / 4); 337 338 pc += 2; 339 continue; 340 } 341 } 342 343 if (__predict_false(db_trace_debug > 1)) { 344 print(" unknown insn at "); 345 db_printsym(pc - 2, DB_STGY_PROC, print); 346 print(":\t"); 347 db_disasm(pc - 2, 0);/* XXX: always uses db_printf */ 348 } 349 } 350 351 out: 352 DPRINTF(2, "%s:%d: fpdepth=%d prdepth=%d depth=%d sp=%lx->%lx\n", 353 __func__, __LINE__, fpdepth, prdepth, depth, *sp, *sp - depth * 4); 354 355 /* dump stack */ 356 if (__predict_false(db_trace_debug > 2) && 357 ((fpdepth != -1) || (prdepth != -1))) { 358 print("%s:%d: func=%lx pc=%lx\n", 359 __func__, __LINE__, func, curpc); 360 361 for (int j = 0; j < prdepth + 32; j++) { 362 uint32_t v; 363 364 db_read_bytes((db_addr_t)&stack[j], 365 sizeof(v), (char *)&v); 366 print(" STACK[%2d]: %p: %08x ", 367 j, &stack[j], v); 368 db_printsym(v, DB_STGY_PROC, print); 369 if (j == (depth - prdepth - 1)) 370 print(" # = pr"); 371 if (j == (depth - fpdepth - 1)) 372 print(" # = fp"); 373 print("\n"); 374 } 375 } 376 377 /* fetch fp and pr if exists in stack */ 378 if (fpdepth != -1) 379 db_read_bytes((db_addr_t)&stack[depth - fpdepth - 1], 380 sizeof(*fp), (char *)fp); 381 if (prdepth != -1) 382 db_read_bytes((db_addr_t)&stack[depth - prdepth - 1], 383 sizeof(*pr), (char *)pr); 384 385 /* adjust stack pointer, and update */ 386 stack += depth; 387 *sp = (db_addr_t)stack; 388 389 if (__predict_false(db_trace_debug >= 2)) { 390 DPRINTF(2, "%s:%d: RESULT: fp=%lx pr=%lx(", 391 __func__, __LINE__, *fp, *pr); 392 db_printsym(*pr, DB_STGY_PROC, print); 393 DPRINTF(2, ") sp=%lx\n", *sp); 394 } 395 396 if ((prdepth == -1) && (fpdepth == -1) && (depth == 0)) 397 return false; 398 return true; 399 } 400