1 /* $NetBSD: db_trace.c,v 1.4 2023/05/07 12:41:48 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.4 2023/05/07 12:41:48 skrll Exp $");
35
36 #include <sys/param.h>
37 #include <sys/systm.h>
38
39 #include <riscv/db_machdep.h>
40
41 #include <uvm/uvm_extern.h>
42
43 #include <ddb/db_access.h>
44 #include <ddb/db_command.h>
45 #include <ddb/db_output.h>
46 #include <ddb/db_variables.h>
47 #include <ddb/db_sym.h>
48 #include <ddb/db_proc.h>
49 #include <ddb/db_lwp.h>
50 #include <ddb/db_extern.h>
51 #include <ddb/db_interface.h>
52
53 #define MAXBACKTRACE 128 /* against infinite loop */
54 #define TRACEFLAG_LOOKUPLWP 0x00000001
55
56 #define IN_USER_VM_ADDRESS(addr) \
57 (VM_MIN_ADDRESS <= (addr) && (addr) < VM_MAX_ADDRESS)
58 #define IN_KERNEL_VM_ADDRESS(addr) \
59 (VM_MIN_KERNEL_ADDRESS <= (addr) && (addr) < VM_MAX_KERNEL_ADDRESS)
60
61 static bool __unused
is_lwp(void * p)62 is_lwp(void *p)
63 {
64 lwp_t *lwp;
65
66 for (lwp = db_lwp_first(); lwp != NULL; lwp = db_lwp_next(lwp)) {
67 if (lwp == p)
68 return true;
69 }
70 return false;
71 }
72
73 static const char *
getlwpnamebysp(uint64_t sp)74 getlwpnamebysp(uint64_t sp)
75 {
76 #if defined(_KERNEL)
77 lwp_t *lwp;
78
79 for (lwp = db_lwp_first(); lwp != NULL; lwp = db_lwp_next(lwp)) {
80 uint64_t uarea = uvm_lwp_getuarea(lwp);
81 if ((uarea <= sp) && (sp < (uarea + USPACE))) {
82 return lwp->l_name;
83 }
84 }
85 #endif
86 return "unknown";
87 }
88
89 static void
90 pr_traceaddr(const char *prefix, uint64_t frame, uint64_t pc, int flags,
91 void (*pr)(const char *, ...) __printflike(1, 2))
92 {
93 db_expr_t offset;
94 db_sym_t sym;
95 const char *name;
96
97 sym = db_search_symbol(pc, DB_STGY_ANY, &offset);
98 if (sym != DB_SYM_NULL) {
99 db_symbol_values(sym, &name, NULL);
100
101 if (flags & TRACEFLAG_LOOKUPLWP) {
102 (*pr)("%s %016" PRIx64 " %s %s() at %016" PRIx64,
103 prefix, frame, getlwpnamebysp(frame), name, pc);
104 } else {
105 (*pr)("%s %016" PRIx64 " %s() at %016" PRIx64 " ",
106 prefix, frame, name, pc);
107 }
108 db_printsym(pc, DB_STGY_PROC, pr);
109 (*pr)("\n");
110 } else {
111 if (flags & TRACEFLAG_LOOKUPLWP) {
112 (*pr)("%s %016" PRIx64 " %s ?() at %016" PRIx64 "\n",
113 prefix, frame, getlwpnamebysp(frame), pc);
114 } else {
115 (*pr)("%s %016" PRIx64 " ?() at %016" PRIx64 "\n", prefix, frame, pc);
116 }
117 }
118 }
119
120 void
121 db_stack_trace_print(db_expr_t addr, bool have_addr, db_expr_t count,
122 const char *modif, void (*pr)(const char *, ...) __printflike(1, 2))
123 {
124 register_t ra, fp, lastra, lastfp;
125 struct trapframe *tf = NULL;
126 int flags = 0;
127 bool trace_user = false;
128 bool trace_thread = false;
129 bool trace_lwp = false;
130
131 printf("have_addr: %s\n", have_addr ? "true" : "false");
132 if (have_addr)
133 printf("addr: %lx\n", addr);
134 printf("count: %ld\n", count);
135 printf("modif: %s\n", modif);
136
137 for (; *modif != '\0'; modif++) {
138 switch (*modif) {
139 case 'a':
140 trace_lwp = true;
141 trace_thread = false;
142 break;
143 case 'l':
144 break;
145 case 't':
146 trace_thread = true;
147 trace_lwp = false;
148 break;
149 case 'u':
150 trace_user = true;
151 break;
152 case 'x':
153 flags |= TRACEFLAG_LOOKUPLWP;
154 break;
155 default:
156 pr("usage: bt[/ulx] [frame-address][,count]\n");
157 pr(" bt/t[ulx] [pid][,count]\n");
158 pr(" bt/a[ulx] [lwpaddr][,count]\n");
159 pr("\n");
160 pr(" /x reverse lookup lwp name from sp\n");
161 return;
162 }
163 }
164
165 #if defined(_KERNEL)
166 if (!have_addr) {
167 if (trace_lwp) {
168 addr = (db_expr_t)curlwp;
169 } else if (trace_thread) {
170 addr = curlwp->l_proc->p_pid;
171 } else {
172 tf = DDB_REGS;
173 }
174 }
175 #endif
176
177 if (trace_thread) {
178 proc_t *pp, p;
179
180 if ((pp = db_proc_find((pid_t)addr)) == 0) {
181 (*pr)("trace: pid %d: not found\n", (int)addr);
182 return;
183 }
184 db_read_bytes((db_addr_t)pp, sizeof(p), (char *)&p);
185 addr = (db_addr_t)p.p_lwps.lh_first;
186 trace_thread = false;
187 trace_lwp = true;
188 }
189
190 #if 0
191 /* "/a" is abbreviated? */
192 if (!trace_lwp && is_lwp(addr))
193 trace_lwp = true;
194 #endif
195
196 if (trace_lwp) {
197 proc_t p;
198 struct lwp l;
199
200 db_read_bytes(addr, sizeof(l), (char *)&l);
201 db_read_bytes((db_addr_t)l.l_proc, sizeof(p), (char *)&p);
202
203 #if defined(_KERNEL)
204 if (addr == (db_expr_t)curlwp) {
205 fp = (register_t)&DDB_REGS->tf_s0; /* s0 = fp */
206 tf = DDB_REGS;
207 (*pr)("trace: pid %d lid %d (curlwp) at tf %p\n",
208 p.p_pid, l.l_lid, tf);
209 } else
210 #endif
211 {
212 tf = l.l_md.md_ktf;
213 db_read_bytes((db_addr_t)&tf->tf_s0, sizeof(fp), (char *)&fp);
214 (*pr)("trace: pid %d lid %d at tf %p\n",
215 p.p_pid, l.l_lid, tf);
216 }
217 } else if (tf == NULL) {
218 fp = addr;
219 pr("trace fp %016" PRIxREGISTER "\n", fp);
220 } else {
221 pr("trace tf %p\n", tf);
222 }
223
224 if (count > MAXBACKTRACE)
225 count = MAXBACKTRACE;
226
227 if (tf != NULL) {
228 #if defined(_KERNEL)
229 (*pr)("---- trapframe %p (%zu bytes) ----\n",
230 tf, sizeof(*tf));
231 dump_trapframe(tf, pr);
232 (*pr)("------------------------"
233 "------------------------\n");
234
235 #endif
236 lastfp = lastra = ra = fp = 0;
237 db_read_bytes((db_addr_t)&tf->tf_ra, sizeof(ra), (char *)&ra);
238 db_read_bytes((db_addr_t)&tf->tf_s0, sizeof(fp), (char *)&fp);
239
240 pr_traceaddr("fp", fp, ra - 4, flags, pr);
241 }
242 for (; (count > 0) && (fp != 0); count--) {
243
244 lastfp = fp;
245 fp = ra = 0;
246 /*
247 * normal stack frame
248 * fp[-1] saved fp(s0) value
249 * fp[-2] saved ra value
250 */
251 db_read_bytes(lastfp - 1 * sizeof(register_t), sizeof(ra), (char *)&ra);
252 db_read_bytes(lastfp - 2 * sizeof(register_t), sizeof(fp), (char *)&fp);
253
254 if (!trace_user && (IN_USER_VM_ADDRESS(ra) || IN_USER_VM_ADDRESS(fp)))
255 break;
256 #if defined(_KERNEL)
257 extern char exception_kernexit[];
258
259 if (((char *)ra == (char *)exception_kernexit)) {
260
261 tf = (struct trapframe *)lastfp;
262
263 lastra = ra;
264 ra = fp = 0;
265 db_read_bytes((db_addr_t)&tf->tf_pc, sizeof(ra), (char *)&ra);
266 db_read_bytes((db_addr_t)&tf->tf_s0, sizeof(fp), (char *)&fp);
267
268 pr_traceaddr("tf", (db_addr_t)tf, lastra, flags, pr);
269
270 (*pr)("---- trapframe %p (%zu bytes) ----\n",
271 tf, sizeof(*tf));
272 dump_trapframe(tf, pr);
273 (*pr)("------------------------"
274 "------------------------\n");
275 if (ra == 0)
276 break;
277 tf = NULL;
278
279 if (!trace_user && IN_USER_VM_ADDRESS(ra))
280 break;
281
282 pr_traceaddr("fp", fp, ra, flags, pr);
283
284 } else
285 #endif
286 {
287 pr_traceaddr("fp", fp, ra - 4, flags, pr);
288 }
289 }
290 }
291