1 /* $NetBSD: db_trace.c,v 1.36 2002/05/14 00:14:53 matt Exp $ */ 2 3 /* 4 * Mach Operating System 5 * Copyright (c) 1992 Carnegie Mellon University 6 * All Rights Reserved. 7 * 8 * Permission to use, copy, modify and distribute this software and its 9 * documentation is hereby granted, provided that both the copyright 10 * notice and this permission notice appear in all copies of the 11 * software, derivative works or modified versions, and any portions 12 * thereof, and that both notices appear in supporting documentation. 13 * 14 * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS" 15 * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR 16 * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. 17 * 18 * Carnegie Mellon requests users of this software to return to 19 * 20 * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU 21 * School of Computer Science 22 * Carnegie Mellon University 23 * Pittsburgh PA 15213-3890 24 * 25 * any improvements or extensions that they make and grant Carnegie Mellon 26 * the rights to redistribute these changes. 27 */ 28 29 #include <sys/param.h> 30 #include <sys/proc.h> 31 #include <sys/user.h> 32 #include <sys/systm.h> 33 34 #include <machine/db_machdep.h> 35 36 #include <ddb/db_interface.h> 37 #include <ddb/db_output.h> 38 #include <ddb/db_access.h> 39 #include <ddb/db_sym.h> 40 #include <ddb/db_extern.h> 41 #include <ddb/db_variables.h> 42 43 /* 44 * Register list 45 */ 46 static int db_var_short __P((const struct db_variable *, db_expr_t *, int)); 47 48 const struct db_variable db_regs[] = { 49 /* D0-D7 */ 50 { "d0", (long *)&ddb_regs.tf_regs[0], FCN_NULL }, 51 { "d1", (long *)&ddb_regs.tf_regs[1], FCN_NULL }, 52 { "d2", (long *)&ddb_regs.tf_regs[2], FCN_NULL }, 53 { "d3", (long *)&ddb_regs.tf_regs[3], FCN_NULL }, 54 { "d4", (long *)&ddb_regs.tf_regs[4], FCN_NULL }, 55 { "d5", (long *)&ddb_regs.tf_regs[5], FCN_NULL }, 56 { "d6", (long *)&ddb_regs.tf_regs[6], FCN_NULL }, 57 { "d7", (long *)&ddb_regs.tf_regs[7], FCN_NULL }, 58 /* A0-A7 */ 59 { "a0", (long *)&ddb_regs.tf_regs[8+0], FCN_NULL }, 60 { "a1", (long *)&ddb_regs.tf_regs[8+1], FCN_NULL }, 61 { "a2", (long *)&ddb_regs.tf_regs[8+2], FCN_NULL }, 62 { "a3", (long *)&ddb_regs.tf_regs[8+3], FCN_NULL }, 63 { "a4", (long *)&ddb_regs.tf_regs[8+4], FCN_NULL }, 64 { "a5", (long *)&ddb_regs.tf_regs[8+5], FCN_NULL }, 65 { "a6", (long *)&ddb_regs.tf_regs[8+6], FCN_NULL }, 66 { "sp", (long *)&ddb_regs.tf_regs[8+7], FCN_NULL }, 67 /* misc. */ 68 { "pc", (long *)&ddb_regs.tf_pc, FCN_NULL }, 69 { "sr", (long *)&ddb_regs.tf_sr, db_var_short } 70 }; 71 const struct db_variable * const db_eregs = db_regs + sizeof(db_regs)/sizeof(db_regs[0]); 72 73 static int 74 db_var_short(varp, valp, op) 75 const struct db_variable *varp; 76 db_expr_t *valp; 77 int op; 78 { 79 if (op == DB_VAR_GET) 80 *valp = (db_expr_t) *((short*)varp->valuep); 81 else 82 *((short*)varp->valuep) = (short) *valp; 83 return(0); 84 } 85 86 #define MAXINT 0x7fffffff 87 88 extern struct pcb *curpcb; 89 #define INKERNEL(va,pcb) (((u_int)(va) > (u_int)(pcb)) && \ 90 ((u_int)(va) < ((u_int)(pcb) + USPACE))) 91 92 #define get(addr, space) \ 93 (db_get_value((db_addr_t)(addr), sizeof(int), FALSE)) 94 #define get16(addr, space) \ 95 (db_get_value((db_addr_t)(addr), sizeof(u_short), FALSE)) 96 97 #define offsetof(type, member) ((size_t)(&((type *)0)->member)) 98 99 #define NREGISTERS 16 100 101 struct stackpos { 102 int k_pc; 103 int k_fp; 104 int k_nargs; 105 int k_entry; 106 int k_caller; 107 int k_flags; 108 int k_regloc[NREGISTERS]; 109 }; 110 111 static void findentry __P((struct stackpos *, void (*)(const char *, ...))); 112 static void findregs __P((struct stackpos *, db_addr_t)); 113 static int nextframe __P((struct stackpos *, struct pcb *, int, 114 void (*)(const char *, ...))); 115 static void stacktop __P((db_regs_t *, struct stackpos *, 116 void (*)(const char *, ...))); 117 118 119 #define FR_SAVFP 0 120 #define FR_SAVPC 4 121 122 static void 123 stacktop(regs, sp, pr) 124 db_regs_t *regs; 125 struct stackpos *sp; 126 void (*pr) __P((const char *, ...)); 127 { 128 int i; 129 130 /* Note: leave out a6, a7 */ 131 for (i = 0; i < (8+6); i++) { 132 sp->k_regloc[i] = (int) ®s->tf_regs[i]; 133 } 134 135 sp->k_fp = get(®s->tf_regs[8+6], 0); 136 /* skip sp (a7) */ 137 sp->k_pc = get(®s->tf_pc, 0); 138 sp->k_flags = 0; 139 140 findentry(sp, pr); 141 } 142 143 144 /* 145 * The VAX has a very nice calling convention, and it is quite easy to 146 * find saved registers, and the number of parameters. We are not nearly 147 * so lucky. We must grub around in code for much of this information 148 * (remember the PDP-11?), and the saved register list seems to be 149 * especially hard to find. 150 */ 151 152 #define HIWORD 0xffff0000 153 #define LOWORD 0x0000ffff 154 #define LINKLA6 0x480e0000 /* linkl a6,#x */ 155 #define LINKWA6 0x4e560000 /* linkw a6,#x */ 156 #define ADDLSP 0xdffc0000 /* addl #x,sp */ 157 #define ADDWSP 0xdefc0000 /* addw #x,sp */ 158 #define LEASP 0x4fef0000 /* lea sp@(x),sp*/ 159 #define TSTBSP 0x4a2f0000 /* tstb sp@(x) */ 160 #define INSMSK 0xfff80000 161 #define MOVLSP 0x2e800000 /* movl dx,sp@ */ 162 #define MOVLD0 0x20000000 /* movl d0,dx */ 163 #define MOVLA0 0x20400000 /* movl d0,ax */ 164 #define MVLMSK 0xf1ff0000 165 #define MOVEML 0x48d70000 /* moveml #x,sp@ */ 166 #define JSR 0x4eb80000 /* jsr x.[WL] */ 167 #define JSRPC 0x4eba0000 /* jsr PC@( ) */ 168 #define LONGBIT 0x00010000 169 #define BSR 0x61000000 /* bsr x */ 170 #define BSRL 0x61ff0000 /* bsrl x */ 171 #define BYTE3 0x0000ff00 172 #define LOBYTE 0x000000ff 173 #define ADQMSK 0xf1ff0000 174 #define ADDQSP 0x508f0000 /* addql #x,sp */ 175 #define ADDQWSP 0x504f0000 /* addqw #x,sp */ 176 177 struct nlist * trampsym = 0; 178 struct nlist * funcsym = 0; 179 180 static int 181 nextframe(sp, pcb, kerneltrace, pr) 182 struct stackpos *sp; 183 struct pcb *pcb; 184 int kerneltrace; 185 void (*pr) __P((const char *, ...)); 186 { 187 int i; 188 db_addr_t addr; 189 db_addr_t calladdr; 190 db_addr_t oldfp = sp->k_fp; 191 192 /* 193 * Find our entry point. Then find out 194 * which registers we saved, and map them. 195 * Our entry point is the address our caller called. 196 */ 197 198 calladdr = sp->k_caller; 199 addr = sp->k_entry; 200 if (addr == MAXINT) { 201 202 /* 203 * we don't know what registers are involved here, 204 * invalidate them all. 205 */ 206 for (i = 0; i < NREGISTERS; i++) 207 sp->k_regloc[i] = -1; 208 } else 209 findregs(sp, addr); 210 211 /* find caller's pc and fp */ 212 sp->k_pc = calladdr; 213 sp->k_fp = get(sp->k_fp + FR_SAVFP, DSP); 214 215 /* 216 * Now that we have assumed the identity of our caller, find 217 * how many longwords of argument WE were called with. 218 */ 219 sp->k_flags = 0; 220 221 /* 222 * Don't dig around in user stack to find no. of args and 223 * entry point if just tracing the kernel 224 */ 225 if (kerneltrace && !INKERNEL(sp->k_fp, pcb)) { 226 sp->k_nargs = 0; 227 sp->k_entry = MAXINT; 228 } else 229 findentry(sp, pr); 230 231 if (sp->k_fp == 0 || oldfp == sp->k_fp) 232 return 0; 233 return (sp->k_fp); 234 } 235 236 static void 237 findentry(sp, pr) 238 struct stackpos *sp; 239 void (*pr) __P((const char *, ...)); 240 { 241 /* 242 * Set the k_nargs and k_entry fields in the stackpos structure. This 243 * is called from stacktop() and from nextframe(). Our caller will do 244 * an addq or addl or addw to sp just after we return to pop off our 245 * arguments. Find that instruction and extract the value. 246 */ 247 int instruc; 248 int val; 249 db_addr_t addr, nextword; 250 label_t db_jmpbuf; 251 label_t *savejmp; 252 253 savejmp = db_recover; 254 db_recover = &db_jmpbuf; 255 if (setjmp(&db_jmpbuf)) { 256 /* oops -- we touched something we ought not to have */ 257 /* cannot trace caller of "start" */ 258 sp->k_entry = MAXINT; 259 sp->k_nargs = 0; 260 db_recover = savejmp; 261 return; 262 } 263 264 addr = get(sp->k_fp + FR_SAVPC, DSP); 265 if (addr == 0) { 266 /* oops -- we touched something we ought not to have */ 267 /* cannot trace caller of "start" */ 268 sp->k_entry = MAXINT; 269 sp->k_nargs = 0; 270 db_recover = savejmp; 271 return; 272 } 273 instruc = get(addr - 6, ISP); 274 nextword = get(addr - 4, ISP); 275 276 db_recover = savejmp; 277 278 if ((instruc & HIWORD) == (JSR | LONGBIT)) { 279 /* longword offset here */ 280 sp->k_caller = addr - 6; 281 sp->k_entry = nextword; 282 } else if ((instruc & HIWORD) == BSRL) { 283 /* longword self-relative offset */ 284 sp->k_caller = addr - 6; 285 sp->k_entry = nextword + (addr - 4); 286 } else { 287 instruc = nextword; 288 if ((instruc & HIWORD) == JSR) { 289 /* short word offset */ 290 sp->k_caller = addr - 4; 291 sp->k_entry = instruc & LOWORD; 292 } else if ((instruc & HIWORD) == BSR) { 293 /* short word, self-relative offset */ 294 sp->k_caller = addr - 4; 295 sp->k_entry = (addr - 2) + (short)(instruc & LOWORD); 296 } else if ((instruc & HIWORD) == JSRPC) { 297 /* PC-relative, short word offset */ 298 sp->k_caller = addr - 4; 299 sp->k_entry = (addr - 2) + (instruc & LOWORD); 300 } else { 301 if ((instruc & BYTE3) == (BSR >> 16)) { 302 /* byte, self-relative offset */ 303 sp->k_caller = addr - 2; 304 sp->k_entry = addr + (char)(instruc & LOBYTE); 305 } else { 306 /* was a call through a proc parameter */ 307 sp->k_caller = addr - 2; 308 sp->k_entry = MAXINT; 309 } 310 } 311 } 312 instruc = get(addr, ISP); 313 /* on bad days, the compiler dumps a register move here */ 314 if ((instruc & MVLMSK) == MOVLA0 || 315 (instruc & MVLMSK) == MOVLD0) 316 instruc = get(addr += 2, ISP); 317 if ((instruc & ADQMSK) == ADDQSP || 318 (instruc & ADQMSK) == ADDQWSP) { 319 val = 0; 320 do { 321 int n; 322 n = (instruc >> (16+9)) & 07; 323 if (n == 0) 324 n = 8; 325 val += n; 326 instruc = get(addr += 2, ISP); 327 } while ((instruc & ADQMSK) == ADDQSP || 328 (instruc & ADQMSK) == ADDQWSP); 329 } else if ((instruc & HIWORD) == ADDLSP) 330 val = get(addr + 2, ISP); 331 else if ((instruc & HIWORD) == ADDWSP || 332 (instruc & HIWORD) == LEASP) 333 val = instruc & LOWORD; 334 else 335 val = 20; 336 sp->k_nargs = val / 4; 337 } 338 339 /* 340 * Look at the procedure prolog of the current called procedure. 341 * Figure out which registers we saved, and where they are 342 */ 343 static void 344 findregs(sp, addr) 345 struct stackpos *sp; 346 db_addr_t addr; 347 { 348 long instruc, val, i; 349 int regp; 350 351 regp = 0; 352 instruc = get(addr, ISP); 353 if ((instruc & HIWORD) == LINKLA6) { 354 instruc = get(addr + 2, ISP); 355 addr += 6; 356 regp = sp->k_fp + instruc; 357 } else if ((instruc & HIWORD) == LINKWA6) { 358 addr += 4; 359 if ((instruc &= LOWORD) == 0) { 360 /* look for addl */ 361 instruc = get(addr, ISP); 362 if ((instruc & HIWORD) == ADDLSP) { 363 instruc = get(addr + 2, ISP); 364 addr += 6; 365 } 366 /* else frame is really size 0 */ 367 } else { 368 /* link offset was non-zero -- sign extend it */ 369 instruc <<= 16; 370 instruc >>= 16; 371 } 372 /* we now have the negative frame size */ 373 regp = sp->k_fp + instruc; 374 } 375 376 /* find which registers were saved */ 377 /* (expecting probe instruction next) */ 378 instruc = get(addr, ISP); 379 if ((instruc & HIWORD) == TSTBSP) 380 addr += 4; 381 382 /* now we expect either a moveml or a movl */ 383 instruc = get(addr, ISP); 384 if ((instruc & INSMSK) == MOVLSP) { 385 /* only saving one register */ 386 i = (instruc >> 16) & 07; 387 sp->k_regloc[i] = regp; 388 } else if ((instruc & HIWORD) == MOVEML) { 389 /* saving multiple registers or unoptimized code */ 390 val = instruc & LOWORD; 391 i = 0; 392 while (val) { 393 if (val & 1) { 394 sp->k_regloc[i] = regp; 395 regp += sizeof(int); 396 } 397 val >>= 1; 398 i++; 399 } 400 } 401 /* else no registers saved */ 402 } 403 404 /* 405 * Frame tracing. 406 */ 407 void 408 db_stack_trace_print(addr, have_addr, count, modif, pr) 409 db_expr_t addr; 410 int have_addr; 411 db_expr_t count; 412 char *modif; 413 void (*pr) __P((const char *, ...)); 414 { 415 int i, nargs; 416 long val; 417 db_addr_t regp; 418 char * name; 419 struct stackpos pos; 420 struct pcb *pcb = curpcb; 421 boolean_t kernel_only = TRUE; 422 boolean_t trace_thread = FALSE; 423 int fault_pc = 0; 424 425 { 426 char *cp = modif; 427 char c; 428 429 while ((c = *cp++) != 0) 430 if (c == 't') 431 trace_thread = TRUE; 432 else 433 if (c == 'u') 434 kernel_only = FALSE; 435 } 436 437 if (!have_addr) 438 stacktop(&ddb_regs, &pos, pr); 439 else { 440 if (trace_thread) { 441 struct proc *p; 442 struct user *u; 443 (*pr)("trace: pid %d ", (int)addr); 444 p = pfind(addr); 445 if (p == NULL) { 446 (*pr)("not found\n"); 447 return; 448 } 449 if (!(p->p_flag & P_INMEM)) { 450 (*pr)("swapped out\n"); 451 return; 452 } 453 u = p->p_addr; 454 pos.k_fp = u->u_pcb.pcb_regs[PCB_REGS_FP]; 455 /* 456 * Note: The following only works because cpu_switch() 457 * doesn't push anything on the stack before it saves 458 * the process' context in the pcb. 459 */ 460 pos.k_pc = get(u->u_pcb.pcb_regs[PCB_REGS_SP], DSP); 461 (*pr)("at %p\n", (void *)pos.k_fp); 462 pcb = &u->u_pcb; 463 } else { 464 pos.k_fp = addr; 465 pos.k_pc = MAXINT; 466 } 467 468 pos.k_flags = 0; 469 pos.k_nargs = 0; 470 pos.k_entry = MAXINT; 471 472 for (i = 0; i < NREGISTERS; i++) 473 pos.k_regloc[i] = 0; 474 475 findentry(&pos, pr); 476 } 477 478 while (count) { 479 count--; 480 481 /* HACK */ 482 if (pos.k_pc == MAXINT) { 483 name = "?"; 484 pos.k_pc = 0; 485 val = MAXINT; 486 } else { 487 db_find_sym_and_offset(pos.k_pc, &name, &val); 488 if (name == 0) { 489 name = "?"; 490 val = MAXINT; 491 } 492 } 493 494 /* 495 * Since faultstkadj doesn't set up a valid stack frame, 496 * we would assume it was the source of the fault. To 497 * get around this we peek at the fourth argument of 498 * "trap()" (the stack frame at the time of the fault) 499 * to determine the _real_ value of PC when things wen 500 * wrong. 501 * 502 * NOTE: If the argument list for 'trap()' ever changes, 503 * we lose. 504 */ 505 if (strcmp(___STRING(_C_LABEL(trap)), name) == 0) { 506 int tfp; 507 508 /* Point to 'trap()'s 4th argument (frame structure) */ 509 tfp = pos.k_fp + FR_SAVFP + 4 + (4 * 4); 510 511 /* Determine if fault was from kernel or user mode */ 512 regp = tfp + offsetof(struct frame, f_sr); 513 if (!USERMODE(get16(regp, DSP))) { 514 515 /* 516 * Definitely a kernel mode fault, 517 * so get the PC at the time of the fault. 518 */ 519 regp = tfp + offsetof(struct frame, f_pc); 520 fault_pc = get(regp, DSP); 521 } 522 } else if (fault_pc) { 523 if (strcmp("faultstkadj", name) == 0) { 524 db_find_sym_and_offset(fault_pc, &name, &val); 525 if (name == 0) { 526 name = "?"; 527 val = MAXINT; 528 } 529 } 530 fault_pc = 0; 531 } 532 533 (*pr)("%s", name); 534 if (pos.k_entry != MAXINT && name) { 535 char * entry_name; 536 long e_val; 537 538 db_find_sym_and_offset(pos.k_entry, &entry_name, 539 &e_val); 540 if (entry_name != 0 && entry_name != name && 541 e_val != val) { 542 (*pr)("(?)\n%s", entry_name); 543 } 544 } 545 (*pr)("("); 546 regp = pos.k_fp + FR_SAVFP + 4; 547 if ((nargs = pos.k_nargs)) { 548 while (nargs--) { 549 (*pr)("%lx", get(regp += 4, DSP)); 550 if (nargs) 551 (*pr)(","); 552 } 553 } 554 if (val == MAXINT) 555 (*pr)(") at %x\n", pos.k_pc); 556 else 557 (*pr)(") + %lx\n", val); 558 559 /* 560 * Stop tracing if frame ptr no longer points into kernel 561 * stack. 562 */ 563 if (kernel_only && !INKERNEL(pos.k_fp, pcb)) 564 break; 565 if (nextframe(&pos, pcb, kernel_only, pr) == 0) 566 break; 567 } 568 } 569 570