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 *)®);
345 callpc = reg;
346 }
347 frame += pi.pi_frame_size;
348 ra_from_tf = false;
349 }
350 }
351