1/* $NetBSD: db_trace.c,v 1.24 2024/02/07 04:20:26 msaitoh Exp $ */ 2 3/* 4 * Copyright (c) 2017 Ryo Shimizu 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 17 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 18 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19 * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, 20 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 21 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 22 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 24 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 25 * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 26 * POSSIBILITY OF SUCH DAMAGE. 27 */ 28 29#include <sys/cdefs.h> 30 31__KERNEL_RCSID(0, "$NetBSD: db_trace.c,v 1.24 2024/02/07 04:20:26 msaitoh Exp $"); 32 33#include <sys/param.h> 34#include <sys/bitops.h> 35#include <sys/proc.h> 36 37#include <aarch64/db_machdep.h> 38#include <aarch64/machdep.h> 39#include <aarch64/armreg.h> 40#include <aarch64/vmparam.h> 41 42#include <arm/cpufunc.h> 43 44#include <uvm/uvm_extern.h> 45 46#include <ddb/db_access.h> 47#include <ddb/db_command.h> 48#include <ddb/db_output.h> 49#include <ddb/db_variables.h> 50#include <ddb/db_sym.h> 51#include <ddb/db_proc.h> 52#include <ddb/db_lwp.h> 53#include <ddb/db_extern.h> 54#include <ddb/db_interface.h> 55 56#ifdef _KERNEL 57extern char el0_trap[]; 58extern char el1_trap[]; 59#else 60/* see also usr.sbin/crash/arch/aarch64.c */ 61extern vaddr_t el0_trap; 62extern vaddr_t el1_trap; 63#endif 64 65#define MAXBACKTRACE 128 /* against infinite loop */ 66 67 68__CTASSERT(VM_MIN_ADDRESS == 0); 69#define IN_USER_VM_ADDRESS(addr) \ 70 ((addr) < VM_MAX_ADDRESS) 71#define IN_KERNEL_VM_ADDRESS(addr) \ 72 ((VM_MIN_KERNEL_ADDRESS <= (addr)) && ((addr) < VM_MAX_KERNEL_ADDRESS)) 73 74static void 75pr_frame(struct trapframe *tf, void (*pr)(const char *, ...) __printflike(1, 2)) 76{ 77 struct trapframe tf_buf; 78 79 db_read_bytes((db_addr_t)tf, sizeof(tf_buf), (char *)&tf_buf); 80 81 if (tf_buf.tf_sp == 0) { 82 (*pr)("---- switchframe %p (%zu bytes) ----\n", 83 tf, sizeof(*tf)); 84 dump_switchframe(tf, pr); 85 } else { 86#ifdef _KERNEL 87 (*pr)("---- %s: trapframe %p (%zu bytes) ----\n", 88 (tf_buf.tf_esr == (uint64_t)-1) ? "Interrupt" : 89 eclass_trapname(__SHIFTOUT(tf_buf.tf_esr, ESR_EC)), 90 tf, sizeof(*tf)); 91#else 92 (*pr)("---- trapframe %p (%zu bytes) ----\n", tf, sizeof(*tf)); 93#endif 94 dump_trapframe(tf, pr); 95 } 96 (*pr)("------------------------" 97 "------------------------\n"); 98} 99 100static bool __unused 101is_lwp(void *p) 102{ 103 lwp_t *lwp; 104 105 for (lwp = db_lwp_first(); lwp != NULL; lwp = db_lwp_next(lwp)) { 106 if (lwp == p) 107 return true; 108 } 109 return false; 110} 111 112static vaddr_t 113db_lwp_getuarea(lwp_t *l) 114{ 115 void *laddr; 116 db_read_bytes((db_addr_t)&l->l_addr, sizeof(laddr), (char *)&laddr); 117 if (laddr == 0) 118 return 0; 119 return (vaddr_t)((char *)laddr - UAREA_PCB_OFFSET); 120} 121 122static const char * 123getlwpnamebysp(uint64_t sp) 124{ 125 static char c_name[MAXCOMLEN]; 126 lwp_t *lwp; 127 struct proc *pp; 128 char *lname; 129 130 for (lwp = db_lwp_first(); lwp != NULL; lwp = db_lwp_next(lwp)) { 131 uint64_t uarea = db_lwp_getuarea(lwp); 132 if ((uarea <= sp) && (sp < (uarea + USPACE))) { 133 db_read_bytes((db_addr_t)&lwp->l_name, sizeof(lname), 134 (char *)&lname); 135 if (lname != NULL) { 136 db_read_bytes((db_addr_t)lname, sizeof(c_name), 137 c_name); 138 return c_name; 139 } 140 db_read_bytes((db_addr_t)&lwp->l_proc, sizeof(pp), 141 (char *)&pp); 142 if (pp != NULL) { 143 db_read_bytes((db_addr_t)&pp->p_comm, 144 sizeof(c_name), c_name); 145 return c_name; 146 } 147 break; 148 } 149 } 150 return "unknown"; 151} 152 153#define TRACEFLAG_LOOKUPLWP 0x00000001 154#define TRACEFLAG_USERSPACE 0x00000002 155 156static void 157pr_traceaddr(const char *prefix, uint64_t frame, uint64_t pc, int flags, 158 void (*pr)(const char *, ...) __printflike(1, 2)) 159{ 160 db_expr_t offset; 161 db_sym_t sym; 162 const char *name; 163 164 sym = db_search_symbol(pc, DB_STGY_ANY, &offset); 165 if (sym != DB_SYM_NULL) { 166 db_symbol_values(sym, &name, NULL); 167 168 if (flags & TRACEFLAG_LOOKUPLWP) { 169 (*pr)("%s %016lx %s %s() at %016lx ", 170 prefix, frame, getlwpnamebysp(frame), name, pc); 171 } else { 172 (*pr)("%s %016lx %s() at %016lx ", 173 prefix, frame, name, pc); 174 } 175 db_printsym(pc, DB_STGY_PROC, pr); 176 (*pr)("\n"); 177 } else { 178 if (flags & TRACEFLAG_LOOKUPLWP) { 179 (*pr)("%s %016lx %s ?() at %016lx\n", 180 prefix, frame, getlwpnamebysp(frame), pc); 181 } else { 182 (*pr)("%s %016lx ?() at %016lx\n", prefix, frame, pc); 183 } 184 } 185} 186 187static __inline uint64_t 188SignExtend(int bitwidth, uint64_t imm, unsigned int multiply) 189{ 190 const uint64_t signbit = ((uint64_t)1 << (bitwidth - 1)); 191 const uint64_t immmax = signbit << 1; 192 193 if (imm & signbit) 194 imm -= immmax; 195 return imm * multiply; 196} 197 198static __inline uint64_t 199ZeroExtend(int bitwidth, uint64_t imm, unsigned int multiply) 200{ 201 return imm * multiply; 202} 203 204/* rotate right. if n < 0, rotate left. */ 205static __inline uint64_t 206rotate(int bitwidth, uint64_t v, int n) 207{ 208 uint64_t result; 209 210 n &= (bitwidth - 1); 211 result = (((v << (bitwidth - n)) | (v >> n))); 212 if (bitwidth < 64) 213 result &= ((1ULL << bitwidth) - 1); 214 return result; 215} 216 217static __inline uint64_t 218DecodeBitMasks(uint64_t sf, uint64_t n, uint64_t imms, uint64_t immr) 219{ 220 const int bitwidth = (sf == 0) ? 32 : 64; 221 uint64_t result; 222 int esize, len; 223 224 len = fls64((n << 6) + (~imms & 0x3f)) - 1; 225 esize = (1 << len); 226 imms &= (esize - 1); 227 immr &= (esize - 1); 228 result = rotate(esize, (1ULL << (imms + 1)) - 1, immr); 229 while (esize < bitwidth) { 230 result |= (result << esize); 231 esize <<= 1; 232 } 233 if (sf == 0) 234 result &= ((1ULL << bitwidth) - 1); 235 return result; 236} 237 238static int 239analyze_func(db_addr_t func_entry, db_addr_t pc, db_addr_t sp, 240 db_addr_t *lrp, vsize_t *stacksizep, 241 void (*pr)(const char *, ...) __printflike(1, 2)) 242{ 243 vsize_t ssize = 0, lr_off = 0; 244 db_addr_t lr = 0; 245 uint64_t alloc_by_Xn_kvalue = 0; 246 uint64_t alloc_by_Xn_kmask = 0; 247 int alloc_by_Xn_reg = -1; 248 bool found_lr_off = false; 249 bool func_entry_autodetect = false; 250 251#define MAX_BACKTRACK_ANALYZE_INSN (1024 * 4) 252 if (func_entry == 0) { 253 if (pc > MAX_BACKTRACK_ANALYZE_INSN) 254 func_entry = pc - MAX_BACKTRACK_ANALYZE_INSN; 255 else 256 func_entry = 4; 257 func_entry_autodetect = true; 258 }; 259 260 261 /* 262 * Locate the following instructions that allocates a stackframe. 263 * Only the following patterns are supported: 264 * 265 * sub sp, sp, #ALLOCSIZE -> ssize += ALLOCSIZE 266 * sub sp, sp, #ALLOCSIZE, lsl #12 -> ssize += (ALLOCSIZE << 12) 267 * 268 * mov xN, #ALLOCSIZE1 269 * (movk xN, #ALLOCSIZE2, lsl #xx) 270 * sub sp, sp, xN -> ssize += ALLOCSIZE 271 * 272 * stp x30, x??, [sp, #-ALLOCSIZE]! -> ssize =+ ALLOCSIZE, lr_off=0 273 * stp x??, x30, [sp, #-ALLOCSIZE]! -> ssize =+ ALLOCSIZE, lr_off=8 274 * stp x??, x??, [sp, #-ALLOCSIZE]! -> ssize =+ ALLOCSIZE 275 * 276 * str x30, [sp, #-ALLOCSIZE]! -> ssize =+ ALLOCSIZE, lr_off=0 277 * 278 * stp x30, x??, [sp, #LR_OFF] -> lr_off = LR_OFF 279 * stp x??, x30, [sp, #LR_OFF] -> lr_off = LR_OFF+8 280 * str x30, [sp, #LR_OFF] -> lr_off = LR_OFF 281 */ 282 283/* #define BACKTRACE_ANALYZE_DEBUG */ 284#ifdef BACKTRACE_ANALYZE_DEBUG 285#define TRACE_DEBUG(fmt, args...) pr("BACKTRACE: " fmt, ## args) 286#else 287#define TRACE_DEBUG(args...) __nothing 288#endif 289 290 TRACE_DEBUG("func_entry=%016lx\n", func_entry); 291 TRACE_DEBUG(" pc=%016lx (+%#lx)\n", pc, pc - func_entry); 292 TRACE_DEBUG(" sp=%016lx\n", sp); 293 294 for (pc -= 4; pc >= func_entry; pc -= 4) { 295 uint32_t insn; 296 297 db_read_bytes(pc, sizeof(insn), (char *)&insn); 298 if (insn == 0) 299 break; 300 LE32TOH(insn); 301 302 TRACE_DEBUG("INSN: %016lx: %04x\n", pc, insn); 303 304 /* "ret", "eret", or "paciasp" to detect function entry */ 305 if (func_entry_autodetect && ( 306 insn == 0xd65f03e0 || /* "ret" */ 307 insn == 0xd69f03e0 || /* "eret" */ 308 insn == 0xd503233f)) /* "paciasp" */ 309 break; 310 311 /* "sub sp,sp,#imm" or "sub sp,sp,#imm,lsl #12" */ 312 if ((insn & 0xff8003ff) == 0xd10003ff) { 313 unsigned int sh = (insn >> 22) & 1; 314 uint64_t imm12 = 315 ZeroExtend(12, (insn >> 10) & 0xfff, 1); 316 if (sh) 317 imm12 <<= 12; 318 ssize += imm12; 319 TRACE_DEBUG("sub sp,sp,#%lu\n", imm12); 320 continue; 321 } 322 323 /* sub sp,sp,Xn */ 324 if ((insn & 0xffe0ffff) == 0xcb2063ff) { 325 alloc_by_Xn_reg = (insn >> 16) & 0x1f; 326 alloc_by_Xn_kvalue = 0; 327 alloc_by_Xn_kmask = 0; 328 TRACE_DEBUG("sub sp,sp,x%d\n", alloc_by_Xn_reg); 329 continue; 330 } 331 if (alloc_by_Xn_reg >= 0) { 332 /* movk xN,#ALLOCSIZE2,lsl #xx */ 333 if ((insn & 0xff80001f) == 334 (0xf2800000 | alloc_by_Xn_reg)) { 335 int hw = (insn >> 21) & 3; 336 alloc_by_Xn_kvalue = ZeroExtend(16, 337 (insn >> 5) & 0xffff, 1) << (hw * 16); 338 alloc_by_Xn_kmask = (0xffffULL << (hw * 16)); 339 TRACE_DEBUG("movk x%d,#%#lx,lsl #%d\n", 340 alloc_by_Xn_reg, alloc_by_Xn_kvalue, 341 hw * 16); 342 continue; 343 } 344 345 /* (orr) mov xN,#ALLOCSIZE1 */ 346 if ((insn & 0xff8003ff) == 347 (0xb20003e0 | alloc_by_Xn_reg)) { 348 uint64_t n = (insn >> 22) & 1; 349 uint64_t immr = (insn >> 16) & 0x3f; 350 uint64_t imms = (insn >> 10) & 0x3f; 351 uint64_t v = DecodeBitMasks(1, n, imms, immr); 352 TRACE_DEBUG("(orr) mov x%d,#%#lx\n", 353 alloc_by_Xn_reg, v); 354 ssize += v; 355 alloc_by_Xn_reg = -1; 356 continue; 357 } 358 359 /* (movz) mov xN,#ALLOCSIZE1 */ 360 if ((insn & 0xffe0001f) == 361 (0xd2800000 | alloc_by_Xn_reg)) { 362 uint64_t v = 363 ZeroExtend(16, (insn >> 5) & 0xffff, 1); 364 TRACE_DEBUG("(movz) mov x%d,#%#lx\n", 365 alloc_by_Xn_reg, v); 366 v &= ~alloc_by_Xn_kmask; 367 v |= alloc_by_Xn_kvalue; 368 ssize += v; 369 alloc_by_Xn_reg = -1; 370 continue; 371 } 372 /* (movn) mov xN,#ALLOCSIZE1 */ 373 if ((insn & 0xffe0001f) == 374 (0x92800000 | alloc_by_Xn_reg)) { 375 uint64_t v = 376 ~ZeroExtend(16, (insn >> 5) & 0xffff, 1); 377 TRACE_DEBUG("(movn) mov x%d,#%#lx\n", 378 alloc_by_Xn_reg, v); 379 v &= ~alloc_by_Xn_kmask; 380 v |= alloc_by_Xn_kvalue; 381 ssize += v; 382 alloc_by_Xn_reg = -1; 383 continue; 384 } 385 } 386 387 /* stp x??,x??,[sp,#-imm7]! */ 388 if ((insn & 0xffe003e0) == 0xa9a003e0) { 389 int64_t imm7 = SignExtend(7, (insn >> 15) & 0x7f, 8); 390 uint64_t Rt2 = (insn >> 10) & 0x1f; 391 uint64_t Rt1 = insn & 0x1f; 392 if (Rt1 == 30) { 393 TRACE_DEBUG("stp x30,Xn[sp,#%ld]!\n", imm7); 394 lr_off = ssize; 395 ssize += -imm7; 396 found_lr_off = true; 397 } else if (Rt2 == 30) { 398 TRACE_DEBUG("stp Xn,x30,[sp,#%ld]!\n", imm7); 399 lr_off = ssize + 8; 400 ssize += -imm7; 401 found_lr_off = true; 402 } else { 403 ssize += -imm7; 404 TRACE_DEBUG("stp Xn,Xn,[sp,#%ld]!\n", imm7); 405 } 406 407 /* 408 * "stp x29,x30,[sp,#-n]!" is the code to create 409 * a frame pointer at the beginning of the function. 410 */ 411 if (func_entry_autodetect && Rt1 == 29 && Rt2 == 30) 412 break; 413 414 continue; 415 } 416 417 /* stp x??,x??,[sp,#imm7] */ 418 if ((insn & 0xffc003e0) == 0xa90003e0) { 419 int64_t imm7 = SignExtend(7, (insn >> 15) & 0x7f, 8); 420 uint64_t Rt2 = (insn >> 10) & 0x1f; 421 uint64_t Rt1 = insn & 0x1f; 422 if (Rt1 == 30) { 423 lr_off = ssize + imm7; 424 found_lr_off = true; 425 TRACE_DEBUG("stp x30,X%lu[sp,#%ld]\n", 426 Rt2, imm7); 427 TRACE_DEBUG("lr off = %lu = %#lx\n", 428 lr_off, lr_off); 429 } else if (Rt2 == 30) { 430 lr_off = ssize + imm7 + 8; 431 found_lr_off = true; 432 TRACE_DEBUG("stp X%lu,x30,[sp,#%ld]\n", 433 Rt1, imm7); 434 TRACE_DEBUG("lr off = %lu = %#lx\n", 435 lr_off, lr_off); 436 } 437 continue; 438 } 439 440 /* str x30,[sp,#imm12] */ 441 if ((insn & 0xffc003ff) == 0xf90003fe) { 442 uint64_t imm12 = 443 ZeroExtend(12, (insn >> 10) & 0xfff, 8); 444 lr_off = ssize + imm12; 445 found_lr_off = true; 446 TRACE_DEBUG("str x30,[sp,#%lu]\n", imm12); 447 TRACE_DEBUG("lr off = %lu = %#lx\n", lr_off, lr_off); 448 continue; 449 } 450 451 /* str x30,[sp,#-imm9]! */ 452 if ((insn & 0xfff00fff) == 0xf8100ffe) { 453 int64_t imm9 = SignExtend(9, (insn >> 12) & 0x1ff, 1); 454 lr_off = ssize; 455 ssize += -imm9; 456 found_lr_off = true; 457 TRACE_DEBUG("str x30,[sp,#%ld]!\n", imm9); 458 TRACE_DEBUG("lr off = %lu = %#lx\n", lr_off, lr_off); 459 continue; 460 } 461 } 462 463 if (found_lr_off) { 464 if (lr_off >= ssize) { 465 pr("cannot locate return address\n"); 466 return -1; 467 } 468 db_read_bytes((db_addr_t)sp + lr_off, sizeof(lr), (char *)&lr); 469 lr = aarch64_strip_pac(lr); 470 } 471 *stacksizep = ssize; 472 *lrp = lr; 473 474 TRACE_DEBUG("-----------\n"); 475 TRACE_DEBUG(" sp: %#lx\n", sp); 476 TRACE_DEBUG("stacksize: %#06lx = %lu\n", ssize, ssize); 477 TRACE_DEBUG("lr offset: %#06lx = %lu\n", lr_off, lr_off); 478 TRACE_DEBUG(" new lr: %#lx\n", lr); 479 TRACE_DEBUG("===========\n\n"); 480 481 return 0; 482} 483 484/* 485 * Backtrace without framepointer ($fp). 486 * 487 * Examines the contents of a function and returns the stack size allocated 488 * by the function and the stored $lr. 489 * 490 * This works well for code compiled with -fomit-frame-pointer. 491 */ 492static void 493db_sp_trace(struct trapframe *tf, db_addr_t fp, db_expr_t count, int flags, 494 void (*pr)(const char *, ...) __printflike(1, 2)) 495{ 496 struct trapframe tf_buf; 497 db_addr_t pc, sp, lr0; 498 bool allow_leaf_function = false; 499 500 if (tf == NULL) { 501 /* 502 * In the case of "trace/s <frame-address>", 503 * the specified frame pointer address is considered 504 * a trapframe (or a switchframe) address. 505 */ 506 tf = (struct trapframe *)fp; 507 } 508 509 pr_frame(tf, pr); 510 511 db_read_bytes((db_addr_t)tf, sizeof(tf_buf), (char *)&tf_buf); 512 if (tf_buf.tf_sp == 0) { 513 /* switchframe */ 514 lr0 = 0; 515 pc = aarch64_strip_pac(tf_buf.tf_lr) - 4; 516 sp = (uint64_t)(tf + 1); 517 } else { 518 /* trapframe */ 519 lr0 = aarch64_strip_pac(tf_buf.tf_lr); 520 pc = tf_buf.tf_pc; 521 sp = tf_buf.tf_sp; 522 allow_leaf_function = true; 523 } 524 525 TRACE_DEBUG("pc =%016lx\n", pc); 526 TRACE_DEBUG("sp =%016lx\n", sp); 527 TRACE_DEBUG("lr0=%016lx\n", lr0); 528 529 for (; (count > 0) && (sp != 0); count--) { 530 if ((pc == (db_addr_t)el0_trap) || 531 (pc == (db_addr_t)el1_trap)) { 532 533 pr_traceaddr("tf", sp, pc, flags, pr); 534 535 db_read_bytes((db_addr_t)sp, sizeof(tf_buf), 536 (char *)&tf_buf); 537 if (tf_buf.tf_lr == 0) 538 break; 539 pr_frame((struct trapframe *)sp, pr); 540 541 sp = tf_buf.tf_sp; 542 pc = tf_buf.tf_pc; 543 if (pc == 0) 544 pc = aarch64_strip_pac(tf_buf.tf_lr) - 4; 545 if (pc == 0) 546 break; 547 lr0 = aarch64_strip_pac(tf_buf.tf_lr); 548 allow_leaf_function = true; 549 550 } else { 551 db_sym_t sym; 552 db_addr_t func_entry, lr; 553 db_expr_t func_offset; 554 vsize_t stacksize; 555 556 pr_traceaddr("sp", sp, pc, flags, pr); 557 558 if ((flags & TRACEFLAG_USERSPACE) == 0 && 559 !IN_KERNEL_VM_ADDRESS(pc)) 560 break; 561 562 sym = db_search_symbol(pc, DB_STGY_ANY, &func_offset); 563 if (sym != 0) { 564 func_entry = pc - func_offset; 565 if (func_entry == 566 (db_addr_t)cpu_switchto_softint) { 567 /* 568 * In cpu_switchto_softint(), backtrace 569 * information for DDB is pushed onto 570 * the stack. 571 */ 572 db_read_bytes((db_addr_t)sp + 8, 573 sizeof(pc), (char *)&pc); 574 db_read_bytes((db_addr_t)sp, 575 sizeof(sp), (char *)&sp); 576 continue; 577 } 578 } else { 579 func_entry = 0; /* autodetect mode */ 580 } 581 582 if (analyze_func(func_entry, pc, sp, &lr, &stacksize, 583 pr) != 0) 584 break; 585 586 if (allow_leaf_function) { 587 if (lr == 0) 588 lr = lr0; 589 allow_leaf_function = false; 590 } else { 591 if (lr == 0) 592 break; 593 } 594 595 sp += stacksize; 596 pc = lr - 4; 597 } 598 } 599} 600 601static void 602db_fp_trace(struct trapframe *tf, db_addr_t fp, db_expr_t count, int flags, 603 void (*pr)(const char *, ...) __printflike(1, 2)) 604{ 605 uint64_t lr; 606 uint64_t lastlr, lastfp; 607 608 if (tf != NULL) { 609 pr_frame(tf, pr); 610 lastfp = lastlr = lr = fp = 0; 611 612 db_read_bytes((db_addr_t)&tf->tf_pc, sizeof(lr), (char *)&lr); 613 db_read_bytes((db_addr_t)&tf->tf_reg[29], sizeof(fp), (char *)&fp); 614 lr = aarch64_strip_pac(lr); 615 616 pr_traceaddr("fp", fp, lr - 4, flags, pr); 617 } 618 619 for (; (count > 0) && (fp != 0); count--) { 620 621 lastfp = fp; 622 fp = lr = 0; 623 /* 624 * normal stack frame 625 * fp[0] saved fp(x29) value 626 * fp[1] saved lr(x30) value 627 */ 628 db_read_bytes(lastfp + 0, sizeof(fp), (char *)&fp); 629 db_read_bytes(lastfp + 8, sizeof(lr), (char *)&lr); 630 lr = aarch64_strip_pac(lr); 631 632 if (lr == 0 || ((flags & TRACEFLAG_USERSPACE) == 0 && 633 IN_USER_VM_ADDRESS(lr))) 634 break; 635 636 if (((char *)(lr - 4) == (char *)el0_trap) || 637 ((char *)(lr - 4) == (char *)el1_trap)) { 638 639 tf = (struct trapframe *)fp; 640 641 lastfp = (uint64_t)tf; 642 lastlr = lr; 643 lr = fp = 0; 644 db_read_bytes((db_addr_t)&tf->tf_pc, sizeof(lr), 645 (char *)&lr); 646 if (lr == 0) { 647 /* 648 * The exception may have been from a 649 * jump to null, so the null pc we 650 * would return to is useless. Try 651 * x[30] instead -- that will be the 652 * return address for the jump. 653 */ 654 db_read_bytes((db_addr_t)&tf->tf_reg[30], 655 sizeof(lr), (char *)&lr); 656 } 657 db_read_bytes((db_addr_t)&tf->tf_reg[29], sizeof(fp), 658 (char *)&fp); 659 lr = aarch64_strip_pac(lr); 660 661 pr_traceaddr("tf", (db_addr_t)tf, lastlr - 4, flags, pr); 662 663 if (lr == 0) 664 break; 665 666 pr_frame(tf, pr); 667 tf = NULL; 668 669 if ((flags & TRACEFLAG_USERSPACE) == 0 && 670 IN_USER_VM_ADDRESS(lr)) 671 break; 672 673 pr_traceaddr("fp", fp, lr, flags, pr); 674 } else { 675 pr_traceaddr("fp", fp, lr - 4, flags, pr); 676 } 677 } 678} 679 680void 681db_stack_trace_print(db_expr_t addr, bool have_addr, db_expr_t count, 682 const char *modif, void (*pr)(const char *, ...) __printflike(1, 2)) 683{ 684 uint64_t fp; 685 struct trapframe *tf = NULL; 686 int flags = 0; 687 bool trace_thread = false; 688 bool trace_lwp = false; 689 bool trace_sp = false; 690 691 for (; *modif != '\0'; modif++) { 692 switch (*modif) { 693 case 'a': 694 trace_lwp = true; 695 trace_thread = false; 696 break; 697 case 'l': 698 break; 699 case 't': 700 trace_thread = true; 701 trace_lwp = false; 702 break; 703 case 's': 704 trace_sp = true; 705 break; 706 case 'u': 707 flags |= TRACEFLAG_USERSPACE; 708 break; 709 case 'x': 710 flags |= TRACEFLAG_LOOKUPLWP; 711 break; 712 default: 713 pr("usage: bt[/ulsx] [frame-address][,count]\n"); 714 pr(" bt/t[ulsx] [pid][,count]\n"); 715 pr(" bt/a[ulsx] [lwpaddr][,count]\n"); 716 pr("\n"); 717 pr(" /s trace without framepointer\n"); 718 pr(" /x reverse lookup lwp name from sp\n"); 719 return; 720 } 721 } 722 723#if defined(_KERNEL) 724 if (!have_addr) { 725 if (trace_lwp) { 726 addr = (db_expr_t)curlwp; 727 } else if (trace_thread) { 728 addr = curlwp->l_proc->p_pid; 729 } else { 730 tf = DDB_REGS; 731 } 732 } 733#endif 734 735 if (trace_thread) { 736 proc_t *pp; 737 738 if ((pp = db_proc_find((pid_t)addr)) == 0) { 739 (*pr)("trace: pid %d: not found\n", (int)addr); 740 return; 741 } 742 db_read_bytes((db_addr_t)pp + offsetof(proc_t, p_lwps.lh_first), 743 sizeof(addr), (char *)&addr); 744 trace_thread = false; 745 trace_lwp = true; 746 } 747 748#if 0 749 /* "/a" is abbreviated? */ 750 if (!trace_lwp && is_lwp(addr)) 751 trace_lwp = true; 752#endif 753 754 if (trace_lwp) { 755 struct lwp l; 756 pid_t pid; 757 758 db_read_bytes(addr, sizeof(l), (char *)&l); 759 db_read_bytes((db_addr_t)l.l_proc + offsetof(proc_t, p_pid), 760 sizeof(pid), (char *)&pid); 761 762#if defined(_KERNEL) 763 if (addr == (db_expr_t)curlwp) { 764 fp = (uint64_t)&DDB_REGS->tf_reg[29]; /* ®[29]={fp,lr} */ 765 tf = DDB_REGS; 766 (*pr)("trace: pid %d lid %d (curlwp) at tf %p\n", 767 pid, l.l_lid, tf); 768 } else 769#endif 770 { 771 struct pcb *pcb = lwp_getpcb(&l); 772 773 db_read_bytes((db_addr_t)pcb + 774 offsetof(struct pcb, pcb_tf), 775 sizeof(tf), (char *)&tf); 776 if (tf != 0) { 777 db_read_bytes((db_addr_t)&tf->tf_reg[29], 778 sizeof(fp), (char *)&fp); 779 (*pr)("trace: pid %d lid %d at tf %p (in pcb)\n", 780 pid, l.l_lid, tf); 781 } 782#if defined(MULTIPROCESSOR) && defined(_KERNEL) 783 else if (l.l_stat == LSONPROC || 784 (l.l_pflag & LP_RUNNING) != 0) { 785 786 /* running lwp on other cpus */ 787 extern struct trapframe *db_readytoswitch[]; 788 u_int index; 789 790 db_read_bytes((db_addr_t)l.l_cpu + 791 offsetof(struct cpu_info, ci_index), 792 sizeof(index), (char *)&index); 793 tf = db_readytoswitch[index]; 794 795 (*pr)("trace: pid %d lid %d at tf %p (in kdb_trap)\n", 796 pid, l.l_lid, tf); 797 } 798#endif 799 else { 800 (*pr)("trace: no trapframe found for lwp: %p\n", (void *)addr); 801 } 802 } 803 } else if (tf == NULL) { 804 fp = addr; 805 pr("trace fp %016lx\n", fp); 806 } else { 807 pr("trace tf %p\n", tf); 808 } 809 810 if (count > MAXBACKTRACE) 811 count = MAXBACKTRACE; 812 813 if (trace_sp) { 814 /* trace $lr pushed to sp */ 815 db_sp_trace(tf, fp, count, flags, pr); 816 } else { 817 /* trace $fp linked list */ 818 db_fp_trace(tf, fp, count, flags, pr); 819 } 820} 821