xref: /netbsd-src/sys/arch/alpha/alpha/db_trace.c (revision 2d93273ebd9d1840d6d98e634f2f7a05b707a1b7)
1 /* $NetBSD: db_trace.c,v 1.39 2023/11/21 22:19:12 thorpej Exp $ */
2 
3 /*-
4  * Copyright (c) 1999 The NetBSD Foundation, Inc.
5  * All rights reserved.
6  *
7  * This code is derived from software contributed to The NetBSD Foundation
8  * by Jason R. Thorpe of the Numerical Aerospace Simulation Facility,
9  * NASA Ames Research Center.
10  *
11  * This code is derived from software contributed to The NetBSD Foundation
12  * by Ross Harvey.
13  *
14  * Redistribution and use in source and binary forms, with or without
15  * modification, are permitted provided that the following conditions
16  * are met:
17  * 1. Redistributions of source code must retain the above copyright
18  *    notice, this list of conditions and the following disclaimer.
19  * 2. Redistributions in binary form must reproduce the above copyright
20  *    notice, this list of conditions and the following disclaimer in the
21  *    documentation and/or other materials provided with the distribution.
22  *
23  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
24  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
25  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
26  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
27  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
28  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
29  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
30  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
31  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
32  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
33  * POSSIBILITY OF SUCH DAMAGE.
34  */
35 
36 #include <sys/cdefs.h>			/* RCS ID & Copyright macro defns */
37 
38 __KERNEL_RCSID(0, "$NetBSD: db_trace.c,v 1.39 2023/11/21 22:19:12 thorpej Exp $");
39 
40 #include <sys/param.h>
41 #include <sys/systm.h>
42 #include <sys/proc.h>
43 
44 #include <machine/alpha.h>
45 #include <machine/db_machdep.h>
46 
47 #include <machine/alpha_instruction.h>
48 
49 #include <ddb/db_sym.h>
50 #include <ddb/db_access.h>
51 #include <ddb/db_variables.h>
52 #include <ddb/db_output.h>
53 #include <ddb/db_interface.h>
54 
55 /*
56  * Information about the `standard' Alpha function prologue.
57  */
58 struct prologue_info {
59 	int	pi_reg_offset[32]; /* offset of registers in stack frame */
60 	uint32_t pi_regmask;	   /* which registers are in frame */
61 	int	pi_frame_size;	   /* frame size */
62 };
63 
64 /*
65  * Decode the function prologue for the function we're in, and note
66  * which registers are stored where, and how large the stack frame is.
67  */
68 static void
decode_prologue(db_addr_t callpc,db_addr_t func,struct prologue_info * pi,void (* pr)(const char *,...))69 decode_prologue(db_addr_t callpc, db_addr_t func,
70     struct prologue_info *pi, void (*pr)(const char *, ...))
71 {
72 	long signed_immediate;
73 	alpha_instruction ins;
74 	db_addr_t pc;
75 
76 	pi->pi_regmask = 0;
77 	pi->pi_frame_size = 0;
78 
79 #define	CHECK_FRAMESIZE							\
80 do {									\
81 	if (pi->pi_frame_size != 0) {					\
82 		(*pr)("frame size botch: adjust register offsets?\n"); \
83 	}								\
84 } while (0)
85 
86 	for (pc = func; pc < callpc; pc += sizeof(alpha_instruction)) {
87 		db_read_bytes(pc, sizeof(ins.bits), (char *)&ins.bits);
88 
89 		if (ins.mem_format.opcode == op_lda &&
90 		    ins.mem_format.ra == 30 &&
91 		    ins.mem_format.rb == 30) {
92 			/*
93 			 * GCC 2.7-style stack adjust:
94 			 *
95 			 *	lda	sp, -64(sp)
96 			 */
97 			signed_immediate = (long)ins.mem_format.displacement;
98 			/*
99 			 * The assumption here is that a positive
100 			 * stack offset is the function epilogue,
101 			 * which may come before callpc when an
102 			 * aggressive optimizer (like GCC 3.3 or later)
103 			 * has moved part of the function "out of
104 			 * line", past the epilogue. Therefore, ignore
105 			 * the positive offset so that
106 			 * pi->pi_frame_size has the correct value
107 			 * when we reach callpc.
108 			 */
109 			if (signed_immediate <= 0) {
110 				CHECK_FRAMESIZE;
111 				pi->pi_frame_size += -signed_immediate;
112 			}
113 		} else if (ins.operate_lit_format.opcode == op_arit &&
114 			   ins.operate_lit_format.function == op_subq &&
115 			   ins.operate_lit_format.ra == 30 &&
116 			   ins.operate_lit_format.rc == 30) {
117 			/*
118 			 * EGCS-style stack adjust:
119 			 *
120 			 *	subq	sp, 64, sp
121 			 */
122 			CHECK_FRAMESIZE;
123 			pi->pi_frame_size += ins.operate_lit_format.literal;
124 		} else if (ins.mem_format.opcode == op_stq &&
125 			   ins.mem_format.rb == 30 &&
126 			   ins.mem_format.ra != 31) {
127 			/* Store of (non-zero) register onto the stack. */
128 			signed_immediate = (long)ins.mem_format.displacement;
129 			pi->pi_regmask |= 1 << ins.mem_format.ra;
130 			pi->pi_reg_offset[ins.mem_format.ra] = signed_immediate;
131 		}
132 	}
133 }
134 
135 static void
decode_syscall(int number,void (* pr)(const char *,...))136 decode_syscall(int number, void (*pr)(const char *, ...))
137 {
138 	(*pr)(" (%d)", number);
139 }
140 
141 void
db_stack_trace_print(db_expr_t addr,bool have_addr,db_expr_t count,const char * modif,void (* pr)(const char *,...))142 db_stack_trace_print(db_expr_t addr, bool have_addr, db_expr_t count,
143     const char *modif, void (*pr)(const char *, ...))
144 {
145 
146 	db_stack_trace_print_ra(/*ra*/0, /*have_ra*/false, addr, have_addr,
147 	    count, modif, pr);
148 }
149 
150 void
db_stack_trace_print_ra(db_expr_t ra,bool have_ra,db_expr_t addr,bool have_addr,db_expr_t count,const char * modif,void (* pr)(const char *,...))151 db_stack_trace_print_ra(db_expr_t ra, bool have_ra,
152     db_expr_t addr, bool have_addr,
153     db_expr_t count,
154     const char *modif, void (*pr)(const char *, ...))
155 {
156 	db_addr_t callpc, frame, symval;
157 	struct prologue_info pi;
158 	db_expr_t diff;
159 	db_sym_t sym;
160 	u_long tfps;
161 	const char *symname;
162 	struct pcb *pcbp;
163 	const char *cp = modif;
164 	struct trapframe *tf;
165 	bool ra_from_tf;
166 	u_long last_ipl = ~0L;
167 	char c;
168 	bool trace_thread = false;
169 	bool lwpaddr = false;
170 
171 	while ((c = *cp++) != 0) {
172 		trace_thread |= c == 't';
173 		trace_thread |= c == 'a';
174 		lwpaddr |= c == 'a';
175 	}
176 
177 	if (!have_addr) {
178 		addr = DDB_REGS->tf_regs[FRAME_SP] - FRAME_SIZE * 8;
179 		tf = (struct trapframe *)addr;
180 		callpc = db_alpha_tf_reg(tf, FRAME_PC);
181 		frame = (db_addr_t)tf + FRAME_SIZE * 8;
182 		ra_from_tf = true;
183 	} else {
184 #ifdef _KERNEL
185 		struct proc *p = NULL;
186 		struct lwp *l = NULL;
187 #else
188 		struct proc pstore, *p = &pstore;
189 		struct lwp lstore, *l = &lstore;
190 #endif /* _KERNEL */
191 
192 		if (trace_thread) {
193 			if (lwpaddr) {
194 #ifdef _KERNEL
195 				l = (struct lwp *)addr;
196 				p = l->l_proc;
197 #else
198 				db_read_bytes(addr, sizeof(*l), (char *)l);
199 				db_read_bytes((db_addr_t)l->l_proc,
200 				    sizeof(*p), (char *)p);
201 #endif /* _KERNEL */
202 				(*pr)("trace: pid %d ", p->p_pid);
203 			} else {
204 #ifdef _KERNEL
205 				(*pr)("trace: pid %d ", (int)addr);
206 				p = proc_find_raw(addr);
207 				if (p == NULL) {
208 					(*pr)("not found\n");
209 					return;
210 				}
211 				l = LIST_FIRST(&p->p_lwps);
212 				KASSERT(l != NULL);
213 #else
214 				(*pr)("no proc_find_raw() in crash\n");
215 				return;
216 #endif /* _KERNEL */
217 			}
218 			(*pr)("lid %d ", l->l_lid);
219 			pcbp = lwp_getpcb(l);
220 			addr = db_alpha_read_saved_reg(&pcbp->pcb_hw.apcb_ksp);
221 			callpc = db_alpha_read_saved_reg(&pcbp->pcb_context[7]);
222 			(*pr)("at 0x%lx\n", addr);
223 		} else if (have_ra) {
224 			callpc = ra;
225 			(*pr)("at 0x%lx pc 0x%lx\n", addr, callpc);
226 		} else {
227 			(*pr)("alpha trace requires known PC =eject=\n");
228 			return;
229 		}
230 		frame = addr;
231 		tf = NULL;
232 		ra_from_tf = false;
233 	}
234 
235 	while (count--) {
236 		sym = db_search_symbol(callpc, DB_STGY_ANY, &diff);
237 		if (sym == DB_SYM_NULL)
238 			break;
239 
240 		db_symbol_values(sym, &symname, (db_expr_t *)&symval);
241 
242 		if (callpc < symval) {
243 			(*pr)("symbol botch: callpc 0x%lx < "
244 			    "func 0x%lx (%s)\n", callpc, symval, symname);
245 			return;
246 		}
247 
248 		/*
249 		 * If the previous RA pointed at the kernel thread
250 		 * backstop, then we are at the root of the call
251 		 * graph.
252 		 */
253 		if (db_alpha_sym_is_backstop(symval)) {
254 			(*pr)("--- kernel thread backstop ---\n");
255 			break;
256 		}
257 
258 		/*
259 		 * XXX Printing out arguments is Hard.  We'd have to
260 		 * keep lots of state as we traverse the frame, figuring
261 		 * out where the arguments to the function are stored
262 		 * on the stack.
263 		 *
264 		 * Even worse, they may be stored to the stack _after_
265 		 * being modified in place; arguments are passed in
266 		 * registers.
267 		 *
268 		 * So, in order for this to work reliably, we pretty much
269 		 * have to have a kernel built with `cc -g':
270 		 *
271 		 *	- The debugging symbols would tell us where the
272 		 *	  arguments are, how many there are, if there were
273 		 *	  any passed on the stack, etc.
274 		 *
275 		 *	- Presumably, the compiler would be careful to
276 		 *	  store the argument registers on the stack before
277 		 *	  modifying the registers, so that a debugger could
278 		 *	  know what those values were upon procedure entry.
279 		 *
280 		 * Because of this, we don't bother.  We've got most of the
281 		 * benefit of back tracking without the arguments, and we
282 		 * could get the arguments if we use a remote source-level
283 		 * debugger (for serious debugging).
284 		 */
285 		(*pr)("%s() at ", symname);
286 		db_printsym(callpc, DB_STGY_PROC, pr);
287 		(*pr)("\n");
288 
289 		/*
290 		 * If we are in a trap vector, frame points to a
291 		 * trapframe.
292 		 */
293 		if (db_alpha_sym_is_trap(symval)) {
294 			tf = (struct trapframe *)frame;
295 
296 			(*pr)("--- %s", db_alpha_trapsym_description(symval));
297 
298 			tfps = db_alpha_tf_reg(tf, FRAME_PS);
299 			if (db_alpha_sym_is_syscall(symval)) {
300 				decode_syscall(db_alpha_tf_reg(tf, FRAME_V0),
301 				    pr);
302 			}
303 			if ((tfps & ALPHA_PSL_IPL_MASK) != last_ipl) {
304 				last_ipl = tfps & ALPHA_PSL_IPL_MASK;
305 				if (! db_alpha_sym_is_syscall(symval)) {
306 					(*pr)(" (from ipl %ld)", last_ipl);
307 				}
308 			}
309 			(*pr)(" ---\n");
310 			if (tfps & ALPHA_PSL_USERMODE) {
311 				(*pr)("--- user mode ---\n");
312 				break;	/* Terminate search.  */
313 			}
314 			callpc = db_alpha_tf_reg(tf, FRAME_PC);
315 			frame = (db_addr_t)tf + FRAME_SIZE * 8;
316 			ra_from_tf = true;
317 			continue;
318 		}
319 
320 		/*
321 		 * This is a bit trickier; we must decode the function
322 		 * prologue to find the saved RA.
323 		 *
324 		 * XXX How does this interact w/ alloca()?!
325 		 */
326 		decode_prologue(callpc, symval, &pi, pr);
327 		if ((pi.pi_regmask & (1 << 26)) == 0) {
328 			/*
329 			 * No saved RA found.  We might have RA from
330 			 * the trap frame, however (e.g trap occurred
331 			 * in a leaf call).  If not, we've found the
332 			 * root of the call graph.
333 			 */
334 			if (ra_from_tf) {
335 				callpc = db_alpha_tf_reg(tf, FRAME_RA);
336 			} else {
337 				(*pr)("--- root of call graph ---\n");
338 				break;
339 			}
340 		} else {
341 			unsigned long reg;
342 
343 			db_read_bytes(frame + pi.pi_reg_offset[26],
344 			    sizeof(reg), (char *)&reg);
345 			callpc = reg;
346 		}
347 		frame += pi.pi_frame_size;
348 		ra_from_tf = false;
349 	}
350 }
351