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