1 /* $OpenBSD: db_trace.c,v 1.56 2024/11/07 16:02:29 miod Exp $ */ 2 /* $NetBSD: db_trace.c,v 1.1 2003/04/26 18:39:27 fvdl 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 the 27 * 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 #include <machine/frame.h> 38 #include <machine/trap.h> 39 40 #include <ddb/db_sym.h> 41 #include <ddb/db_access.h> 42 #include <ddb/db_variables.h> 43 #include <ddb/db_output.h> 44 45 /* 46 * Machine register set. 47 */ 48 struct db_variable db_regs[] = { 49 { "rdi", (long *)&ddb_regs.tf_rdi, FCN_NULL }, 50 { "rsi", (long *)&ddb_regs.tf_rsi, FCN_NULL }, 51 { "rbp", (long *)&ddb_regs.tf_rbp, FCN_NULL }, 52 { "rbx", (long *)&ddb_regs.tf_rbx, FCN_NULL }, 53 { "rdx", (long *)&ddb_regs.tf_rdx, FCN_NULL }, 54 { "rcx", (long *)&ddb_regs.tf_rcx, FCN_NULL }, 55 { "rax", (long *)&ddb_regs.tf_rax, FCN_NULL }, 56 { "r8", (long *)&ddb_regs.tf_r8, FCN_NULL }, 57 { "r9", (long *)&ddb_regs.tf_r9, FCN_NULL }, 58 { "r10", (long *)&ddb_regs.tf_r10, FCN_NULL }, 59 { "r11", (long *)&ddb_regs.tf_r11, FCN_NULL }, 60 { "r12", (long *)&ddb_regs.tf_r12, FCN_NULL }, 61 { "r13", (long *)&ddb_regs.tf_r13, FCN_NULL }, 62 { "r14", (long *)&ddb_regs.tf_r14, FCN_NULL }, 63 { "r15", (long *)&ddb_regs.tf_r15, FCN_NULL }, 64 { "rip", (long *)&ddb_regs.tf_rip, FCN_NULL }, 65 { "cs", (long *)&ddb_regs.tf_cs, FCN_NULL }, 66 { "rflags", (long *)&ddb_regs.tf_rflags, FCN_NULL }, 67 { "rsp", (long *)&ddb_regs.tf_rsp, FCN_NULL }, 68 { "ss", (long *)&ddb_regs.tf_ss, FCN_NULL }, 69 }; 70 struct db_variable * db_eregs = db_regs + nitems(db_regs); 71 72 /* 73 * Stack trace. 74 */ 75 #define INKERNEL(va) (((vaddr_t)(va)) >= VM_MIN_KERNEL_ADDRESS) 76 77 78 const unsigned long *db_reg_args[6] = { 79 (unsigned long *)&ddb_regs.tf_rdi, 80 (unsigned long *)&ddb_regs.tf_rsi, 81 (unsigned long *)&ddb_regs.tf_rdx, 82 (unsigned long *)&ddb_regs.tf_rcx, 83 (unsigned long *)&ddb_regs.tf_r8, 84 (unsigned long *)&ddb_regs.tf_r9, 85 }; 86 87 void 88 db_stack_trace_print(db_expr_t addr, int have_addr, db_expr_t count, 89 char *modif, int (*pr)(const char *, ...)) 90 { 91 struct callframe *frame, *lastframe; 92 unsigned long *argp, *arg0; 93 vaddr_t callpc; 94 unsigned int cr4save = CR4_SMEP|CR4_SMAP; 95 int kernel_only = 1; 96 int trace_proc = 0; 97 struct proc *p; 98 99 { 100 char *cp = modif; 101 char c; 102 103 while ((c = *cp++) != 0) { 104 if (c == 't') 105 trace_proc = 1; 106 if (c == 'u') 107 kernel_only = 0; 108 } 109 } 110 111 if (trace_proc) { 112 p = tfind((pid_t)addr); 113 if (p == NULL) { 114 (*pr) ("not found\n"); 115 return; 116 } 117 } 118 119 cr4save = rcr4(); 120 if (cr4save & CR4_SMAP) 121 lcr4(cr4save & ~CR4_SMAP); 122 123 if (!have_addr) { 124 frame = (struct callframe *)ddb_regs.tf_rbp; 125 callpc = (vaddr_t)ddb_regs.tf_rip; 126 } else if (trace_proc) { 127 frame = (struct callframe *)p->p_addr->u_pcb.pcb_rbp; 128 callpc = (vaddr_t) 129 db_get_value((vaddr_t)&frame->f_retaddr, 8, 0); 130 frame = (struct callframe *)frame->f_frame; 131 } else { 132 frame = (struct callframe *)addr; 133 callpc = (vaddr_t) 134 db_get_value((vaddr_t)&frame->f_retaddr, 8, 0); 135 frame = (struct callframe *)frame->f_frame; 136 } 137 138 lastframe = 0; 139 while (count && frame != 0) { 140 int narg; 141 unsigned int i; 142 const char * name; 143 db_expr_t offset; 144 Elf_Sym * sym; 145 146 if (INKERNEL(frame)) { 147 sym = db_search_symbol(callpc, DB_STGY_ANY, &offset); 148 db_symbol_values(sym, &name, NULL); 149 } else { 150 sym = NULL; 151 name = NULL; 152 } 153 154 if (lastframe == 0 && sym == NULL && callpc != 0) { 155 /* Symbol not found, peek at code */ 156 unsigned long instr = db_get_value(callpc, 8, 0); 157 158 offset = 1; 159 if (instr == 0xe5894855 || 160 /* enter: pushq %rbp, movq %rsp, %rbp */ 161 (instr & 0x00ffffff) == 0x00e58948 162 /* enter+1: movq %rsp, %rbp */) { 163 offset = 0; 164 } 165 } 166 167 if ((narg = db_ctf_func_numargs(sym)) < 0) 168 narg = 6; 169 170 if (name == NULL) 171 (*pr)("%lx(", callpc); 172 else 173 (*pr)("%s(", name); 174 175 if (lastframe == 0 && offset == 0 && !have_addr) { 176 /* We have a breakpoint before the frame is set up */ 177 for (i = 0; i < narg; i++) { 178 (*pr)("%lx", *db_reg_args[i]); 179 if (--narg != 0) 180 (*pr)(","); 181 } 182 183 /* Use %rsp instead */ 184 arg0 = 185 &((struct callframe *)(ddb_regs.tf_rsp-8))->f_arg0; 186 } else { 187 argp = (unsigned long *)frame; 188 for (i = narg; i > 0; i--) { 189 argp--; 190 (*pr)("%lx", db_get_value((vaddr_t)argp, 191 sizeof(*argp), 0)); 192 if (--narg != 0) 193 (*pr)(","); 194 } 195 196 arg0 = &frame->f_arg0; 197 } 198 199 for (argp = arg0; narg > 0; ) { 200 (*pr)("%lx", db_get_value((vaddr_t)argp, 201 sizeof(*argp), 0)); 202 argp++; 203 if (--narg != 0) 204 (*pr)(","); 205 } 206 (*pr)(") at "); 207 db_printsym(callpc, DB_STGY_PROC, pr); 208 (*pr)("\n"); 209 210 if (lastframe == 0 && offset == 0 && !have_addr) { 211 /* Frame really belongs to next callpc */ 212 lastframe = (struct callframe *)(ddb_regs.tf_rsp-8); 213 callpc = (vaddr_t) 214 db_get_value((vaddr_t)&lastframe->f_retaddr, 215 8, 0); 216 continue; 217 } 218 219 lastframe = frame; 220 callpc = (vaddr_t)db_get_value( 221 (vaddr_t)&frame->f_retaddr, 8, 0); 222 frame = (struct callframe *)db_get_value( 223 (vaddr_t)&frame->f_frame, 8, 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 (*pr)("end trace frame: 0x%lx, count: %d\n", frame, count); 252 253 if (cr4save & CR4_SMAP) 254 lcr4(cr4save); 255 } 256 257 void 258 stacktrace_save_at(struct stacktrace *st, unsigned int skip) 259 { 260 struct callframe *frame, *lastframe, *limit; 261 struct pcb *pcb = curpcb; 262 263 st->st_count = 0; 264 265 if (pcb == NULL) 266 return; 267 268 frame = __builtin_frame_address(0); 269 KASSERT(INKERNEL(frame)); 270 limit = (struct callframe *)((struct trapframe *)pcb->pcb_kstack - 1); 271 272 while (st->st_count < STACKTRACE_MAX) { 273 if (skip == 0) 274 st->st_pc[st->st_count++] = frame->f_retaddr; 275 else 276 skip--; 277 278 lastframe = frame; 279 frame = frame->f_frame; 280 281 if (frame <= lastframe) 282 break; 283 if (frame >= limit) 284 break; 285 if (!INKERNEL(frame->f_retaddr)) 286 break; 287 } 288 } 289 290 void 291 stacktrace_save_utrace(struct stacktrace *st) 292 { 293 struct callframe f, *frame, *lastframe; 294 struct pcb *pcb = curpcb; 295 296 st->st_count = 0; 297 298 if (pcb == NULL) 299 return; 300 301 frame = __builtin_frame_address(0); 302 KASSERT(INKERNEL(frame)); 303 f = *frame; 304 305 while (st->st_count < STACKTRACE_MAX) { 306 if (f.f_retaddr != 0 && !INKERNEL(f.f_retaddr)) 307 st->st_pc[st->st_count++] = f.f_retaddr; 308 309 lastframe = frame; 310 frame = f.f_frame; 311 312 if (frame == NULL) 313 break; 314 if (INKERNEL(f.f_retaddr)) { 315 if (frame <= lastframe) 316 break; 317 f = *frame; 318 continue; 319 } 320 if (!INKERNEL(lastframe) && frame <= lastframe) 321 break; 322 if (copyin(frame, &f, sizeof(f)) != 0) 323 break; 324 } 325 } 326 327 vaddr_t 328 db_get_pc(struct trapframe *tf) 329 { 330 struct callframe *cf = (struct callframe *)(tf->tf_rsp - sizeof(long)); 331 332 return db_get_value((vaddr_t)&cf->f_retaddr, sizeof(long), 0); 333 } 334 335 vaddr_t 336 db_get_probe_addr(struct trapframe *tf) 337 { 338 return tf->tf_rip - BKPT_SIZE; 339 } 340