xref: /openbsd-src/sys/arch/powerpc64/powerpc64/db_trace.c (revision 949c1c4ec8cc03255798b09f6078e1d0aed70a6a)
1 /*	$OpenBSD: db_trace.c,v 1.10 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/proc.h>
32 #include <sys/stacktrace.h>
33 #include <sys/systm.h>
34 #include <sys/user.h>
35 
36 #include <machine/db_machdep.h>
37 
38 #include <ddb/db_access.h>
39 #include <ddb/db_interface.h>
40 #include <ddb/db_sym.h>
41 #include <ddb/db_variables.h>
42 
43 db_regs_t ddb_regs;
44 
45 struct db_variable db_regs[] = {
46 	{ "r0",  (long *)&ddb_regs.fixreg[0],	FCN_NULL },
47 	{ "r1",  (long *)&ddb_regs.fixreg[1],	FCN_NULL },
48 	{ "r2",  (long *)&ddb_regs.fixreg[2],	FCN_NULL },
49 	{ "r3",  (long *)&ddb_regs.fixreg[3],	FCN_NULL },
50 	{ "r4",  (long *)&ddb_regs.fixreg[4],	FCN_NULL },
51 	{ "r5",  (long *)&ddb_regs.fixreg[5],	FCN_NULL },
52 	{ "r6",  (long *)&ddb_regs.fixreg[6],	FCN_NULL },
53 	{ "r7",  (long *)&ddb_regs.fixreg[7],	FCN_NULL },
54 	{ "r8",  (long *)&ddb_regs.fixreg[8],	FCN_NULL },
55 	{ "r9",  (long *)&ddb_regs.fixreg[9],	FCN_NULL },
56 	{ "r10", (long *)&ddb_regs.fixreg[10],	FCN_NULL },
57 	{ "r11", (long *)&ddb_regs.fixreg[11],	FCN_NULL },
58 	{ "r12", (long *)&ddb_regs.fixreg[12],	FCN_NULL },
59 	{ "r13", (long *)&ddb_regs.fixreg[13],	FCN_NULL },
60 	{ "r14", (long *)&ddb_regs.fixreg[14],	FCN_NULL },
61 	{ "r15", (long *)&ddb_regs.fixreg[15],	FCN_NULL },
62 	{ "r16", (long *)&ddb_regs.fixreg[16],	FCN_NULL },
63 	{ "r17", (long *)&ddb_regs.fixreg[17],	FCN_NULL },
64 	{ "r18", (long *)&ddb_regs.fixreg[18],	FCN_NULL },
65 	{ "r19", (long *)&ddb_regs.fixreg[19],	FCN_NULL },
66 	{ "r20", (long *)&ddb_regs.fixreg[20],	FCN_NULL },
67 	{ "r21", (long *)&ddb_regs.fixreg[21],	FCN_NULL },
68 	{ "r22", (long *)&ddb_regs.fixreg[22],	FCN_NULL },
69 	{ "r23", (long *)&ddb_regs.fixreg[23],	FCN_NULL },
70 	{ "r24", (long *)&ddb_regs.fixreg[24],	FCN_NULL },
71 	{ "r25", (long *)&ddb_regs.fixreg[25],	FCN_NULL },
72 	{ "r26", (long *)&ddb_regs.fixreg[26],	FCN_NULL },
73 	{ "r27", (long *)&ddb_regs.fixreg[27],	FCN_NULL },
74 	{ "r28", (long *)&ddb_regs.fixreg[28],	FCN_NULL },
75 	{ "r29", (long *)&ddb_regs.fixreg[29],	FCN_NULL },
76 	{ "r30", (long *)&ddb_regs.fixreg[30],	FCN_NULL },
77 	{ "r31", (long *)&ddb_regs.fixreg[31],	FCN_NULL },
78 	{ "lr",  (long *)&ddb_regs.lr,		FCN_NULL },
79 	{ "cr",  (long *)&ddb_regs.cr,		FCN_NULL },
80 	{ "xer", (long *)&ddb_regs.xer,		FCN_NULL },
81 	{ "ctr", (long *)&ddb_regs.ctr,		FCN_NULL },
82 	{ "iar", (long *)&ddb_regs.srr0,	FCN_NULL },
83 	{ "msr", (long *)&ddb_regs.srr1,	FCN_NULL },
84 	{ "dar", (long *)&ddb_regs.dar,		FCN_NULL },
85 	{ "dsisr", (long *)&ddb_regs.dsisr,	FCN_NULL },
86 };
87 
88 struct db_variable *db_eregs = db_regs + nitems(db_regs);
89 
90 extern vaddr_t trapexit;
91 
92 /* stdu r1,_(r1) */
93 #define inst_establish_frame(ins) ((ins & 0xffff0003) == 0xf8210001)
94 
95 void
96 db_stack_trace_print(db_expr_t addr, int have_addr, db_expr_t count,
97     char *modif, int (*pr)(const char *, ...))
98 {
99 	vaddr_t		 callpc, lr, sp, lastsp;
100 	db_expr_t	 offset;
101 	const char	*name;
102 	char		 c, *cp = modif;
103 	Elf_Sym		*sym;
104 	int		 has_frame, trace_proc = 0;
105 	int		 end_trace = 0;
106 
107 	while ((c = *cp++) != 0) {
108 		if (c == 't')
109 			trace_proc = 1;
110 	}
111 
112 	if (!have_addr) {
113 		sp = ddb_regs.fixreg[1];
114 		callpc = ddb_regs.srr0;
115 		has_frame = 0;
116 	} else {
117 		if (trace_proc) {
118 			(*pr)("trace/t not yet implemented!\n");
119 			return;
120 		} else
121 			sp = addr;
122 		/* The 1st return address is in the 2nd frame. */
123 		db_read_bytes(sp, sizeof(vaddr_t), (char *)&sp);
124 		db_read_bytes(sp + 16, sizeof(vaddr_t), (char *)&lr);
125 		callpc = lr - 4;
126 		has_frame = 1;
127 	}
128 
129 	while (count && sp != 0) {
130 		sym = db_search_symbol(callpc, DB_STGY_ANY, &offset);
131 		db_symbol_values(sym, &name, NULL);
132 
133 		/* Guess whether this function has a stack frame. */
134 		if (!has_frame && sym) {
135 			vaddr_t iaddr, limit;
136 			uint32_t ins;
137 
138 			iaddr = sym->st_value;
139 			limit = MIN(iaddr + 0x100, callpc);
140 			for (; iaddr < limit; iaddr += 4) {
141 				db_read_bytes(iaddr, sizeof(ins),
142 				    (char *)&ins);
143 				if (inst_establish_frame(ins)) {
144 					has_frame = 1;
145 					break;
146 				}
147 			}
148 		}
149 
150 		if (name == NULL || strcmp(name, "end") == 0) {
151 			(*pr)("at 0x%lx", callpc);
152 		} else {
153 			db_printsym(callpc, DB_STGY_PROC, pr);
154 		}
155 		(*pr)("\n");
156 
157 		/* Go to the next frame. */
158 		lastsp = sp;
159 
160 		if (lr == (vaddr_t)&trapexit) {
161 			struct trapframe *frame =
162 			    (struct trapframe *)(sp + 32);
163 
164 			if ((frame->srr1 & PSL_PR) && frame->exc == EXC_SC) {
165 				(*pr)("--- syscall (number %ld) ---\n",
166 				      frame->fixreg[0]);
167 			} else {
168 				(*pr)("--- trap (type 0x%x) ---\n",
169 				      frame->exc);
170 			}
171 
172 			if (frame->srr1 & PSL_PR)
173 				end_trace = 1;
174 
175 			sp = frame->fixreg[1];
176 			lr = frame->srr0 + 4;
177 		} else if (!has_frame) {
178 			lr = ddb_regs.lr;
179 			has_frame = 1;
180 		} else {
181 			db_read_bytes(sp, sizeof(vaddr_t), (char *)&sp);
182 			if (sp == 0)
183 				break;
184 			if (sp <= lastsp) {
185 				(*pr)("Bad frame pointer: 0x%lx\n", sp);
186 				break;
187 			}
188 			db_read_bytes(sp + 16, sizeof(vaddr_t), (char *)&lr);
189 		}
190 		callpc = lr - 4;
191 
192 		if (end_trace) {
193 			(*pr)("End of kernel: 0x%lx lr 0x%lx\n", sp, callpc);
194 			break;
195 		}
196 
197 		--count;
198 	}
199 }
200 
201 extern char _start[], _etext[];
202 
203 void
204 stacktrace_save_at(struct stacktrace *st, unsigned int skip)
205 {
206 	struct callframe *frame, *lastframe, *limit;
207 	struct proc *p = curproc;
208 
209 	st->st_count = 0;
210 
211 	if (p == NULL)
212 		return;
213 
214 	frame = __builtin_frame_address(0);
215 	limit = (struct callframe *)(p->p_addr + USPACE - FRAMELEN);
216 
217 	while (st->st_count < STACKTRACE_MAX) {
218 		if (skip == 0)
219 			st->st_pc[st->st_count++] = frame->cf_lr;
220 		else
221 			skip--;
222 
223 		lastframe = frame;
224 		frame = (struct callframe *)frame->cf_sp;
225 
226 		if (frame <= lastframe)
227 			break;
228 		if (frame >= limit)
229 			break;
230 		if (frame->cf_lr < (vaddr_t)_start ||
231 		    frame->cf_lr >= (vaddr_t)_etext)
232 			break;
233 	}
234 }
235 
236 void
237 stacktrace_save_utrace(struct stacktrace *st)
238 {
239 	st->st_count = 0;
240 }
241