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