1 /* $OpenBSD: db_trace.c,v 1.21 2024/11/07 16:02:29 miod Exp $ */ 2 /* $NetBSD: db_trace.c,v 1.15 1996/02/22 23:23:41 gwr Exp $ */ 3 4 /* 5 * Mach Operating System 6 * Copyright (c) 1992 Carnegie Mellon University 7 * All Rights Reserved. 8 * 9 * Permission to use, copy, modify and distribute this software and its 10 * documentation is hereby granted, provided that both the copyright 11 * notice and this permission notice appear in all copies of the 12 * software, derivative works or modified versions, and any portions 13 * thereof, and that both notices appear in supporting documentation. 14 * 15 * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS" 16 * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR 17 * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. 18 * 19 * Carnegie Mellon requests users of this software to return to 20 * 21 * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU 22 * School of Computer Science 23 * Carnegie Mellon University 24 * Pittsburgh PA 15213-3890 25 * 26 * any improvements or extensions that they make and grant Carnegie Mellon 27 * the rights to redistribute these changes. 28 */ 29 30 #include <sys/param.h> 31 #include <sys/systm.h> 32 #include <sys/proc.h> 33 #include <sys/user.h> 34 #include <sys/stacktrace.h> 35 36 #include <uvm/uvm_extern.h> 37 38 #include <machine/db_machdep.h> 39 #include <machine/signal.h> 40 #include <machine/pcb.h> 41 #include <machine/pmap.h> 42 43 #include <ddb/db_access.h> 44 #include <ddb/db_sym.h> 45 #include <ddb/db_variables.h> 46 #include <ddb/db_interface.h> 47 #include <ddb/db_output.h> 48 49 db_regs_t ddb_regs; 50 51 struct db_variable db_regs[] = { 52 { "r0", (long *)&ddb_regs.fixreg[0], FCN_NULL }, 53 { "r1", (long *)&ddb_regs.fixreg[1], FCN_NULL }, 54 { "r2", (long *)&ddb_regs.fixreg[2], FCN_NULL }, 55 { "r3", (long *)&ddb_regs.fixreg[3], FCN_NULL }, 56 { "r4", (long *)&ddb_regs.fixreg[4], FCN_NULL }, 57 { "r5", (long *)&ddb_regs.fixreg[5], FCN_NULL }, 58 { "r6", (long *)&ddb_regs.fixreg[6], FCN_NULL }, 59 { "r7", (long *)&ddb_regs.fixreg[7], FCN_NULL }, 60 { "r8", (long *)&ddb_regs.fixreg[8], FCN_NULL }, 61 { "r9", (long *)&ddb_regs.fixreg[9], FCN_NULL }, 62 { "r10", (long *)&ddb_regs.fixreg[10], FCN_NULL }, 63 { "r11", (long *)&ddb_regs.fixreg[11], FCN_NULL }, 64 { "r12", (long *)&ddb_regs.fixreg[12], FCN_NULL }, 65 { "r13", (long *)&ddb_regs.fixreg[13], FCN_NULL }, 66 { "r14", (long *)&ddb_regs.fixreg[14], FCN_NULL }, 67 { "r15", (long *)&ddb_regs.fixreg[15], FCN_NULL }, 68 { "r16", (long *)&ddb_regs.fixreg[16], FCN_NULL }, 69 { "r17", (long *)&ddb_regs.fixreg[17], FCN_NULL }, 70 { "r18", (long *)&ddb_regs.fixreg[18], FCN_NULL }, 71 { "r19", (long *)&ddb_regs.fixreg[19], FCN_NULL }, 72 { "r20", (long *)&ddb_regs.fixreg[20], FCN_NULL }, 73 { "r21", (long *)&ddb_regs.fixreg[21], FCN_NULL }, 74 { "r22", (long *)&ddb_regs.fixreg[22], FCN_NULL }, 75 { "r23", (long *)&ddb_regs.fixreg[23], FCN_NULL }, 76 { "r24", (long *)&ddb_regs.fixreg[24], FCN_NULL }, 77 { "r25", (long *)&ddb_regs.fixreg[25], FCN_NULL }, 78 { "r26", (long *)&ddb_regs.fixreg[26], FCN_NULL }, 79 { "r27", (long *)&ddb_regs.fixreg[27], FCN_NULL }, 80 { "r28", (long *)&ddb_regs.fixreg[28], FCN_NULL }, 81 { "r29", (long *)&ddb_regs.fixreg[29], FCN_NULL }, 82 { "r30", (long *)&ddb_regs.fixreg[30], FCN_NULL }, 83 { "r31", (long *)&ddb_regs.fixreg[31], FCN_NULL }, 84 { "lr", (long *)&ddb_regs.lr, FCN_NULL }, 85 { "cr", (long *)&ddb_regs.cr, FCN_NULL }, 86 { "xer", (long *)&ddb_regs.xer, FCN_NULL }, 87 { "ctr", (long *)&ddb_regs.ctr, FCN_NULL }, 88 { "iar", (long *)&ddb_regs.srr0, FCN_NULL }, 89 { "msr", (long *)&ddb_regs.srr1, FCN_NULL }, 90 { "dar", (long *)&ddb_regs.dar, FCN_NULL }, 91 { "dsisr", (long *)&ddb_regs.dsisr, FCN_NULL }, 92 }; 93 94 struct db_variable *db_eregs = db_regs + nitems(db_regs); 95 96 /* 97 * this is probably hackery. 98 */ 99 void 100 db_save_regs(struct trapframe *frame) 101 { 102 bcopy(frame, &ddb_regs, sizeof (struct trapframe)); 103 } 104 105 /* from locore.S */ 106 extern vaddr_t trapexit; 107 #define INTSTK (8*1024) /* 8K interrupt stack */ 108 109 #define INKERNEL(va) (((vaddr_t)(va)) >= VM_MIN_KERNEL_ADDRESS && \ 110 ((vaddr_t)(va)) < VM_MAX_KERNEL_ADDRESS) 111 112 #define ININTSTK(va) db_in_interrupt_stack((vaddr_t)(va)) 113 114 int 115 db_in_interrupt_stack(vaddr_t va) 116 { 117 struct cpu_info *ci; 118 CPU_INFO_ITERATOR cii; 119 vaddr_t stack; 120 121 CPU_INFO_FOREACH(cii, ci) { 122 stack = (vaddr_t)ci->ci_intstk; 123 if (va >= stack - INTSTK && va < stack) 124 return 1; 125 } 126 return 0; 127 } 128 129 /* 130 * Frame tracing. 131 */ 132 void 133 db_stack_trace_print(db_expr_t addr, int have_addr, db_expr_t count, 134 char *modif, int (*pr)(const char *, ...)) 135 { 136 vaddr_t lr, sp, lastsp, *db_fp_args; 137 db_expr_t offset; 138 Elf_Sym *sym; 139 const char *name; 140 char c, *cp = modif; 141 int i, narg, trace_proc = 0; 142 143 while ((c = *cp++) != 0) { 144 if (c == 't') 145 trace_proc = 1; 146 } 147 148 if (!have_addr) { 149 sp = ddb_regs.fixreg[1]; 150 lr = ddb_regs.srr0; 151 } else { 152 if (trace_proc) { 153 struct proc *p = tfind((pid_t)addr); 154 if (p == NULL) { 155 (*pr) ("not found\n"); 156 return; 157 } 158 addr = p->p_addr->u_pcb.pcb_sp; 159 } 160 sp = addr; 161 db_read_bytes(sp + 4, sizeof(vaddr_t), (char *)&lr); 162 } 163 164 while (count && sp != 0) { 165 /* 166 * lr contains the return address, so adjust its value 167 * to display the offset of the calling address. 168 */ 169 sym = db_search_symbol(lr - 4, DB_STGY_ANY, &offset); 170 db_symbol_values(sym, &name, NULL); 171 172 if (name == NULL || strcmp(name, "end") == 0) { 173 (*pr)("at 0x%lx", lr - 4); 174 } else { 175 narg = db_ctf_func_numargs(sym); 176 if (narg < 0 || narg > 8) 177 narg = 8; 178 179 (*pr)("%s(", name); 180 181 if (narg > 0) { 182 db_fp_args = (vaddr_t *)(sp + 8); 183 184 for (i = 0; i < narg; i++) { 185 (*pr)("%lx", db_fp_args[i]); 186 if (i != (narg-1)) 187 (*pr)(","); 188 } 189 } 190 191 (*pr)(") at "); 192 db_printsym(lr - 4, DB_STGY_PROC, pr); 193 } 194 (*pr)("\n"); 195 196 lastsp = sp; 197 198 /* 199 * Abuse the fact that the return address of the trap() 200 * function is always 'trapexit'. 201 */ 202 if (lr == (vaddr_t)&trapexit) { 203 struct trapframe *tf = (struct trapframe *)(sp + 8); 204 uint32_t code = tf->fixreg[0]; 205 uint32_t type = tf->exc; 206 207 if (tf->srr1 & PSL_PR) 208 type |= EXC_USER; 209 210 if (type == (EXC_SC|EXC_USER)) 211 (*pr)("--- syscall (number %d) ---\n", code); 212 else 213 (*pr)("--- trap (type 0x%x) ---\n", type); 214 } 215 216 db_read_bytes(sp, sizeof(vaddr_t), (char *)&sp); 217 if (sp == 0) 218 break; 219 220 db_read_bytes(sp + 4, sizeof(vaddr_t), (char *)&lr); 221 222 if (INKERNEL(sp)) { 223 if (sp <= lastsp) { 224 (*pr)("Bad frame pointer: 0x%lx\n", sp); 225 break; 226 } 227 228 if (ININTSTK(lastsp)) 229 (*pr)("--- interrupt ---\n"); 230 231 } else { 232 if (!ININTSTK(sp)) { 233 (*pr)("End of kernel: 0x%lx\n", sp); 234 break; 235 } 236 } 237 --count; 238 } 239 (*pr)("end trace frame: 0x%lx, count: %d\n", sp, count); 240 } 241 242 void 243 stacktrace_save_at(struct stacktrace *st, unsigned int skip) 244 { 245 vaddr_t lr, sp, lastsp; 246 247 sp = (vaddr_t)__builtin_frame_address(0); 248 if (!INKERNEL(sp) && !ININTSTK(sp)) 249 return; 250 251 st->st_count = 0; 252 while (st->st_count < STACKTRACE_MAX) { 253 lr = *(vaddr_t *)(sp + 4) - 4; 254 if (lr & 3) 255 break; 256 257 if (skip == 0) 258 st->st_pc[st->st_count++] = lr; 259 else 260 skip--; 261 262 lastsp = sp; 263 sp = *(vaddr_t *)sp; 264 265 if ((sp == 0) || (sp & 3) || (sp <= lastsp)) 266 break; 267 if (!INKERNEL(sp) && !ININTSTK(sp)) 268 break; 269 } 270 } 271 272 void 273 stacktrace_save_utrace(struct stacktrace *st) 274 { 275 st->st_count = 0; 276 } 277