xref: /openbsd-src/sys/arch/powerpc/ddb/db_trace.c (revision 949c1c4ec8cc03255798b09f6078e1d0aed70a6a)
1 /*	$OpenBSD: db_trace.c,v 1.21 2024/11/07 16:02:29 miod Exp $	*/
2 /*	$NetBSD: db_trace.c,v 1.15 1996/02/22 23:23:41 gwr Exp $	*/
3 
4 /*
5  * Mach Operating System
6  * Copyright (c) 1992 Carnegie Mellon University
7  * All Rights Reserved.
8  *
9  * Permission to use, copy, modify and distribute this software and its
10  * documentation is hereby granted, provided that both the copyright
11  * notice and this permission notice appear in all copies of the
12  * software, derivative works or modified versions, and any portions
13  * thereof, and that both notices appear in supporting documentation.
14  *
15  * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
16  * CONDITION.  CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
17  * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
18  *
19  * Carnegie Mellon requests users of this software to return to
20  *
21  *  Software Distribution Coordinator  or  Software.Distribution@CS.CMU.EDU
22  *  School of Computer Science
23  *  Carnegie Mellon University
24  *  Pittsburgh PA 15213-3890
25  *
26  * any improvements or extensions that they make and grant Carnegie Mellon
27  * the rights to redistribute these changes.
28  */
29 
30 #include <sys/param.h>
31 #include <sys/systm.h>
32 #include <sys/proc.h>
33 #include <sys/user.h>
34 #include <sys/stacktrace.h>
35 
36 #include <uvm/uvm_extern.h>
37 
38 #include <machine/db_machdep.h>
39 #include <machine/signal.h>
40 #include <machine/pcb.h>
41 #include <machine/pmap.h>
42 
43 #include <ddb/db_access.h>
44 #include <ddb/db_sym.h>
45 #include <ddb/db_variables.h>
46 #include <ddb/db_interface.h>
47 #include <ddb/db_output.h>
48 
49 db_regs_t ddb_regs;
50 
51 struct db_variable db_regs[] = {
52 	{ "r0",  (long *)&ddb_regs.fixreg[0],	FCN_NULL },
53 	{ "r1",  (long *)&ddb_regs.fixreg[1],	FCN_NULL },
54 	{ "r2",  (long *)&ddb_regs.fixreg[2],	FCN_NULL },
55 	{ "r3",  (long *)&ddb_regs.fixreg[3],	FCN_NULL },
56 	{ "r4",  (long *)&ddb_regs.fixreg[4],	FCN_NULL },
57 	{ "r5",  (long *)&ddb_regs.fixreg[5],	FCN_NULL },
58 	{ "r6",  (long *)&ddb_regs.fixreg[6],	FCN_NULL },
59 	{ "r7",  (long *)&ddb_regs.fixreg[7],	FCN_NULL },
60 	{ "r8",  (long *)&ddb_regs.fixreg[8],	FCN_NULL },
61 	{ "r9",  (long *)&ddb_regs.fixreg[9],	FCN_NULL },
62 	{ "r10", (long *)&ddb_regs.fixreg[10],	FCN_NULL },
63 	{ "r11", (long *)&ddb_regs.fixreg[11],	FCN_NULL },
64 	{ "r12", (long *)&ddb_regs.fixreg[12],	FCN_NULL },
65 	{ "r13", (long *)&ddb_regs.fixreg[13],	FCN_NULL },
66 	{ "r14", (long *)&ddb_regs.fixreg[14],	FCN_NULL },
67 	{ "r15", (long *)&ddb_regs.fixreg[15],	FCN_NULL },
68 	{ "r16", (long *)&ddb_regs.fixreg[16],	FCN_NULL },
69 	{ "r17", (long *)&ddb_regs.fixreg[17],	FCN_NULL },
70 	{ "r18", (long *)&ddb_regs.fixreg[18],	FCN_NULL },
71 	{ "r19", (long *)&ddb_regs.fixreg[19],	FCN_NULL },
72 	{ "r20", (long *)&ddb_regs.fixreg[20],	FCN_NULL },
73 	{ "r21", (long *)&ddb_regs.fixreg[21],	FCN_NULL },
74 	{ "r22", (long *)&ddb_regs.fixreg[22],	FCN_NULL },
75 	{ "r23", (long *)&ddb_regs.fixreg[23],	FCN_NULL },
76 	{ "r24", (long *)&ddb_regs.fixreg[24],	FCN_NULL },
77 	{ "r25", (long *)&ddb_regs.fixreg[25],	FCN_NULL },
78 	{ "r26", (long *)&ddb_regs.fixreg[26],	FCN_NULL },
79 	{ "r27", (long *)&ddb_regs.fixreg[27],	FCN_NULL },
80 	{ "r28", (long *)&ddb_regs.fixreg[28],	FCN_NULL },
81 	{ "r29", (long *)&ddb_regs.fixreg[29],	FCN_NULL },
82 	{ "r30", (long *)&ddb_regs.fixreg[30],	FCN_NULL },
83 	{ "r31", (long *)&ddb_regs.fixreg[31],	FCN_NULL },
84 	{ "lr",  (long *)&ddb_regs.lr,		FCN_NULL },
85 	{ "cr",  (long *)&ddb_regs.cr,		FCN_NULL },
86 	{ "xer", (long *)&ddb_regs.xer,		FCN_NULL },
87 	{ "ctr", (long *)&ddb_regs.ctr,		FCN_NULL },
88 	{ "iar", (long *)&ddb_regs.srr0,	FCN_NULL },
89 	{ "msr", (long *)&ddb_regs.srr1,	FCN_NULL },
90 	{ "dar", (long *)&ddb_regs.dar,		FCN_NULL },
91 	{ "dsisr", (long *)&ddb_regs.dsisr,	FCN_NULL },
92 };
93 
94 struct db_variable *db_eregs = db_regs + nitems(db_regs);
95 
96 /*
97  * this is probably hackery.
98  */
99 void
100 db_save_regs(struct trapframe *frame)
101 {
102 	bcopy(frame, &ddb_regs, sizeof (struct trapframe));
103 }
104 
105 /* from locore.S */
106 extern vaddr_t trapexit;
107 #define	INTSTK		(8*1024)	/* 8K interrupt stack */
108 
109 #define	INKERNEL(va)	(((vaddr_t)(va)) >= VM_MIN_KERNEL_ADDRESS &&	\
110 			((vaddr_t)(va)) < VM_MAX_KERNEL_ADDRESS)
111 
112 #define	ININTSTK(va)	db_in_interrupt_stack((vaddr_t)(va))
113 
114 int
115 db_in_interrupt_stack(vaddr_t va)
116 {
117 	struct cpu_info *ci;
118 	CPU_INFO_ITERATOR cii;
119 	vaddr_t stack;
120 
121 	CPU_INFO_FOREACH(cii, ci) {
122 		stack = (vaddr_t)ci->ci_intstk;
123 		if (va >= stack - INTSTK && va < stack)
124 			return 1;
125 	}
126 	return 0;
127 }
128 
129 /*
130  *	Frame tracing.
131  */
132 void
133 db_stack_trace_print(db_expr_t addr, int have_addr, db_expr_t count,
134     char *modif, int (*pr)(const char *, ...))
135 {
136 	vaddr_t		 lr, sp, lastsp, *db_fp_args;
137 	db_expr_t	 offset;
138 	Elf_Sym		*sym;
139 	const char	*name;
140 	char		 c, *cp = modif;
141 	int		 i, narg, trace_proc = 0;
142 
143 	while ((c = *cp++) != 0) {
144 		if (c == 't')
145 			trace_proc = 1;
146 	}
147 
148 	if (!have_addr) {
149 		sp = ddb_regs.fixreg[1];
150 		lr = ddb_regs.srr0;
151 	} else {
152 		if (trace_proc) {
153 			struct proc *p = tfind((pid_t)addr);
154 			if (p == NULL) {
155 				(*pr) ("not found\n");
156 				return;
157 			}
158 			addr = p->p_addr->u_pcb.pcb_sp;
159 		}
160 		sp = addr;
161 		db_read_bytes(sp + 4, sizeof(vaddr_t), (char *)&lr);
162 	}
163 
164 	while (count && sp != 0) {
165 		/*
166 		 * lr contains the return address, so adjust its value
167 		 * to display the offset of the calling address.
168 		 */
169 		sym = db_search_symbol(lr - 4, DB_STGY_ANY, &offset);
170 		db_symbol_values(sym, &name, NULL);
171 
172 		if (name == NULL || strcmp(name, "end") == 0) {
173 			(*pr)("at 0x%lx", lr - 4);
174 		} else {
175 			narg = db_ctf_func_numargs(sym);
176 			if (narg < 0 || narg > 8)
177 				narg = 8;
178 
179 			(*pr)("%s(", name);
180 
181 			if (narg > 0) {
182 				db_fp_args = (vaddr_t *)(sp + 8);
183 
184 				for (i = 0; i < narg; i++) {
185 					(*pr)("%lx", db_fp_args[i]);
186 					if (i != (narg-1))
187 						(*pr)(",");
188 				}
189 			}
190 
191 			(*pr)(") at ");
192 			db_printsym(lr - 4, DB_STGY_PROC, pr);
193 		}
194 		(*pr)("\n");
195 
196 		lastsp = sp;
197 
198 		/*
199 		 * Abuse the fact that the return address of the trap()
200 		 * function is always 'trapexit'.
201 		 */
202 		if (lr == (vaddr_t)&trapexit) {
203 			struct trapframe *tf = (struct trapframe *)(sp + 8);
204 			uint32_t code = tf->fixreg[0];
205 			uint32_t type = tf->exc;
206 
207 			if (tf->srr1 & PSL_PR)
208 				type |= EXC_USER;
209 
210 			if (type == (EXC_SC|EXC_USER))
211 				(*pr)("--- syscall (number %d) ---\n", code);
212 			else
213 				(*pr)("--- trap (type 0x%x) ---\n", type);
214 		}
215 
216 		db_read_bytes(sp, sizeof(vaddr_t), (char *)&sp);
217 		if (sp == 0)
218 			break;
219 
220 		db_read_bytes(sp + 4, sizeof(vaddr_t), (char *)&lr);
221 
222 		if (INKERNEL(sp)) {
223 			if (sp <= lastsp) {
224 				(*pr)("Bad frame pointer: 0x%lx\n", sp);
225 				break;
226 			}
227 
228 			if (ININTSTK(lastsp))
229 				(*pr)("--- interrupt ---\n");
230 
231 		} else  {
232 			if (!ININTSTK(sp)) {
233 				(*pr)("End of kernel: 0x%lx\n", sp);
234 				break;
235 			}
236 		}
237 		--count;
238 	}
239 	(*pr)("end trace frame: 0x%lx, count: %d\n", sp, count);
240 }
241 
242 void
243 stacktrace_save_at(struct stacktrace *st, unsigned int skip)
244 {
245 	vaddr_t		 lr, sp, lastsp;
246 
247 	sp = (vaddr_t)__builtin_frame_address(0);
248 	if (!INKERNEL(sp) && !ININTSTK(sp))
249 		return;
250 
251 	st->st_count = 0;
252 	while (st->st_count < STACKTRACE_MAX) {
253 		lr = *(vaddr_t *)(sp + 4) - 4;
254 		if (lr & 3)
255 			break;
256 
257 		if (skip == 0)
258 			st->st_pc[st->st_count++] = lr;
259 		else
260 			skip--;
261 
262 		lastsp = sp;
263 		sp = *(vaddr_t *)sp;
264 
265 		if ((sp == 0) || (sp & 3) || (sp <= lastsp))
266 			break;
267 		if (!INKERNEL(sp) && !ININTSTK(sp))
268 			break;
269 	}
270 }
271 
272 void
273 stacktrace_save_utrace(struct stacktrace *st)
274 {
275 	st->st_count = 0;
276 }
277