xref: /netbsd-src/sys/arch/aarch64/aarch64/db_trace.c (revision cef8759bd76c1b621f8eab8faa6f208faabc2e15)
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];	/* &reg[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