1/*	$NetBSD: db_trace.c,v 1.26 2019/05/09 16:48:31 ryo Exp $	*/
2
3/*-
4 * Copyright (c) 2000 Tsubai Masanari.  All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 *    notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 *    notice, this list of conditions and the following disclaimer in the
13 *    documentation and/or other materials provided with the distribution.
14 * 3. The name of the author may not be used to endorse or promote products
15 *    derived from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29#include <sys/cdefs.h>
30__KERNEL_RCSID(0, "$NetBSD: db_trace.c,v 1.26 2019/05/09 16:48:31 ryo Exp $");
31
32#include <sys/param.h>
33#include <sys/systm.h>
34
35#include <machine/db_machdep.h>
36
37#include <ddb/db_access.h>
38#include <ddb/db_interface.h>
39#include <ddb/db_output.h>
40#include <ddb/db_proc.h>
41#include <ddb/db_sym.h>
42#include <ddb/db_variables.h>
43
44volatile int db_trace_debug = 0; /* settabble from ddb */
45#define DPRINTF(level, fmt, args...)					\
46	do {								\
47		if (__predict_false(db_trace_debug > (level))) {	\
48			print(fmt, ## args);				\
49		}							\
50	} while (0 /* CONSTCOND*/)
51
52extern char start[], etext[];
53static bool db_nextframe(db_addr_t, db_addr_t, db_addr_t *, db_addr_t *,
54    db_addr_t *, void (*)(const char *, ...) __printflike(1, 2));
55
56const struct db_variable db_regs[] = {
57	{ "r0",   (long *)&ddb_regs.tf_r0,   FCN_NULL },
58	{ "r1",   (long *)&ddb_regs.tf_r1,   FCN_NULL },
59	{ "r2",   (long *)&ddb_regs.tf_r2,   FCN_NULL },
60	{ "r3",   (long *)&ddb_regs.tf_r3,   FCN_NULL },
61	{ "r4",   (long *)&ddb_regs.tf_r4,   FCN_NULL },
62	{ "r5",   (long *)&ddb_regs.tf_r5,   FCN_NULL },
63	{ "r6",   (long *)&ddb_regs.tf_r6,   FCN_NULL },
64	{ "r7",   (long *)&ddb_regs.tf_r7,   FCN_NULL },
65	{ "r8",   (long *)&ddb_regs.tf_r8,   FCN_NULL },
66	{ "r9",   (long *)&ddb_regs.tf_r9,   FCN_NULL },
67	{ "r10",  (long *)&ddb_regs.tf_r10,  FCN_NULL },
68	{ "r11",  (long *)&ddb_regs.tf_r11,  FCN_NULL },
69	{ "r12",  (long *)&ddb_regs.tf_r12,  FCN_NULL },
70	{ "r13",  (long *)&ddb_regs.tf_r13,  FCN_NULL },
71	{ "r14",  (long *)&ddb_regs.tf_r14,  FCN_NULL },
72	{ "r15",  (long *)&ddb_regs.tf_r15,  FCN_NULL },
73	{ "pr",   (long *)&ddb_regs.tf_pr,   FCN_NULL },
74	{ "pc",   (long *)&ddb_regs.tf_spc,  FCN_NULL },
75	{ "sr",   (long *)&ddb_regs.tf_ssr,  FCN_NULL },
76	{ "mach", (long *)&ddb_regs.tf_mach, FCN_NULL },
77	{ "macl", (long *)&ddb_regs.tf_macl, FCN_NULL },
78};
79
80const struct db_variable * const db_eregs = db_regs + __arraycount(db_regs);
81
82static void
83dump_trapframe(struct trapframe *tf,
84	void (*print)(const char *, ...) __printflike(1, 2))
85{
86	print("   sr=%08x   gbr=%08x    pc=%08x     pr=%08x\n",
87	    tf->tf_ssr, tf->tf_gbr, tf->tf_spc, tf->tf_pr);
88	print("   r0=%08x    r1=%08x    r2=%08x     r3=%08x\n",
89	    tf->tf_r0, tf->tf_r1, tf->tf_r2, tf->tf_r3);
90	print("   r4=%08x    r6=%08x    r7=%08x     r8=%08x\n",
91	    tf->tf_r4, tf->tf_r5, tf->tf_r6, tf->tf_r7);
92	print("   r5=%08x    r9=%08x   r10=%08x    r11=%08x\n",
93	    tf->tf_r8, tf->tf_r9, tf->tf_r10, tf->tf_r11);
94	print("  r12=%08x   r13=%08x   r14=%08x sp=r15=%08x\n",
95	    tf->tf_r12, tf->tf_r13, tf->tf_r14, tf->tf_r15);
96}
97
98void
99db_stack_trace_print(db_expr_t addr, bool have_addr, db_expr_t count,
100	const char *modif, void (*print)(const char *, ...) __printflike(1, 2))
101{
102	struct trapframe *tf;
103	db_addr_t func, pc, lastpc, pr, sp, fp;
104	uint32_t vbr;
105	bool lwpid = false;
106	bool lwpaddr = false;
107	const char *cp;
108	char c;
109
110	__asm volatile("stc vbr, %0" : "=r"(vbr));
111
112	cp = modif;
113	while ((c = *cp++) != 0) {
114		if (c == 'a')
115			lwpaddr = true;
116		else if (c == 't')
117			lwpid = true;
118	}
119
120	if (lwpaddr && lwpid) {
121		db_printf("only one of /a or /t can be specified\n");
122		return;
123	}
124	if ((lwpaddr || lwpid) && !have_addr) {
125		db_printf("%s required\n", lwpaddr ? "address" : "pid");
126		return;
127	}
128
129	if (!have_addr) {
130		tf = &ddb_regs;
131		fp = tf->tf_r14;
132		sp = tf->tf_r15;
133		pr = tf->tf_pr;
134		pc = tf->tf_spc;
135		if (pc == 0) {
136			print("calling through null pointer?\n");
137			pc = tf->tf_pr;
138		}
139		DPRINTF(1, "# trapframe: pc=%lx pr=%lx fp=%lx sp=%lx\n",
140		    pc, pr, fp, sp);
141
142	} else if (lwpaddr || lwpid) {
143		struct proc *p;
144		struct lwp *l;
145		struct pcb *pcb;
146
147		if (lwpaddr) {
148			l = (struct lwp *)addr;
149			p = l->l_proc;
150			print("trace: lwp addr %p pid %d ",
151			    (void *)addr, p->p_pid);
152		} else {
153			pid_t pid = (pid_t)addr;
154			print("trace: pid %d ", pid);
155			p = db_proc_find(pid);
156			if (p == NULL) {
157				print("not found\n");
158				return;
159			}
160			l = LIST_FIRST(&p->p_lwps);
161		}
162		KASSERT(l != NULL);
163		print("lid %d", l->l_lid);
164		pcb = lwp_getpcb(l);
165		tf = (struct trapframe *)pcb->pcb_sf.sf_r6_bank;
166		fp = pcb->pcb_sf.sf_r14;
167		sp = pcb->pcb_sf.sf_r15;
168		pr = pcb->pcb_sf.sf_pr;
169		pc = pcb->pcb_sf.sf_pr;
170		print(", fp=%lx, sp=%lx\n", fp, sp);
171		DPRINTF(1, "# lwp: pc=%lx pr=%lx fp=%lx sp=%lx\n",
172		    pc, pr, fp, sp);
173	} else {
174		fp = 0;
175		sp = addr;
176		pr = 0;
177		/*
178		 * Assume that the frame address (__builtin_frame_address)
179		 * passed as an argument is the same level
180		 * as this __builtin_return_address().
181		 */
182		pc = (db_addr_t)__builtin_return_address(0);
183	}
184
185	lastpc = 0;
186	while (count > 0 && pc != 0 && sp != 0) {
187		DPRINTF(2, "# trace: pc=%lx sp=%lx fp=%lx\n", pc, sp, fp);
188
189		/* Are we crossing a trap frame? */
190		if ((pc & ~PAGE_MASK) == vbr) {
191			struct trapframe trapframe;
192			tf = &trapframe;
193
194			/* r14 in exception vectors points to trap frame */
195			db_read_bytes((db_addr_t)fp, sizeof(*tf), (char *)tf);
196			pc = tf->tf_spc;
197			pr = tf->tf_pr;
198			fp = tf->tf_r14;
199			sp = tf->tf_r15;
200
201			print("<EXPEVT %03x; SSR=%08x> at ",
202			    tf->tf_expevt, tf->tf_ssr);
203			db_printsym(pc, DB_STGY_PROC, print);
204			print("\n");
205
206			print("[trapframe 0x%lx]\n", fp);
207			dump_trapframe(tf, print);
208
209			/* XXX: don't venture into the userland yet */
210			if ((tf->tf_ssr & PSL_MD) == 0)
211				break;
212		} else {
213			const char *name;
214			db_expr_t offset;
215			db_sym_t sym;
216			bool found;
217
218			sym = db_search_symbol(pc, DB_STGY_ANY, &offset);
219			if (sym == 0) {
220				print("symbol not found\n");
221				break;
222			}
223			db_symbol_values(sym, &name, NULL);
224
225			func = pc - offset;
226
227			DPRINTF(1,
228			    "    (1) func=%lx+%lx, pc=%lx, sp=%lx, fp=%lx\n",
229			    func, offset, pc, sp, fp);
230
231			found = db_nextframe(func, pc, &fp, &pr, &sp, print);
232			if (!found && lastpc == pc)
233				break;
234			lastpc = pc;
235
236			DPRINTF(1, "    (2) newpc=%lx, newsp=%lx, newfp=%lx\n",
237			    pr, sp, fp);
238
239			DPRINTF(1, "sp=%lx ", sp);
240			print("%s() at ", name ? name : "");
241			db_printsym(pr, DB_STGY_PROC, print);
242			print("\n");
243
244			pc = pr;
245			pr = 0;
246		}
247
248		count--;
249	}
250}
251
252static bool
253db_nextframe(
254	db_addr_t func,		/* in: entry address of current function */
255	db_addr_t curpc,	/* in: current pc in the function */
256	db_addr_t *fp,		/* out: parent fp */
257	db_addr_t *pr,		/* out: parent pr */
258	db_addr_t *sp,		/* in: current sp, out: parent sp */
259	void (*print)(const char *, ...) __printflike(1, 2))
260{
261	int *stack = (void *)*sp;
262	int i, inst, inst2;
263	int depth, prdepth, fpdepth;
264	db_addr_t pc;
265
266	if (__predict_false(db_trace_debug >= 2)) {
267		DPRINTF(2, "%s:%d: START: func=%lx=", __func__, __LINE__, func);
268		db_printsym(func, DB_STGY_PROC, print);
269		DPRINTF(2, " pc=%lx fp=%lx pr=%lx, sp=%lx\n",
270		    curpc, *fp, *pr, *sp);
271	}
272
273	pc = func;
274	depth = 0;
275	prdepth = fpdepth = -1;
276
277	if (pc < (db_addr_t)start || pc > (db_addr_t)etext)
278		goto out;
279
280	for (i = 0; i < 30 && pc < curpc; i++) {
281		inst = db_get_value(pc, 2, false);
282		DPRINTF(2, "%s:%d: %lx insn=%04x depth=%d\n",
283		    __func__, __LINE__, pc, inst, depth);
284		pc += 2;
285
286		if (inst == 0x000b)	/* rts - asm routines w/out frame */
287			break;
288
289		if (inst == 0x6ef3)	/* mov r15,r14 -- end of prologue */
290			break;
291
292		if (inst == 0x4f22) {			/* sts.l pr,@-r15 */
293			prdepth = depth;
294			depth++;
295			continue;
296		}
297		if (inst == 0x2fe6) {			/* mov.l r14,@-r15 */
298			fpdepth = depth;
299			depth++;
300			continue;
301		}
302		if ((inst & 0xff0f) == 0x2f06) {	/* mov.l r?,@-r15 */
303			depth++;
304			continue;
305		}
306		if ((inst & 0xff00) == 0x7f00) {	/* add #n,r15 */
307			int8_t n = inst & 0xff;
308
309			if (n >= 0) {
310				/* XXX: in epilogue? ignore it */
311				DPRINTF(2,
312				    "%s:%d: %lx: add #%d,r15 (n > 0) ignored\n",
313				    __func__, __LINE__, pc - 2, n);
314				break;
315			}
316
317			depth += -n / 4;
318			continue;
319		}
320		if ((inst & 0xf000) == 0x9000) {
321			inst2 = db_get_value(pc, 2, false);
322			if (((inst2 & 0xff0f) == 0x3f08) &&
323			    ((inst & 0x0f00) == ((inst2 & 0x00f0) << 4))) {
324
325				/* mov <disp>,r?; sub r?,r15 */
326				unsigned int disp = (int)(inst & 0xff);
327				vaddr_t addr;
328				int val;
329
330				addr = pc + (4 - 2) + (disp << 1);
331				db_read_bytes(addr, sizeof(val), (char *)&val);
332				if ((val & 0x00008000) == 0)
333					val &= 0x0000ffff;
334				else
335					val |= 0xffff0000;
336				depth += (val / 4);
337
338				pc += 2;
339				continue;
340			}
341		}
342
343		if (__predict_false(db_trace_debug > 1)) {
344			print("    unknown insn at ");
345			db_printsym(pc - 2, DB_STGY_PROC, print);
346			print(":\t");
347			db_disasm(pc - 2, 0);/* XXX: always uses db_printf */
348		}
349	}
350
351 out:
352	DPRINTF(2, "%s:%d: fpdepth=%d prdepth=%d depth=%d sp=%lx->%lx\n",
353	    __func__, __LINE__, fpdepth, prdepth, depth, *sp, *sp - depth * 4);
354
355	/* dump stack */
356	if (__predict_false(db_trace_debug > 2) &&
357	    ((fpdepth != -1) || (prdepth != -1))) {
358		print("%s:%d: func=%lx pc=%lx\n",
359		    __func__, __LINE__, func, curpc);
360
361		for (int j = 0; j < prdepth + 32; j++) {
362			uint32_t v;
363
364			db_read_bytes((db_addr_t)&stack[j],
365			    sizeof(v), (char *)&v);
366			print("  STACK[%2d]: %p: %08x  ",
367			    j, &stack[j], v);
368			db_printsym(v, DB_STGY_PROC, print);
369			if (j == (depth - prdepth - 1))
370				print("  # = pr");
371			if (j == (depth - fpdepth - 1))
372				print("  # = fp");
373			print("\n");
374		}
375	}
376
377	/* fetch fp and pr if exists in stack */
378	if (fpdepth != -1)
379		db_read_bytes((db_addr_t)&stack[depth - fpdepth - 1],
380		    sizeof(*fp), (char *)fp);
381	if (prdepth != -1)
382		db_read_bytes((db_addr_t)&stack[depth - prdepth - 1],
383		    sizeof(*pr), (char *)pr);
384
385	/* adjust stack pointer, and update */
386	stack += depth;
387	*sp = (db_addr_t)stack;
388
389	if (__predict_false(db_trace_debug >= 2)) {
390		DPRINTF(2, "%s:%d: RESULT: fp=%lx pr=%lx(",
391		    __func__, __LINE__, *fp, *pr);
392		db_printsym(*pr, DB_STGY_PROC, print);
393		DPRINTF(2, ") sp=%lx\n", *sp);
394	}
395
396	if ((prdepth == -1) && (fpdepth == -1) && (depth == 0))
397		return false;
398	return true;
399}
400