1 /* $OpenBSD: db_trace.c,v 1.46 2024/11/07 16:02:29 miod Exp $ */ 2 /* $NetBSD: db_trace.c,v 1.18 1996/05/03 19:42:01 christos Exp $ */ 3 4 /* 5 * Mach Operating System 6 * Copyright (c) 1991,1990 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/stacktrace.h> 34 #include <sys/user.h> 35 36 #include <machine/db_machdep.h> 37 38 #include <ddb/db_sym.h> 39 #include <ddb/db_access.h> 40 #include <ddb/db_variables.h> 41 #include <ddb/db_interface.h> 42 43 /* 44 * Machine register set. 45 */ 46 struct db_variable db_regs[] = { 47 { "ds", (long *)&ddb_regs.tf_ds, FCN_NULL }, 48 { "es", (long *)&ddb_regs.tf_es, FCN_NULL }, 49 { "fs", (long *)&ddb_regs.tf_fs, FCN_NULL }, 50 { "gs", (long *)&ddb_regs.tf_gs, FCN_NULL }, 51 { "edi", (long *)&ddb_regs.tf_edi, FCN_NULL }, 52 { "esi", (long *)&ddb_regs.tf_esi, FCN_NULL }, 53 { "ebp", (long *)&ddb_regs.tf_ebp, FCN_NULL }, 54 { "ebx", (long *)&ddb_regs.tf_ebx, FCN_NULL }, 55 { "edx", (long *)&ddb_regs.tf_edx, FCN_NULL }, 56 { "ecx", (long *)&ddb_regs.tf_ecx, FCN_NULL }, 57 { "eax", (long *)&ddb_regs.tf_eax, FCN_NULL }, 58 { "eip", (long *)&ddb_regs.tf_eip, FCN_NULL }, 59 { "cs", (long *)&ddb_regs.tf_cs, FCN_NULL }, 60 { "eflags", (long *)&ddb_regs.tf_eflags, FCN_NULL }, 61 { "esp", (long *)&ddb_regs.tf_esp, FCN_NULL }, 62 { "ss", (long *)&ddb_regs.tf_ss, FCN_NULL }, 63 }; 64 struct db_variable *db_eregs = db_regs + nitems(db_regs); 65 66 /* 67 * Stack trace. 68 */ 69 #define INKERNEL(va) (((vaddr_t)(va)) >= VM_MIN_KERNEL_ADDRESS) 70 71 int db_i386_numargs(struct callframe *); 72 73 /* 74 * Figure out how many arguments were passed into the frame at "fp". 75 */ 76 int 77 db_i386_numargs(struct callframe *fp) 78 { 79 int *argp; 80 int inst; 81 int args; 82 extern char etext[]; 83 84 argp = (int *)db_get_value((int)&fp->f_retaddr, 4, 0); 85 if (argp < (int *)VM_MIN_KERNEL_ADDRESS || argp > (int *)etext) { 86 args = 5; 87 } else { 88 inst = db_get_value((int)argp, 4, 0); 89 if ((inst & 0xff) == 0x59) /* popl %ecx */ 90 args = 1; 91 else if ((inst & 0xffff) == 0xc483) /* addl %n, %esp */ 92 args = ((inst >> 16) & 0xff) / 4; 93 else 94 args = 5; 95 } 96 return args; 97 } 98 99 void 100 db_stack_trace_print(db_expr_t addr, int have_addr, db_expr_t count, 101 char *modif, int (*pr)(const char *, ...)) 102 { 103 struct callframe *frame, *lastframe; 104 int *argp, *arg0; 105 vaddr_t callpc; 106 unsigned int cr4save = CR4_SMEP|CR4_SMAP; 107 int kernel_only = 1; 108 int trace_proc = 0; 109 struct proc *p; 110 111 { 112 char *cp = modif; 113 char c; 114 115 while ((c = *cp++) != 0) { 116 if (c == 't') 117 trace_proc = 1; 118 if (c == 'u') 119 kernel_only = 0; 120 } 121 } 122 123 if (count == -1) 124 count = 65535; 125 126 if (trace_proc) { 127 p = tfind((pid_t)addr); 128 if (p == NULL) { 129 (*pr) ("not found\n"); 130 return; 131 } 132 } 133 134 if (curcpu()->ci_feature_sefflags_ebx & SEFF0EBX_SMAP) { 135 cr4save = rcr4(); 136 if (cr4save & CR4_SMAP) 137 lcr4(cr4save & ~CR4_SMAP); 138 } else { 139 cr4save = 0; 140 } 141 142 if (!have_addr) { 143 frame = (struct callframe *)ddb_regs.tf_ebp; 144 callpc = (vaddr_t)ddb_regs.tf_eip; 145 } else if (trace_proc) { 146 frame = (struct callframe *)p->p_addr->u_pcb.pcb_ebp; 147 callpc = (vaddr_t) 148 db_get_value((int)&frame->f_retaddr, 4, 0); 149 } else { 150 frame = (struct callframe *)addr; 151 callpc = (vaddr_t) 152 db_get_value((int)&frame->f_retaddr, 4, 0); 153 } 154 155 lastframe = 0; 156 while (count && frame != 0) { 157 int narg; 158 const char * name; 159 db_expr_t offset; 160 Elf_Sym *sym; 161 162 if (INKERNEL(frame)) { 163 sym = db_search_symbol(callpc, DB_STGY_ANY, &offset); 164 db_symbol_values(sym, &name, NULL); 165 } else { 166 sym = NULL; 167 name = NULL; 168 } 169 170 if (lastframe == 0 && sym == NULL) { 171 /* Symbol not found, peek at code */ 172 int instr = db_get_value(callpc, 4, 0); 173 174 offset = 1; 175 if ((instr & 0x00ffffff) == 0x00e58955 || 176 /* enter: pushl %ebp, movl %esp, %ebp */ 177 (instr & 0x0000ffff) == 0x0000e589 178 /* enter+1: movl %esp, %ebp */) { 179 offset = 0; 180 } 181 } 182 183 narg = db_ctf_func_numargs(sym); 184 if (narg < 0) 185 narg = db_i386_numargs(frame); 186 187 if (name == NULL) 188 (*pr)("%lx(", callpc); 189 else 190 (*pr)("%s(", name); 191 192 if (lastframe == 0 && offset == 0 && !have_addr) { 193 /* 194 * We have a breakpoint before the frame is set up 195 * Use %esp instead 196 */ 197 arg0 = 198 &((struct callframe *)(ddb_regs.tf_esp-4))->f_arg0; 199 } else { 200 arg0 = &frame->f_arg0; 201 } 202 203 for (argp = arg0; narg > 0; ) { 204 (*pr)("%x", db_get_value((int)argp, 4, 0)); 205 argp++; 206 if (--narg != 0) 207 (*pr)(","); 208 } 209 (*pr)(") at "); 210 db_printsym(callpc, DB_STGY_PROC, pr); 211 (*pr)("\n"); 212 213 if (lastframe == 0 && offset == 0 && !have_addr) { 214 /* Frame really belongs to next callpc */ 215 lastframe = (struct callframe *)(ddb_regs.tf_esp-4); 216 callpc = (vaddr_t) 217 db_get_value((int)&lastframe->f_retaddr, 4, 0); 218 continue; 219 } 220 221 lastframe = frame; 222 callpc = db_get_value((int)&frame->f_retaddr, 4, 0); 223 frame = (void *)db_get_value((int)&frame->f_frame, 4, 0); 224 225 if (frame == 0) { 226 /* end of chain */ 227 break; 228 } 229 if (INKERNEL(frame)) { 230 /* staying in kernel */ 231 if (frame <= lastframe) { 232 (*pr)("Bad frame pointer: %p\n", frame); 233 break; 234 } 235 } else if (INKERNEL(lastframe)) { 236 /* switch from user to kernel */ 237 if (kernel_only) { 238 (*pr)("end of kernel\n"); 239 break; /* kernel stack only */ 240 } 241 } else { 242 /* in user */ 243 if (frame <= lastframe) { 244 (*pr)("Bad user frame pointer: %p\n", 245 frame); 246 break; 247 } 248 } 249 --count; 250 } 251 252 if (cr4save & CR4_SMAP) 253 lcr4(cr4save); 254 } 255 256 void 257 stacktrace_save_at(struct stacktrace *st, unsigned int skip) 258 { 259 struct callframe *frame, *lastframe, *limit; 260 struct pcb *pcb = curpcb; 261 262 st->st_count = 0; 263 264 if (pcb == NULL) 265 return; 266 267 frame = __builtin_frame_address(0); 268 KASSERT(INKERNEL(frame)); 269 limit = (struct callframe *)((struct trapframe *)pcb->pcb_kstack - 1); 270 271 while (st->st_count < STACKTRACE_MAX) { 272 if (skip == 0) 273 st->st_pc[st->st_count++] = frame->f_retaddr; 274 else 275 skip--; 276 277 lastframe = frame; 278 frame = frame->f_frame; 279 280 if (frame <= lastframe) 281 break; 282 if (frame >= limit) 283 break; 284 if (!INKERNEL(frame->f_retaddr)) 285 break; 286 } 287 } 288 289 void 290 stacktrace_save_utrace(struct stacktrace *st) 291 { 292 struct callframe f, *frame, *lastframe; 293 struct pcb *pcb = curpcb; 294 295 st->st_count = 0; 296 297 if (pcb == NULL) 298 return; 299 300 frame = __builtin_frame_address(0); 301 KASSERT(INKERNEL(frame)); 302 f = *frame; 303 304 while (st->st_count < STACKTRACE_MAX) { 305 if (f.f_retaddr != 0 && !INKERNEL(f.f_retaddr)) 306 st->st_pc[st->st_count++] = f.f_retaddr; 307 308 lastframe = frame; 309 frame = f.f_frame; 310 311 if (frame == NULL) 312 break; 313 if (INKERNEL(f.f_retaddr)) { 314 if (frame <= lastframe) 315 break; 316 f = *frame; 317 continue; 318 } 319 if (!INKERNEL(lastframe) && frame <= lastframe) 320 break; 321 if (copyin(frame, &f, sizeof(f)) != 0) 322 break; 323 } 324 } 325 326 vaddr_t 327 db_get_pc(struct trapframe *tf) 328 { 329 struct callframe *cf; 330 331 if (KERNELMODE(tf->tf_cs, tf->tf_eflags)) 332 cf = (struct callframe *)((long)&tf->tf_esp - sizeof(long)); 333 else 334 cf = (struct callframe *)(tf->tf_esp - sizeof(long)); 335 336 return db_get_value((vaddr_t)&cf->f_retaddr, sizeof(long), 0); 337 } 338 339 vaddr_t 340 db_get_probe_addr(struct trapframe *tf) 341 { 342 return tf->tf_eip - BKPT_SIZE; 343 } 344