1 /* $NetBSD: db_trace.c,v 1.6 2022/12/24 14:14:52 uwe Exp $ */ 2 3 /* 4 * Mach Operating System 5 * Copyright (c) 1991,1990 Carnegie Mellon University 6 * All Rights Reserved. 7 * 8 * Permission to use, copy, modify and distribute this software and its 9 * documentation is hereby granted, provided that both the copyright 10 * notice and this permission notice appear in all copies of the 11 * software, derivative works or modified versions, and any portions 12 * thereof, and that both notices appear in supporting documentation. 13 * 14 * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS" 15 * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR 16 * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. 17 * 18 * Carnegie Mellon requests users of this software to return to 19 * 20 * Software Distribution Coordinator or Software.Distribution (at) CS.CMU.EDU 21 * School of Computer Science 22 * Carnegie Mellon University 23 * Pittsburgh PA 15213-3890 24 * 25 * any improvements or extensions that they make and grant Carnegie the 26 * rights to redistribute these changes. 27 */ 28 29 #include <sys/cdefs.h> 30 __KERNEL_RCSID(0, "$NetBSD: db_trace.c,v 1.6 2022/12/24 14:14:52 uwe Exp $"); 31 32 #include <sys/param.h> 33 #include <sys/systm.h> 34 #include <sys/proc.h> 35 36 #include <uvm/uvm_prot.h> 37 #include <uvm/uvm_pmap.h> 38 39 #include <machine/frame.h> 40 #include <machine/trap.h> 41 #include <machine/intrdefs.h> 42 #include <machine/pmap.h> 43 44 #include <machine/db_machdep.h> 45 #include <ddb/db_sym.h> 46 #include <ddb/db_access.h> 47 #include <ddb/db_variables.h> 48 #include <ddb/db_output.h> 49 #include <ddb/db_interface.h> 50 #include <ddb/db_user.h> 51 #include <ddb/db_proc.h> 52 #include <ddb/db_command.h> 53 #include <x86/db_machdep.h> 54 55 int 56 db_x86_regop(const struct db_variable *vp, db_expr_t *val, int opcode) 57 { 58 db_expr_t *regaddr = 59 (db_expr_t *)(((uint8_t *)DDB_REGS) + ((size_t)vp->valuep)); 60 61 switch (opcode) { 62 case DB_VAR_GET: 63 *val = *regaddr; 64 break; 65 case DB_VAR_SET: 66 *regaddr = *val; 67 break; 68 default: 69 db_printf("db_x86_regop: unknown op %d", opcode); 70 db_error(NULL); 71 } 72 return 0; 73 } 74 75 /* 76 * Stack trace. 77 */ 78 79 #if 0 80 db_addr_t db_trap_symbol_value = 0; 81 db_addr_t db_syscall_symbol_value = 0; 82 db_addr_t db_kdintr_symbol_value = 0; 83 bool db_trace_symbols_found = false; 84 85 void db_find_trace_symbols(void); 86 87 void 88 db_find_trace_symbols(void) 89 { 90 db_expr_t value; 91 92 if (db_value_of_name("_trap", &value)) 93 db_trap_symbol_value = (db_addr_t) value; 94 if (db_value_of_name("_kdintr", &value)) 95 db_kdintr_symbol_value = (db_addr_t) value; 96 if (db_value_of_name("_syscall", &value)) 97 db_syscall_symbol_value = (db_addr_t) value; 98 db_trace_symbols_found = true; 99 } 100 #endif 101 102 #define set_frame_callpc() do { \ 103 frame = (long *)ddb_regs.tf_bp; \ 104 callpc = (db_addr_t)ddb_regs.tf_ip; \ 105 } while (/*CONSTCCOND*/0) 106 107 void 108 db_stack_trace_print(db_expr_t addr, bool have_addr, db_expr_t count, 109 const char *modif, void (*pr)(const char *, ...)) 110 { 111 long *frame, *lastframe; 112 long *retaddr, *arg0; 113 long *argp; 114 db_addr_t callpc; 115 int is_trap; 116 bool kernel_only = true; 117 bool trace_thread = false; 118 bool lwpaddr = false; 119 120 #if 0 121 if (!db_trace_symbols_found) 122 db_find_trace_symbols(); 123 #endif 124 125 { 126 const char *cp = modif; 127 char c; 128 129 while ((c = *cp++) != 0) { 130 if (c == 'a') { 131 lwpaddr = true; 132 trace_thread = true; 133 } 134 if (c == 't') 135 trace_thread = true; 136 if (c == 'u') 137 kernel_only = false; 138 } 139 } 140 141 if (have_addr && trace_thread) { 142 struct pcb *pcb; 143 proc_t p; 144 lwp_t l; 145 146 if (lwpaddr) { 147 db_read_bytes(addr, sizeof(l), 148 (char *)&l); 149 db_read_bytes((db_addr_t)l.l_proc, 150 sizeof(p), (char *)&p); 151 (*pr)("trace: pid %d ", p.p_pid); 152 } else { 153 proc_t *pp; 154 155 (*pr)("trace: pid %d ", (int)addr); 156 if ((pp = db_proc_find((pid_t)addr)) == 0) { 157 (*pr)("not found\n"); 158 return; 159 } 160 db_read_bytes((db_addr_t)pp, sizeof(p), (char *)&p); 161 addr = (db_addr_t)p.p_lwps.lh_first; 162 db_read_bytes(addr, sizeof(l), (char *)&l); 163 } 164 (*pr)("lid %d ", l.l_lid); 165 pcb = lwp_getpcb(&l); 166 #ifdef _KERNEL 167 if (l.l_proc == curproc && (lwp_t *)addr == curlwp) 168 set_frame_callpc(); 169 else 170 #endif 171 { 172 db_read_bytes((db_addr_t)&pcb->pcb_bp, 173 sizeof(frame), (char *)&frame); 174 db_read_bytes((db_addr_t)(frame + 1), 175 sizeof(callpc), (char *)&callpc); 176 db_read_bytes((db_addr_t)frame, 177 sizeof(frame), (char *)&frame); 178 } 179 (*pr)("at %p\n", frame); 180 } else if (have_addr) { 181 frame = (long *)addr; 182 db_read_bytes((db_addr_t)(frame + 1), 183 sizeof(callpc), (char *)&callpc); 184 db_read_bytes((db_addr_t)frame, 185 sizeof(frame), (char *)&frame); 186 } else { 187 set_frame_callpc(); 188 } 189 190 retaddr = frame + 1; 191 arg0 = frame + 2; 192 193 lastframe = NULL; 194 while (count && frame != 0) { 195 int narg; 196 const char *name; 197 db_expr_t offset; 198 db_sym_t sym; 199 char *argnames[MAXNARG], **argnp = NULL; 200 db_addr_t lastcallpc; 201 202 name = "?"; 203 is_trap = NONE; 204 offset = 0; 205 sym = db_frame_info(frame, callpc, &name, &offset, &is_trap, 206 &narg); 207 208 if (lastframe == NULL && sym == DB_SYM_NULL && callpc != 0) { 209 /* Symbol not found, peek at code */ 210 u_long instr = db_get_value(callpc, 4, false); 211 212 offset = 1; 213 if ( 214 #ifdef __x86_64__ 215 (instr == 0xe5894855 || 216 /* enter: pushq %rbp, movq %rsp, %rbp */ 217 (instr & 0x00ffffff) == 0x0048e589 218 /* enter+1: movq %rsp, %rbp */) 219 #else 220 ((instr & 0x00ffffff) == 0x00e58955 || 221 /* enter: pushl %ebp, movl %esp, %ebp */ 222 (instr & 0x0000ffff) == 0x0000e589 223 /* enter+1: movl %esp, %ebp */) 224 #endif 225 ) 226 { 227 offset = 0; 228 } 229 } 230 231 if (is_trap == NONE) { 232 if (db_sym_numargs(sym, &narg, argnames)) 233 argnp = argnames; 234 else 235 narg = db_numargs(frame); 236 } 237 238 (*pr)("%s(", name); 239 240 if (lastframe == NULL && offset == 0 && !have_addr) { 241 /* 242 * We have a breakpoint before the frame is set up 243 * Use %[er]sp instead 244 */ 245 argp = (long *)&((struct x86_frame *) 246 (ddb_regs.tf_sp-sizeof(long)))->f_arg0; 247 } else { 248 argp = frame + 2; 249 } 250 251 while (narg) { 252 if (argnp) 253 (*pr)("%s=", *argnp++); 254 (*pr)("%lx", db_get_value((long)argp, sizeof(long), 255 false)); 256 argp++; 257 if (--narg != 0) 258 (*pr)(","); 259 } 260 (*pr)(") at "); 261 db_printsym(callpc, DB_STGY_PROC, pr); 262 (*pr)("\n"); 263 264 if (lastframe == NULL && offset == 0 && !have_addr) { 265 /* Frame really belongs to next callpc */ 266 struct x86_frame *fp = (void *) 267 (ddb_regs.tf_sp-sizeof(long)); 268 lastframe = (long *)fp; 269 callpc = (db_addr_t) 270 db_get_value((db_addr_t)&fp->f_retaddr, 271 sizeof(long), false); 272 273 continue; 274 } 275 276 lastframe = frame; 277 lastcallpc = callpc; 278 if (!db_nextframe(&frame, &retaddr, &arg0, &callpc, 279 frame + 2, is_trap, pr)) 280 break; 281 282 if (INKERNEL((long)frame)) { 283 /* staying in kernel */ 284 #ifdef __i386__ 285 if (!db_intrstack_p(frame) && 286 db_intrstack_p(lastframe)) { 287 (*pr)("--- switch to interrupt stack ---\n"); 288 } else 289 #endif 290 if (frame < lastframe || 291 (frame == lastframe && callpc == lastcallpc)) { 292 (*pr)("Bad frame pointer: %p\n", frame); 293 break; 294 } 295 } else if (INKERNEL((long)lastframe)) { 296 /* switch from user to kernel */ 297 if (kernel_only) 298 break; /* kernel stack only */ 299 } else { 300 /* in user */ 301 if (frame <= lastframe) { 302 (*pr)("Bad user frame pointer: %p\n", frame); 303 break; 304 } 305 } 306 --count; 307 } 308 309 if (count && is_trap != NONE) { 310 db_printsym(callpc, DB_STGY_XTRN, pr); 311 (*pr)(":\n"); 312 } 313 } 314