1 /* $NetBSD: db_trace.c,v 1.12 2020/06/27 00:43:38 rin Exp $ */ 2 3 /* 4 * Copyright (c) 2017 Ryo Shimizu <ryo@nerv.org> 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 17 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 18 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19 * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, 20 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 21 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 22 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 24 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 25 * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 26 * POSSIBILITY OF SUCH DAMAGE. 27 */ 28 29 #include <sys/cdefs.h> 30 31 __KERNEL_RCSID(0, "$NetBSD: db_trace.c,v 1.12 2020/06/27 00:43:38 rin Exp $"); 32 33 #include <sys/param.h> 34 #include <sys/proc.h> 35 36 #include <aarch64/cpufunc.h> 37 #include <aarch64/db_machdep.h> 38 #include <aarch64/machdep.h> 39 #include <aarch64/armreg.h> 40 #include <aarch64/vmparam.h> 41 42 #include <uvm/uvm_extern.h> 43 44 #include <ddb/db_access.h> 45 #include <ddb/db_command.h> 46 #include <ddb/db_output.h> 47 #include <ddb/db_variables.h> 48 #include <ddb/db_sym.h> 49 #include <ddb/db_proc.h> 50 #include <ddb/db_lwp.h> 51 #include <ddb/db_extern.h> 52 #include <ddb/db_interface.h> 53 54 #define MAXBACKTRACE 128 /* against infinite loop */ 55 56 57 __CTASSERT(VM_MIN_ADDRESS == 0); 58 #define IN_USER_VM_ADDRESS(addr) \ 59 ((addr) < VM_MAX_ADDRESS) 60 #define IN_KERNEL_VM_ADDRESS(addr) \ 61 ((VM_MIN_KERNEL_ADDRESS <= (addr)) && ((addr) < VM_MAX_KERNEL_ADDRESS)) 62 63 64 static bool __unused 65 is_lwp(void *p) 66 { 67 lwp_t *lwp; 68 69 for (lwp = db_lwp_first(); lwp != NULL; lwp = db_lwp_next(lwp)) { 70 if (lwp == p) 71 return true; 72 } 73 return false; 74 } 75 76 static const char * 77 getlwpnamebysp(uint64_t sp) 78 { 79 #if defined(_KERNEL) 80 lwp_t *lwp; 81 82 for (lwp = db_lwp_first(); lwp != NULL; lwp = db_lwp_next(lwp)) { 83 uint64_t uarea = uvm_lwp_getuarea(lwp); 84 if ((uarea <= sp) && (sp < (uarea + USPACE))) { 85 return lwp->l_name; 86 } 87 } 88 #endif 89 return "unknown"; 90 } 91 92 #define TRACEFLAG_LOOKUPLWP 0x00000001 93 94 static void 95 pr_traceaddr(const char *prefix, uint64_t frame, uint64_t pc, int flags, 96 void (*pr)(const char *, ...) __printflike(1, 2)) 97 { 98 db_expr_t offset; 99 db_sym_t sym; 100 const char *name; 101 102 sym = db_search_symbol(pc, DB_STGY_ANY, &offset); 103 if (sym != DB_SYM_NULL) { 104 db_symbol_values(sym, &name, NULL); 105 106 if (flags & TRACEFLAG_LOOKUPLWP) { 107 (*pr)("%s %016lx %s %s() at %016lx ", 108 prefix, frame, getlwpnamebysp(frame), name, pc); 109 } else { 110 (*pr)("%s %016lx %s() at %016lx ", 111 prefix, frame, name, pc); 112 } 113 db_printsym(pc, DB_STGY_PROC, pr); 114 (*pr)("\n"); 115 } else { 116 if (flags & TRACEFLAG_LOOKUPLWP) { 117 (*pr)("%s %016lx %s ?() at %016lx\n", 118 prefix, frame, getlwpnamebysp(frame), pc); 119 } else { 120 (*pr)("%s %016lx ?() at %016lx\n", prefix, frame, pc); 121 } 122 } 123 } 124 125 void 126 db_stack_trace_print(db_expr_t addr, bool have_addr, db_expr_t count, 127 const char *modif, void (*pr)(const char *, ...) __printflike(1, 2)) 128 { 129 uint64_t lr, fp, lastlr, lastfp; 130 struct trapframe *tf = NULL; 131 int flags = 0; 132 bool trace_user = false; 133 bool trace_thread = false; 134 bool trace_lwp = false; 135 136 for (; *modif != '\0'; modif++) { 137 switch (*modif) { 138 case 'a': 139 trace_lwp = true; 140 trace_thread = false; 141 break; 142 case 'l': 143 break; 144 case 't': 145 trace_thread = true; 146 trace_lwp = false; 147 break; 148 case 'u': 149 trace_user = true; 150 break; 151 case 'x': 152 flags |= TRACEFLAG_LOOKUPLWP; 153 break; 154 default: 155 pr("usage: bt[/ulx] [frame-address][,count]\n"); 156 pr(" bt/t[ulx] [pid][,count]\n"); 157 pr(" bt/a[ulx] [lwpaddr][,count]\n"); 158 pr("\n"); 159 pr(" /x reverse lookup lwp name from sp\n"); 160 return; 161 } 162 } 163 164 #if defined(_KERNEL) 165 if (!have_addr) { 166 if (trace_lwp) { 167 addr = (db_expr_t)curlwp; 168 } else if (trace_thread) { 169 addr = curlwp->l_proc->p_pid; 170 } else { 171 tf = DDB_REGS; 172 } 173 } 174 #endif 175 176 if (trace_thread) { 177 proc_t *pp; 178 179 if ((pp = db_proc_find((pid_t)addr)) == 0) { 180 (*pr)("trace: pid %d: not found\n", (int)addr); 181 return; 182 } 183 db_read_bytes((db_addr_t)pp + offsetof(proc_t, p_lwps.lh_first), 184 sizeof(addr), (char *)&addr); 185 trace_thread = false; 186 trace_lwp = true; 187 } 188 189 #if 0 190 /* "/a" is abbreviated? */ 191 if (!trace_lwp && is_lwp(addr)) 192 trace_lwp = true; 193 #endif 194 195 if (trace_lwp) { 196 struct lwp l; 197 pid_t pid; 198 199 db_read_bytes(addr, sizeof(l), (char *)&l); 200 db_read_bytes((db_addr_t)l.l_proc + offsetof(proc_t, p_pid), 201 sizeof(pid), (char *)&pid); 202 203 #if defined(_KERNEL) 204 if (addr == (db_expr_t)curlwp) { 205 fp = (uint64_t)&DDB_REGS->tf_reg[29]; /* ®[29]={fp,lr} */ 206 tf = DDB_REGS; 207 (*pr)("trace: pid %d lid %d (curlwp) at tf %p\n", 208 pid, l.l_lid, tf); 209 } else 210 #endif 211 { 212 struct pcb *pcb = lwp_getpcb(&l); 213 214 db_read_bytes((db_addr_t)pcb + 215 offsetof(struct pcb, pcb_tf), 216 sizeof(tf), (char *)&tf); 217 if (tf != 0) { 218 db_read_bytes((db_addr_t)&tf->tf_reg[29], 219 sizeof(fp), (char *)&fp); 220 (*pr)("trace: pid %d lid %d at tf %p (in pcb)\n", 221 pid, l.l_lid, tf); 222 } 223 #if defined(MULTIPROCESSOR) && defined(_KERNEL) 224 else if (l.l_stat == LSONPROC || 225 (l.l_pflag & LP_RUNNING) != 0) { 226 227 /* running lwp on other cpus */ 228 extern struct trapframe *db_readytoswitch[]; 229 u_int index; 230 231 db_read_bytes((db_addr_t)l.l_cpu + 232 offsetof(struct cpu_info, ci_index), 233 sizeof(index), (char *)&index); 234 tf = db_readytoswitch[index]; 235 236 (*pr)("trace: pid %d lid %d at tf %p (in kdb_trap)\n", 237 pid, l.l_lid, tf); 238 } 239 #endif 240 else { 241 (*pr)("trace: no trapframe found for lwp: %p\n", (void *)addr); 242 } 243 } 244 } else if (tf == NULL) { 245 fp = addr; 246 pr("trace fp %016lx\n", fp); 247 } else { 248 pr("trace tf %p\n", tf); 249 } 250 251 if (count > MAXBACKTRACE) 252 count = MAXBACKTRACE; 253 254 if (tf != NULL) { 255 #if defined(_KERNEL) 256 bool is_switchframe = (tf->tf_sp == 0); 257 (*pr)("---- %s %p (%zu bytes) ----\n", 258 is_switchframe ? "switchframe" : "trapframe", 259 tf, sizeof(*tf)); 260 if (is_switchframe) 261 dump_switchframe(tf, pr); 262 else 263 dump_trapframe(tf, pr); 264 (*pr)("------------------------" 265 "------------------------\n"); 266 267 #endif 268 lastfp = lastlr = lr = fp = 0; 269 270 db_read_bytes((db_addr_t)&tf->tf_pc, sizeof(lr), (char *)&lr); 271 db_read_bytes((db_addr_t)&tf->tf_reg[29], sizeof(fp), (char *)&fp); 272 lr = aarch64_strip_pac(lr); 273 274 pr_traceaddr("fp", fp, lr - 4, flags, pr); 275 } 276 277 for (; (count > 0) && (fp != 0); count--) { 278 279 lastfp = fp; 280 fp = lr = 0; 281 /* 282 * normal stack frame 283 * fp[0] saved fp(x29) value 284 * fp[1] saved lr(x30) value 285 */ 286 db_read_bytes(lastfp + 0, sizeof(fp), (char *)&fp); 287 db_read_bytes(lastfp + 8, sizeof(lr), (char *)&lr); 288 lr = aarch64_strip_pac(lr); 289 290 if (!trace_user && IN_USER_VM_ADDRESS(lr)) 291 break; 292 293 #if defined(_KERNEL) 294 extern char el1_trap[]; /* XXX */ 295 extern char el0_trap[]; /* XXX */ 296 if (((char *)(lr - 4) == (char *)el0_trap) || 297 ((char *)(lr - 4) == (char *)el1_trap)) { 298 299 tf = (struct trapframe *)fp; 300 301 lastfp = (uint64_t)tf; 302 lastlr = lr; 303 lr = fp = 0; 304 db_read_bytes((db_addr_t)&tf->tf_pc, sizeof(lr), (char *)&lr); 305 db_read_bytes((db_addr_t)&tf->tf_reg[29], sizeof(fp), (char *)&fp); 306 lr = aarch64_strip_pac(lr); 307 308 /* 309 * no need to display the frame of el0_trap 310 * of kernel thread 311 */ 312 if (((char *)(lastlr - 4) == (char *)el0_trap) && 313 (lr == 0)) 314 break; 315 316 pr_traceaddr("tf", (db_addr_t)tf, lastlr - 4, flags, pr); 317 318 if (lr == 0) 319 break; 320 321 (*pr)("---- trapframe %p (%zu bytes) ----\n", 322 tf, sizeof(*tf)); 323 dump_trapframe(tf, pr); 324 (*pr)("------------------------" 325 "------------------------\n"); 326 tf = NULL; 327 328 if (!trace_user && IN_USER_VM_ADDRESS(lr)) 329 break; 330 331 pr_traceaddr("fp", fp, lr, flags, pr); 332 333 } else 334 #endif 335 { 336 pr_traceaddr("fp", fp, lr - 4, flags, pr); 337 } 338 } 339 } 340