xref: /netbsd-src/sys/arch/mips/mips/mips_stacktrace.c (revision 90dcc60021a0120529e661ab802d423e26c2cb2f)
1 /*	$NetBSD: mips_stacktrace.c,v 1.9 2021/04/06 13:11:22 simonb Exp $	*/
2 
3 /*
4  * Copyright (c) 1988 University of Utah.
5  * Copyright (c) 1992, 1993
6  *	The Regents of the University of California.  All rights reserved.
7  *
8  * This code is derived from software contributed to Berkeley by
9  * the Systems Programming Group of the University of Utah Computer
10  * Science Department and Ralph Campbell.
11  *
12  * Redistribution and use in source and binary forms, with or without
13  * modification, are permitted provided that the following conditions
14  * are met:
15  * 1. Redistributions of source code must retain the above copyright
16  *    notice, this list of conditions and the following disclaimer.
17  * 2. Redistributions in binary form must reproduce the above copyright
18  *    notice, this list of conditions and the following disclaimer in the
19  *    documentation and/or other materials provided with the distribution.
20  * 3. Neither the name of the University nor the names of its contributors
21  *    may be used to endorse or promote products derived from this software
22  *    without specific prior written permission.
23  *
24  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
25  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
28  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34  * SUCH DAMAGE.
35  *
36  * from: NetBSD: trap.c,v 1.255 2020/07/13 09:00:40 simonb Exp
37  * from: Utah Hdr: trap.c 1.32 91/04/06
38  *
39  *	@(#)trap.c	8.5 (Berkeley) 1/11/94
40  */
41 
42 #include <sys/cdefs.h>
43 __KERNEL_RCSID(0, "$NetBSD: mips_stacktrace.c,v 1.9 2021/04/06 13:11:22 simonb Exp $");
44 
45 #ifdef _KERNEL_OPT
46 #include "opt_ddb.h"
47 #include "opt_kgdb.h"
48 #endif
49 
50 #include <sys/param.h>
51 #include <sys/systm.h>
52 #include <sys/proc.h>
53 
54 #include <mips/locore.h>
55 #include <mips/mips_opcode.h>
56 #include <mips/regnum.h>
57 #include <mips/stacktrace.h>
58 
59 #if defined(_KMEMUSER) && !defined(DDB)
60 #define DDB 1
61 #endif
62 
63 #ifdef DDB
64 #include <machine/db_machdep.h>
65 #include <ddb/db_sym.h>
66 #include <ddb/db_user.h>
67 #include <ddb/db_access.h>
68 #endif
69 
70 #ifdef KGDB
71 #include <sys/kgdb.h>
72 #endif
73 
74 #ifndef DDB_TRACE
75 
76 #if defined(DEBUG) || defined(DDB) || defined(KGDB) || defined(geo)
77 
78 extern char start[], edata[], verylocore[];
79 #ifdef MIPS1
80 extern char mips1_kern_gen_exception[];
81 extern char mips1_user_gen_exception[];
82 extern char mips1_kern_intr[];
83 extern char mips1_user_intr[];
84 extern char mips1_systemcall[];
85 #endif
86 #ifdef MIPS3
87 extern char mips3_kern_gen_exception[];
88 extern char mips3_user_gen_exception[];
89 extern char mips3_kern_intr[];
90 extern char mips3_user_intr[];
91 extern char mips3_systemcall[];
92 #endif
93 #ifdef MIPS32
94 extern char mips32_kern_gen_exception[];
95 extern char mips32_user_gen_exception[];
96 extern char mips32_kern_intr[];
97 extern char mips32_user_intr[];
98 extern char mips32_systemcall[];
99 #endif
100 #ifdef MIPS32R2
101 extern char mips32r2_kern_gen_exception[];
102 extern char mips32r2_user_gen_exception[];
103 extern char mips32r2_kern_intr[];
104 extern char mips32r2_user_intr[];
105 extern char mips32r2_systemcall[];
106 #endif
107 #ifdef MIPS64
108 extern char mips64_kern_gen_exception[];
109 extern char mips64_user_gen_exception[];
110 extern char mips64_kern_intr[];
111 extern char mips64_user_intr[];
112 extern char mips64_systemcall[];
113 #endif
114 #ifdef MIPS64R2
115 extern char mips64r2_kern_gen_exception[];
116 extern char mips64r2_user_gen_exception[];
117 extern char mips64r2_kern_intr[];
118 extern char mips64r2_user_intr[];
119 extern char mips64r2_systemcall[];
120 #endif
121 
122 #define	MIPS_JR_RA	0x03e00008	/* instruction code for jr ra */
123 #define	MIPS_JR_K0	0x03400008	/* instruction code for jr k0 */
124 #define	MIPS_ERET	0x42000018	/* instruction code for eret */
125 
126 int main(void *);	/* XXX */
127 
128 /*
129  * Functions ``special'' enough to print by name
130  */
131 #define Name(_fn)  { (void*)_fn, # _fn }
132 static const struct { void *addr; const char *name;} names[] = {
133 #ifdef _KERNEL
134 	Name(stacktrace),
135 	Name(stacktrace_subr),
136 	Name(main),
137 	Name(trap),
138 
139 #ifdef MIPS1	/*  r2000 family  (mips-I CPU) */
140 	Name(mips1_kern_gen_exception),
141 	Name(mips1_user_gen_exception),
142 	Name(mips1_systemcall),
143 	Name(mips1_kern_intr),
144 	Name(mips1_user_intr),
145 #endif	/* MIPS1 */
146 
147 #if defined(MIPS3)			/* r4000 family (mips-III CPU) */
148 	Name(mips3_kern_gen_exception),
149 	Name(mips3_user_gen_exception),
150 	Name(mips3_systemcall),
151 	Name(mips3_kern_intr),
152 	Name(mips3_user_intr),
153 #endif	/* MIPS3 */
154 
155 #if defined(MIPS32)			/* MIPS32 family (mips-III CPU) */
156 	Name(mips32_kern_gen_exception),
157 	Name(mips32_user_gen_exception),
158 	Name(mips32_systemcall),
159 	Name(mips32_kern_intr),
160 	Name(mips32_user_intr),
161 #endif	/* MIPS32 */
162 
163 #if defined(MIPS32R2)			/* MIPS32R2 family (mips-III CPU) */
164 	Name(mips32r2_kern_gen_exception),
165 	Name(mips32r2_user_gen_exception),
166 	Name(mips32r2_systemcall),
167 	Name(mips32r2_kern_intr),
168 	Name(mips32r2_user_intr),
169 #endif	/* MIPS32R2 */
170 
171 #if defined(MIPS64)			/* MIPS64 family (mips-III CPU) */
172 	Name(mips64_kern_gen_exception),
173 	Name(mips64_user_gen_exception),
174 	Name(mips64_systemcall),
175 	Name(mips64_kern_intr),
176 	Name(mips64_user_intr),
177 #endif	/* MIPS64 */
178 
179 #if defined(MIPS64R2)			/* MIPS64R2 family (mips-III CPU) */
180 	Name(mips64r2_kern_gen_exception),
181 	Name(mips64r2_user_gen_exception),
182 	Name(mips64r2_systemcall),
183 	Name(mips64r2_kern_intr),
184 	Name(mips64r2_user_intr),
185 #endif	/* MIPS64R2 */
186 
187 	Name(cpu_idle),
188 	Name(cpu_switchto),
189 #endif /* _KERNEL */
190 	{0, 0}
191 };
192 
193 
194 bool
kdbpeek(vaddr_t addr,unsigned * valp)195 kdbpeek(vaddr_t addr, unsigned *valp)
196 {
197 	if (addr & 3) {
198 		printf("kdbpeek: unaligned address %#"PRIxVADDR"\n", addr);
199 		/* We might have been called from DDB, so do not go there. */
200 		return false;
201 	} else if (addr == 0) {
202 		printf("kdbpeek: NULL\n");
203 		return false;
204 	} else {
205 #if _KERNEL
206 		*valp = *(unsigned *)addr;
207 #else
208 		db_read_bytes((db_addr_t)addr, sizeof(unsigned), (char *)valp);
209 #endif
210 		return true;
211 	}
212 }
213 
214 mips_reg_t
kdbrpeek(vaddr_t addr,size_t n)215 kdbrpeek(vaddr_t addr, size_t n)
216 {
217 	mips_reg_t rc = 0;
218 
219 	if (addr & (n - 1)) {
220 		printf("kdbrpeek: unaligned address %#"PRIxVADDR"\n", addr);
221 #if _KERNEL
222 		/* We might have been called from DDB, so do not go there. */
223 		stacktrace();
224 #endif
225 		rc = -1;
226 	} else if (addr == 0) {
227 		printf("kdbrpeek: NULL\n");
228 		rc = 0xdeadfeed;
229 	} else {
230 		if (sizeof(mips_reg_t) == 8 && n == 8)
231 #if _KERNEL
232 			rc = *(int64_t *)addr;
233  		else
234 			rc = *(int32_t *)addr;
235 #else
236 			db_read_bytes((db_addr_t)addr, sizeof(int64_t), (char *)&rc);
237 		else
238 			db_read_bytes((db_addr_t)addr, sizeof(int32_t), (char *)&rc);
239 #endif
240 	}
241 	return rc;
242 }
243 
244 /*
245  * Map a function address to a string name, if known; or a hex string.
246  */
247 static const char *
fn_name(vaddr_t addr)248 fn_name(vaddr_t addr)
249 {
250 	static char buf[17];
251 	int i = 0;
252 #ifdef DDB
253 	db_expr_t diff;
254 	db_sym_t sym;
255 	const char *symname;
256 #endif
257 
258 #ifdef DDB
259 	diff = 0;
260 	symname = NULL;
261 	sym = db_search_symbol(addr, DB_STGY_ANY, &diff);
262 	db_symbol_values(sym, &symname, 0);
263 	if (symname && diff == 0)
264 		return (symname);
265 #endif
266 	for (i = 0; names[i].name; i++)
267 		if (names[i].addr == (void*)addr)
268 			return (names[i].name);
269 	snprintf(buf, sizeof(buf), "%#"PRIxVADDR, addr);
270 	return (buf);
271 }
272 
273 /*
274  * Do a stack backtrace.
275  * (*printfn)()  prints the output to either the system log,
276  * the console, or both.
277  */
278 void
stacktrace_subr(mips_reg_t a0,mips_reg_t a1,mips_reg_t a2,mips_reg_t a3,vaddr_t pc,vaddr_t sp,vaddr_t fp,vaddr_t ra,void (* printfn)(const char *,...))279 stacktrace_subr(mips_reg_t a0, mips_reg_t a1, mips_reg_t a2, mips_reg_t a3,
280     vaddr_t pc, vaddr_t sp, vaddr_t fp, vaddr_t ra,
281     void (*printfn)(const char*, ...))
282 {
283 	vaddr_t va, subr;
284 	unsigned instr, mask;
285 	InstFmt i;
286 	int more, stksize;
287 	unsigned int frames =  0;
288 	int foundframesize = 0;
289 	mips_reg_t regs[32] = {
290 		[_R_ZERO] = 0,
291 		[_R_A0] = a0, [_R_A1] = a1, [_R_A2] = a2, [_R_A3] = a3,
292 		[_R_RA] = ra,
293 	};
294 
295 /* Jump here when done with a frame, to start a new one */
296 loop:
297 	stksize = 0;
298 	subr = 0;
299 	mask = 1;
300 	if (frames++ > 100) {
301 		(*printfn)("\nstackframe count exceeded\n");
302 		/* return breaks stackframe-size heuristics with gcc -O2 */
303 		goto finish;	/*XXX*/
304 	}
305 
306 	/* check for bad SP: could foul up next frame */
307 	if ((sp & (sizeof(sp)-1)) || (intptr_t)sp >= 0) {
308 		(*printfn)("SP 0x%x: not in kernel\n", sp);
309 		ra = 0;
310 		subr = 0;
311 		goto done;
312 	}
313 
314 	/* Check for bad PC */
315 	if (pc & 3 || (intptr_t)pc >= 0 || (intptr_t)pc >= (intptr_t)edata) {
316 		(*printfn)("PC 0x%x: not in kernel space\n", pc);
317 		ra = 0;
318 		goto done;
319 	}
320 
321 #if defined(DDB) && defined(_KERNEL)
322 	if (ksyms_available()) {
323 		db_expr_t diff;
324 		db_sym_t sym;
325 
326 		/*
327 		 * Check the kernel symbol table to see the beginning of
328 		 * the current subroutine.
329 		 */
330 		diff = 0;
331 		sym = db_search_symbol(pc, DB_STGY_ANY, &diff);
332 		if (sym != DB_SYM_NULL && diff == 0) {
333 			/* check func(foo) __attribute__((__noreturn__)) case */
334 			if (!kdbpeek(pc - 2 * sizeof(unsigned), &instr))
335 				return;
336 			i.word = instr;
337 			if (i.JType.op == OP_JAL) {
338 				sym = db_search_symbol(pc - sizeof(int),
339 				    DB_STGY_ANY, &diff);
340 				if (sym != DB_SYM_NULL && diff != 0)
341 					diff += sizeof(int);
342 			}
343 		}
344 		if (sym == DB_SYM_NULL) {
345 			ra = 0;
346 			goto done;
347 		}
348 		va = pc - diff;
349 	} else {
350 #endif /* DDB && _KERNEL */
351 		/*
352 		 * Find the beginning of the current subroutine by
353 		 * scanning backwards from the current PC for the end
354 		 * of the previous subroutine.
355 		 *
356 		 * XXX This won't work well because nowadays gcc is so
357 		 *     aggressive as to reorder instruction blocks for
358 		 *     branch-predict. (i.e. 'jr ra' wouldn't indicate
359 		 *     the end of subroutine)
360 		 */
361 		va = pc;
362 		do {
363 			va -= sizeof(int);
364 #ifdef _KERNEL /* XXX crash */
365 			if (va <= (vaddr_t)verylocore)
366 				goto finish;
367 #endif
368 			if (!kdbpeek(va, &instr))
369 				return;
370 			if (instr == MIPS_ERET)
371 				goto mips3_eret;
372 		} while (instr != MIPS_JR_RA && instr != MIPS_JR_K0);
373 		/* skip back over branch & delay slot */
374 		va += sizeof(int);
375 mips3_eret:
376 		va += sizeof(int);
377 		/* skip over nulls which might separate .o files */
378 		instr = 0;
379 		while (instr == 0) {
380 			if (!kdbpeek(va, &instr))
381 				return;
382 			va += sizeof(int);
383 		}
384 #if defined(DDB) && defined(_KERNEL)
385 	}
386 #endif /* DDB && _KERNEL */
387 	subr = va;
388 
389 	/* scan forwards to find stack size and any saved registers */
390 	stksize = 0;
391 	more = 3;
392 	mask &= 0x40ff0001;	/* if s0-s8 are valid, leave then as valid */
393 	foundframesize = 0;
394 	for (va = subr; more; va += sizeof(int),
395 			      more = (more == 3) ? 3 : more - 1) {
396 		/* stop if hit our current position */
397 		if (va >= pc)
398 			break;
399 		if (!kdbpeek(va, &instr))
400 			return;
401 		i.word = instr;
402 		switch (i.JType.op) {
403 		case OP_SPECIAL:
404 			switch (i.RType.func) {
405 			case OP_JR:
406 			case OP_JALR:
407 				more = 2; /* stop after next instruction */
408 				break;
409 
410 			case OP_ADD:
411 			case OP_ADDU:
412 			case OP_DADD:
413 			case OP_DADDU:
414 				if (!(mask & (1 << i.RType.rd))
415 				    || !(mask & (1 << i.RType.rt)))
416 					break;
417 				if (i.RType.rd != _R_ZERO)
418 					break;
419 				mask |= (1 << i.RType.rs);
420 				regs[i.RType.rs] = regs[i.RType.rt];
421 				if (i.RType.func >= OP_DADD)
422 					break;
423 				regs[i.RType.rs] = (int32_t)regs[i.RType.rs];
424 				break;
425 
426 			case OP_SYSCALL:
427 			case OP_BREAK:
428 				more = 1; /* stop now */
429 				break;
430 			}
431 			break;
432 
433 		case OP_REGIMM:
434 		case OP_J:
435 		case OP_JAL:
436 		case OP_BEQ:
437 		case OP_BNE:
438 		case OP_BLEZ:
439 		case OP_BGTZ:
440 			more = 2; /* stop after next instruction */
441 			break;
442 
443 		case OP_COP0:
444 		case OP_COP1:
445 		case OP_COP2:
446 		case OP_COP3:
447 			switch (i.RType.rs) {
448 			case OP_BCx:
449 			case OP_BCy:
450 				more = 2; /* stop after next instruction */
451 			};
452 			break;
453 
454 		case OP_SW:
455 #if !defined(__mips_o32)
456 		case OP_SD:
457 #endif
458 		{
459 			size_t size = (i.JType.op == OP_SW) ? 4 : 8;
460 
461 			/* look for saved registers on the stack */
462 			if (i.IType.rs != _R_SP)
463 				break;
464 			switch (i.IType.rt) {
465 			case _R_A0: /* a0 */
466 			case _R_A1: /* a1 */
467 			case _R_A2: /* a2 */
468 			case _R_A3: /* a3 */
469 			case _R_S0: /* s0 */
470 			case _R_S1: /* s1 */
471 			case _R_S2: /* s2 */
472 			case _R_S3: /* s3 */
473 			case _R_S4: /* s4 */
474 			case _R_S5: /* s5 */
475 			case _R_S6: /* s6 */
476 			case _R_S7: /* s7 */
477 			case _R_S8: /* s8 */
478 			case _R_RA: /* ra */
479 				regs[i.IType.rt] =
480 				    kdbrpeek(sp + (int16_t)i.IType.imm, size);
481 				mask |= (1 << i.IType.rt);
482 				break;
483 			}
484 			break;
485 		}
486 
487 		case OP_ADDI:
488 		case OP_ADDIU:
489 #if !defined(__mips_o32)
490 		case OP_DADDI:
491 		case OP_DADDIU:
492 #endif
493 			/* look for stack pointer adjustment */
494 			if (i.IType.rs != _R_SP || i.IType.rt != _R_SP)
495 				break;
496 			/* don't count pops for mcount */
497 			if (!foundframesize) {
498 				stksize = - ((short)i.IType.imm);
499 				foundframesize = 1;
500 			}
501 			break;
502 		}
503 	}
504 done:
505 	if (mask & (1 << _R_RA))
506 		ra = regs[_R_RA];
507 	(*printfn)("%#"PRIxVADDR": %s+%#"PRIxVADDR" (%#"PRIxREGISTER","
508 	    "%#"PRIxREGISTER",%#"PRIxREGISTER",%#"PRIxREGISTER") "
509 	    "ra %#"PRIxVADDR" sz %d\n",
510 	    sp, fn_name(subr), pc - subr,
511 	    regs[_R_A0], regs[_R_A1], regs[_R_A2], regs[_R_A3],
512 	    ra, stksize);
513 
514 	if (ra) {
515 		if (pc == ra && stksize == 0)
516 			(*printfn)("stacktrace: loop!\n");
517 		else {
518 			pc = ra;
519 			sp += stksize;
520 			ra = 0;
521 			goto loop;
522 		}
523 	} else {
524 finish:
525 #ifdef _KERNEL
526 		(*printfn)("User-level: pid %d.%d\n",
527 		    curlwp->l_proc->p_pid, curlwp->l_lid);
528 #else
529 		(*printfn)("User-level: FIXME\n");
530 #endif
531 	}
532 }
533 
534 #endif /* DEBUG || DDB || KGDB || geo */
535 #endif /* DDB_TRACE */
536