xref: /openbsd-src/sys/arch/sh/sh/db_trace.c (revision 949c1c4ec8cc03255798b09f6078e1d0aed70a6a)
1 /*	$OpenBSD: db_trace.c,v 1.11 2024/11/07 16:02:29 miod Exp $	*/
2 /*	$NetBSD: db_trace.c,v 1.19 2006/01/21 22:10:59 uwe Exp $	*/
3 
4 /*-
5  * Copyright (c) 2000 Tsubai Masanari.  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  * 3. The name of the author may not be used to endorse or promote products
16  *    derived from this software without specific prior written permission.
17  *
18  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
19  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
20  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
21  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
22  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
23  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28  */
29 
30 #include <sys/param.h>
31 #include <sys/systm.h>
32 
33 #include <machine/db_machdep.h>
34 
35 #include <ddb/db_access.h>
36 #include <ddb/db_interface.h>
37 #include <ddb/db_output.h>
38 #include <ddb/db_sym.h>
39 #include <ddb/db_variables.h>
40 
41 #ifdef TRACE_DEBUG
42 # define DPRINTF printf
43 #else
44 # define DPRINTF while (/* CONSTCOND */ 0) printf
45 #endif
46 
47 extern char start[], etext[];
48 void db_nextframe(vaddr_t, vaddr_t *, vaddr_t *);
49 
50 struct db_variable db_regs[] = {
51 	{ "r0",   (long *)&ddb_regs.tf_r0,   FCN_NULL },
52 	{ "r1",   (long *)&ddb_regs.tf_r1,   FCN_NULL },
53 	{ "r2",   (long *)&ddb_regs.tf_r2,   FCN_NULL },
54 	{ "r3",   (long *)&ddb_regs.tf_r3,   FCN_NULL },
55 	{ "r4",   (long *)&ddb_regs.tf_r4,   FCN_NULL },
56 	{ "r5",   (long *)&ddb_regs.tf_r5,   FCN_NULL },
57 	{ "r6",   (long *)&ddb_regs.tf_r6,   FCN_NULL },
58 	{ "r7",   (long *)&ddb_regs.tf_r7,   FCN_NULL },
59 	{ "r8",   (long *)&ddb_regs.tf_r8,   FCN_NULL },
60 	{ "r9",   (long *)&ddb_regs.tf_r9,   FCN_NULL },
61 	{ "r10",  (long *)&ddb_regs.tf_r10,  FCN_NULL },
62 	{ "r11",  (long *)&ddb_regs.tf_r11,  FCN_NULL },
63 	{ "r12",  (long *)&ddb_regs.tf_r12,  FCN_NULL },
64 	{ "r13",  (long *)&ddb_regs.tf_r13,  FCN_NULL },
65 	{ "r14",  (long *)&ddb_regs.tf_r14,  FCN_NULL },
66 	{ "r15",  (long *)&ddb_regs.tf_r15,  FCN_NULL },
67 	{ "pr",   (long *)&ddb_regs.tf_pr,   FCN_NULL },
68 	{ "spc",  (long *)&ddb_regs.tf_spc,  FCN_NULL },
69 	{ "ssr",  (long *)&ddb_regs.tf_ssr,  FCN_NULL },
70 	{ "mach", (long *)&ddb_regs.tf_mach, FCN_NULL },
71 	{ "macl", (long *)&ddb_regs.tf_macl, FCN_NULL },
72 };
73 
74 struct db_variable *db_eregs = db_regs + nitems(db_regs);
75 
76 void
77 db_stack_trace_print(db_expr_t addr, int have_addr, db_expr_t count,
78     char *modif, int (*print)(const char *, ...))
79 {
80 	vaddr_t callpc, frame, lastframe;
81 	uint32_t vbr;
82 
83 	if (have_addr) {
84 		(*print)("sh trace requires a trap frame... giving up\n");
85 		return;
86 	}
87 
88 	__asm volatile("stc vbr, %0" : "=r"(vbr));
89 
90 	frame = ddb_regs.tf_r14;
91 	callpc = ddb_regs.tf_spc;
92 
93 	if (count == 0 || count == -1)
94 		count = INT_MAX;
95 
96 	lastframe = 0;
97 	while (count > 0 && frame != 0) {
98 		/* Are we crossing a trap frame? */
99 		if ((callpc & ~PAGE_MASK) == vbr) {
100 			struct trapframe *tf = (void *)frame;
101 
102 			frame = tf->tf_r14;
103 			callpc = tf->tf_spc;
104 
105 			(*print)("(EXPEVT %03x; SSR=%08x) at ",
106 				 tf->tf_expevt, tf->tf_ssr);
107 			db_printsym(callpc, DB_STGY_PROC, print);
108 			(*print)("\n");
109 
110 			/* XXX: don't venture into the userland yet */
111 			if ((tf->tf_ssr & PSL_MD) == 0)
112 				break;
113 		} else {
114 			const char *name;
115 			db_expr_t offset;
116 			Elf_Sym *sym;
117 
118 
119 			DPRINTF("    (1)newpc 0x%lx, newfp 0x%lx\n",
120 				callpc, frame);
121 
122 			sym = db_search_symbol(callpc, DB_STGY_ANY, &offset);
123 			db_symbol_values(sym, &name, NULL);
124 
125 			if (name == NULL)
126 				(*print)("%lx()", callpc);
127 			else
128 				(*print)("%s() at ", name);
129 
130 			db_printsym(callpc, DB_STGY_PROC, print);
131 			(*print)("\n");
132 
133 			if (lastframe == 0 && offset == 0) {
134 				callpc = ddb_regs.tf_pr;
135 				continue;
136 			}
137 
138 			db_nextframe(callpc - offset, &frame, &callpc);
139 			DPRINTF("    (2)newpc 0x%lx, newfp 0x%lx\n",
140 				callpc, frame);
141 
142 			if (callpc == 0 && lastframe == 0)
143 				callpc = (vaddr_t)ddb_regs.tf_pr;
144 			DPRINTF("    (3)newpc 0x%lx, newfp 0x%lx\n",
145 				callpc, frame);
146 		}
147 
148 		count--;
149 		lastframe = frame;
150 	}
151 }
152 
153 void
154 db_nextframe(
155 	vaddr_t pc,		/* in: entry address of current function */
156 	vaddr_t *fp,		/* in: current fp, out: parent fp */
157 	vaddr_t *pr)		/* out: parent pr */
158 {
159 	int *frame = (void *)*fp;
160 	int i, inst;
161 	int depth, prdepth, fpdepth;
162 
163 	depth = 0;
164 	prdepth = fpdepth = -1;
165 
166 	if (pc < (vaddr_t)start || pc > (vaddr_t)etext)
167 		goto out;
168 
169 	for (i = 0; i < 30; i++) {
170 		inst = db_get_value(pc, 2, 0);
171 		pc += 2;
172 
173 		if (inst == 0x6ef3)	/* mov r15,r14 -- end of prologue */
174 			break;
175 
176 		if (inst == 0x4f22) {			/* sts.l pr,@-r15 */
177 			prdepth = depth;
178 			depth++;
179 			continue;
180 		}
181 		if (inst == 0x2fe6) {			/* mov.l r14,@-r15 */
182 			fpdepth = depth;
183 			depth++;
184 			continue;
185 		}
186 		if ((inst & 0xff0f) == 0x2f06) {	/* mov.l r?,@-r15 */
187 			depth++;
188 			continue;
189 		}
190 		if ((inst & 0xff00) == 0x7f00) {	/* add #n,r15 */
191 			int8_t n = inst & 0xff;
192 
193 			if (n >= 0) {
194 				printf("add #n,r15  (n > 0)\n");
195 				break;
196 			}
197 
198 			depth += -n/4;
199 			continue;
200 		}
201 		if ((inst & 0xf000) == 0x9000) {
202 			if (db_get_value(pc, 2, 0) == 0x3f38) {
203 				/* "mov #n,r3; sub r3,r15" */
204 				unsigned int disp = (int)(inst & 0xff);
205 				int r3;
206 
207 				r3 = db_get_value(pc + 4 - 2 + (disp << 1),
208 				    2, 0);
209 				if ((r3 & 0x00008000) == 0)
210 					r3 &= 0x0000ffff;
211 				else
212 					r3 |= 0xffff0000;
213 				depth += (r3 / 4);
214 
215 				pc += 2;
216 				continue;
217 			}
218 		}
219 
220 #ifdef TRACE_DEBUG
221 		printf("unknown instruction in prologue\n");
222 		db_disasm(pc - 2, 0);
223 #endif
224 	}
225 
226  out:
227 #ifdef TRACE_DEBUG
228 	printf("depth=%x fpdepth=0x%x prdepth=0x%x\n", depth, fpdepth, prdepth);
229 #endif
230 	if (fpdepth != -1)
231 		*fp = frame[depth - fpdepth - 1];
232 	else
233 		*fp = 0;
234 
235 	if (prdepth != -1)
236 		*pr = frame[depth - prdepth - 1];
237 	else
238 		*pr = 0;
239 }
240