1 /* $NetBSD: db_trace.c,v 1.39 2023/11/21 22:19:12 thorpej Exp $ */ 2 3 /*- 4 * Copyright (c) 1999 The NetBSD Foundation, Inc. 5 * All rights reserved. 6 * 7 * This code is derived from software contributed to The NetBSD Foundation 8 * by Jason R. Thorpe of the Numerical Aerospace Simulation Facility, 9 * NASA Ames Research Center. 10 * 11 * This code is derived from software contributed to The NetBSD Foundation 12 * by Ross Harvey. 13 * 14 * Redistribution and use in source and binary forms, with or without 15 * modification, are permitted provided that the following conditions 16 * are met: 17 * 1. Redistributions of source code must retain the above copyright 18 * notice, this list of conditions and the following disclaimer. 19 * 2. Redistributions in binary form must reproduce the above copyright 20 * notice, this list of conditions and the following disclaimer in the 21 * documentation and/or other materials provided with the distribution. 22 * 23 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 24 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 25 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 26 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 27 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 28 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 29 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 30 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 31 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 32 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 33 * POSSIBILITY OF SUCH DAMAGE. 34 */ 35 36 #include <sys/cdefs.h> /* RCS ID & Copyright macro defns */ 37 38 __KERNEL_RCSID(0, "$NetBSD: db_trace.c,v 1.39 2023/11/21 22:19:12 thorpej Exp $"); 39 40 #include <sys/param.h> 41 #include <sys/systm.h> 42 #include <sys/proc.h> 43 44 #include <machine/alpha.h> 45 #include <machine/db_machdep.h> 46 47 #include <machine/alpha_instruction.h> 48 49 #include <ddb/db_sym.h> 50 #include <ddb/db_access.h> 51 #include <ddb/db_variables.h> 52 #include <ddb/db_output.h> 53 #include <ddb/db_interface.h> 54 55 /* 56 * Information about the `standard' Alpha function prologue. 57 */ 58 struct prologue_info { 59 int pi_reg_offset[32]; /* offset of registers in stack frame */ 60 uint32_t pi_regmask; /* which registers are in frame */ 61 int pi_frame_size; /* frame size */ 62 }; 63 64 /* 65 * Decode the function prologue for the function we're in, and note 66 * which registers are stored where, and how large the stack frame is. 67 */ 68 static void 69 decode_prologue(db_addr_t callpc, db_addr_t func, 70 struct prologue_info *pi, void (*pr)(const char *, ...)) 71 { 72 long signed_immediate; 73 alpha_instruction ins; 74 db_addr_t pc; 75 76 pi->pi_regmask = 0; 77 pi->pi_frame_size = 0; 78 79 #define CHECK_FRAMESIZE \ 80 do { \ 81 if (pi->pi_frame_size != 0) { \ 82 (*pr)("frame size botch: adjust register offsets?\n"); \ 83 } \ 84 } while (0) 85 86 for (pc = func; pc < callpc; pc += sizeof(alpha_instruction)) { 87 db_read_bytes(pc, sizeof(ins.bits), (char *)&ins.bits); 88 89 if (ins.mem_format.opcode == op_lda && 90 ins.mem_format.ra == 30 && 91 ins.mem_format.rb == 30) { 92 /* 93 * GCC 2.7-style stack adjust: 94 * 95 * lda sp, -64(sp) 96 */ 97 signed_immediate = (long)ins.mem_format.displacement; 98 /* 99 * The assumption here is that a positive 100 * stack offset is the function epilogue, 101 * which may come before callpc when an 102 * aggressive optimizer (like GCC 3.3 or later) 103 * has moved part of the function "out of 104 * line", past the epilogue. Therefore, ignore 105 * the positive offset so that 106 * pi->pi_frame_size has the correct value 107 * when we reach callpc. 108 */ 109 if (signed_immediate <= 0) { 110 CHECK_FRAMESIZE; 111 pi->pi_frame_size += -signed_immediate; 112 } 113 } else if (ins.operate_lit_format.opcode == op_arit && 114 ins.operate_lit_format.function == op_subq && 115 ins.operate_lit_format.ra == 30 && 116 ins.operate_lit_format.rc == 30) { 117 /* 118 * EGCS-style stack adjust: 119 * 120 * subq sp, 64, sp 121 */ 122 CHECK_FRAMESIZE; 123 pi->pi_frame_size += ins.operate_lit_format.literal; 124 } else if (ins.mem_format.opcode == op_stq && 125 ins.mem_format.rb == 30 && 126 ins.mem_format.ra != 31) { 127 /* Store of (non-zero) register onto the stack. */ 128 signed_immediate = (long)ins.mem_format.displacement; 129 pi->pi_regmask |= 1 << ins.mem_format.ra; 130 pi->pi_reg_offset[ins.mem_format.ra] = signed_immediate; 131 } 132 } 133 } 134 135 static void 136 decode_syscall(int number, void (*pr)(const char *, ...)) 137 { 138 (*pr)(" (%d)", number); 139 } 140 141 void 142 db_stack_trace_print(db_expr_t addr, bool have_addr, db_expr_t count, 143 const char *modif, void (*pr)(const char *, ...)) 144 { 145 146 db_stack_trace_print_ra(/*ra*/0, /*have_ra*/false, addr, have_addr, 147 count, modif, pr); 148 } 149 150 void 151 db_stack_trace_print_ra(db_expr_t ra, bool have_ra, 152 db_expr_t addr, bool have_addr, 153 db_expr_t count, 154 const char *modif, void (*pr)(const char *, ...)) 155 { 156 db_addr_t callpc, frame, symval; 157 struct prologue_info pi; 158 db_expr_t diff; 159 db_sym_t sym; 160 u_long tfps; 161 const char *symname; 162 struct pcb *pcbp; 163 const char *cp = modif; 164 struct trapframe *tf; 165 bool ra_from_tf; 166 u_long last_ipl = ~0L; 167 char c; 168 bool trace_thread = false; 169 bool lwpaddr = false; 170 171 while ((c = *cp++) != 0) { 172 trace_thread |= c == 't'; 173 trace_thread |= c == 'a'; 174 lwpaddr |= c == 'a'; 175 } 176 177 if (!have_addr) { 178 addr = DDB_REGS->tf_regs[FRAME_SP] - FRAME_SIZE * 8; 179 tf = (struct trapframe *)addr; 180 callpc = db_alpha_tf_reg(tf, FRAME_PC); 181 frame = (db_addr_t)tf + FRAME_SIZE * 8; 182 ra_from_tf = true; 183 } else { 184 #ifdef _KERNEL 185 struct proc *p = NULL; 186 struct lwp *l = NULL; 187 #else 188 struct proc pstore, *p = &pstore; 189 struct lwp lstore, *l = &lstore; 190 #endif /* _KERNEL */ 191 192 if (trace_thread) { 193 if (lwpaddr) { 194 #ifdef _KERNEL 195 l = (struct lwp *)addr; 196 p = l->l_proc; 197 #else 198 db_read_bytes(addr, sizeof(*l), (char *)l); 199 db_read_bytes((db_addr_t)l->l_proc, 200 sizeof(*p), (char *)p); 201 #endif /* _KERNEL */ 202 (*pr)("trace: pid %d ", p->p_pid); 203 } else { 204 #ifdef _KERNEL 205 (*pr)("trace: pid %d ", (int)addr); 206 p = proc_find_raw(addr); 207 if (p == NULL) { 208 (*pr)("not found\n"); 209 return; 210 } 211 l = LIST_FIRST(&p->p_lwps); 212 KASSERT(l != NULL); 213 #else 214 (*pr)("no proc_find_raw() in crash\n"); 215 return; 216 #endif /* _KERNEL */ 217 } 218 (*pr)("lid %d ", l->l_lid); 219 pcbp = lwp_getpcb(l); 220 addr = db_alpha_read_saved_reg(&pcbp->pcb_hw.apcb_ksp); 221 callpc = db_alpha_read_saved_reg(&pcbp->pcb_context[7]); 222 (*pr)("at 0x%lx\n", addr); 223 } else if (have_ra) { 224 callpc = ra; 225 (*pr)("at 0x%lx pc 0x%lx\n", addr, callpc); 226 } else { 227 (*pr)("alpha trace requires known PC =eject=\n"); 228 return; 229 } 230 frame = addr; 231 tf = NULL; 232 ra_from_tf = false; 233 } 234 235 while (count--) { 236 sym = db_search_symbol(callpc, DB_STGY_ANY, &diff); 237 if (sym == DB_SYM_NULL) 238 break; 239 240 db_symbol_values(sym, &symname, (db_expr_t *)&symval); 241 242 if (callpc < symval) { 243 (*pr)("symbol botch: callpc 0x%lx < " 244 "func 0x%lx (%s)\n", callpc, symval, symname); 245 return; 246 } 247 248 /* 249 * If the previous RA pointed at the kernel thread 250 * backstop, then we are at the root of the call 251 * graph. 252 */ 253 if (db_alpha_sym_is_backstop(symval)) { 254 (*pr)("--- kernel thread backstop ---\n"); 255 break; 256 } 257 258 /* 259 * XXX Printing out arguments is Hard. We'd have to 260 * keep lots of state as we traverse the frame, figuring 261 * out where the arguments to the function are stored 262 * on the stack. 263 * 264 * Even worse, they may be stored to the stack _after_ 265 * being modified in place; arguments are passed in 266 * registers. 267 * 268 * So, in order for this to work reliably, we pretty much 269 * have to have a kernel built with `cc -g': 270 * 271 * - The debugging symbols would tell us where the 272 * arguments are, how many there are, if there were 273 * any passed on the stack, etc. 274 * 275 * - Presumably, the compiler would be careful to 276 * store the argument registers on the stack before 277 * modifying the registers, so that a debugger could 278 * know what those values were upon procedure entry. 279 * 280 * Because of this, we don't bother. We've got most of the 281 * benefit of back tracking without the arguments, and we 282 * could get the arguments if we use a remote source-level 283 * debugger (for serious debugging). 284 */ 285 (*pr)("%s() at ", symname); 286 db_printsym(callpc, DB_STGY_PROC, pr); 287 (*pr)("\n"); 288 289 /* 290 * If we are in a trap vector, frame points to a 291 * trapframe. 292 */ 293 if (db_alpha_sym_is_trap(symval)) { 294 tf = (struct trapframe *)frame; 295 296 (*pr)("--- %s", db_alpha_trapsym_description(symval)); 297 298 tfps = db_alpha_tf_reg(tf, FRAME_PS); 299 if (db_alpha_sym_is_syscall(symval)) { 300 decode_syscall(db_alpha_tf_reg(tf, FRAME_V0), 301 pr); 302 } 303 if ((tfps & ALPHA_PSL_IPL_MASK) != last_ipl) { 304 last_ipl = tfps & ALPHA_PSL_IPL_MASK; 305 if (! db_alpha_sym_is_syscall(symval)) { 306 (*pr)(" (from ipl %ld)", last_ipl); 307 } 308 } 309 (*pr)(" ---\n"); 310 if (tfps & ALPHA_PSL_USERMODE) { 311 (*pr)("--- user mode ---\n"); 312 break; /* Terminate search. */ 313 } 314 callpc = db_alpha_tf_reg(tf, FRAME_PC); 315 frame = (db_addr_t)tf + FRAME_SIZE * 8; 316 ra_from_tf = true; 317 continue; 318 } 319 320 /* 321 * This is a bit trickier; we must decode the function 322 * prologue to find the saved RA. 323 * 324 * XXX How does this interact w/ alloca()?! 325 */ 326 decode_prologue(callpc, symval, &pi, pr); 327 if ((pi.pi_regmask & (1 << 26)) == 0) { 328 /* 329 * No saved RA found. We might have RA from 330 * the trap frame, however (e.g trap occurred 331 * in a leaf call). If not, we've found the 332 * root of the call graph. 333 */ 334 if (ra_from_tf) { 335 callpc = db_alpha_tf_reg(tf, FRAME_RA); 336 } else { 337 (*pr)("--- root of call graph ---\n"); 338 break; 339 } 340 } else { 341 unsigned long reg; 342 343 db_read_bytes(frame + pi.pi_reg_offset[26], 344 sizeof(reg), (char *)®); 345 callpc = reg; 346 } 347 frame += pi.pi_frame_size; 348 ra_from_tf = false; 349 } 350 } 351