xref: /trueos/sys/mips/mips/db_trace.c (revision 0f382ff74a6672094e30146db1b8ab03245e6229)
1 /*-
2  * Copyright (c) 2004-2005, Juniper Networks, Inc.
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17  * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24  * SUCH DAMAGE.
25  *
26  *	JNPR: db_trace.c,v 1.8 2007/08/09 11:23:32 katta
27  */
28 
29 #include <sys/cdefs.h>
30 __FBSDID("$FreeBSD$");
31 
32 #include <sys/param.h>
33 #include <sys/systm.h>
34 #include <sys/kdb.h>
35 #include <sys/proc.h>
36 #include <sys/stack.h>
37 #include <sys/sysent.h>
38 
39 #include <machine/db_machdep.h>
40 #include <machine/md_var.h>
41 #include <machine/mips_opcode.h>
42 #include <machine/pcb.h>
43 #include <machine/trap.h>
44 
45 #include <ddb/ddb.h>
46 #include <ddb/db_sym.h>
47 
48 extern char _locore[];
49 extern char _locoreEnd[];
50 extern char edata[];
51 
52 /*
53  * A function using a stack frame has the following instruction as the first
54  * one: [d]addiu sp,sp,-<frame_size>
55  *
56  * We make use of this to detect starting address of a function. This works
57  * better than using 'j ra' instruction to signify end of the previous
58  * function (for e.g. functions like boot() or panic() do not actually
59  * emit a 'j ra' instruction).
60  *
61  * XXX the abi does not require that the addiu instruction be the first one.
62  */
63 #define	MIPS_START_OF_FUNCTION(ins)	((((ins) & 0xffff8000) == 0x27bd8000) \
64 	|| (((ins) & 0xffff8000) == 0x67bd8000))
65 
66 /*
67  * MIPS ABI 3.0 requires that all functions return using the 'j ra' instruction
68  *
69  * XXX gcc doesn't do this for functions with __noreturn__ attribute.
70  */
71 #define	MIPS_END_OF_FUNCTION(ins)	((ins) == 0x03e00008)
72 
73 #if defined(__mips_n64)
74 #	define	MIPS_IS_VALID_KERNELADDR(reg)	((((reg) & 3) == 0) && \
75 					((vm_offset_t)(reg) >= MIPS_XKPHYS_START))
76 #else
77 #	define	MIPS_IS_VALID_KERNELADDR(reg)	((((reg) & 3) == 0) && \
78 					((vm_offset_t)(reg) >= MIPS_KSEG0_START))
79 #endif
80 
81 /*
82  * Functions ``special'' enough to print by name
83  */
84 #ifdef __STDC__
85 #define	Name(_fn)  { (void*)_fn, # _fn }
86 #else
87 #define	Name(_fn) { _fn, "_fn"}
88 #endif
89 static struct {
90 	void *addr;
91 	char *name;
92 }      names[] = {
93 
94 	Name(trap),
95 	Name(MipsKernGenException),
96 	Name(MipsUserGenException),
97 	Name(MipsKernIntr),
98 	Name(MipsUserIntr),
99 	Name(cpu_switch),
100 	{
101 		0, 0
102 	}
103 };
104 
105 /*
106  * Map a function address to a string name, if known; or a hex string.
107  */
108 static char *
fn_name(uintptr_t addr)109 fn_name(uintptr_t addr)
110 {
111 	static char buf[17];
112 	int i = 0;
113 
114 	db_expr_t diff;
115 	c_db_sym_t sym;
116 	char *symname;
117 
118 	diff = 0;
119 	symname = NULL;
120 	sym = db_search_symbol((db_addr_t)addr, DB_STGY_ANY, &diff);
121 	db_symbol_values(sym, (const char **)&symname, (db_expr_t *)0);
122 	if (symname && diff == 0)
123 		return (symname);
124 
125 	for (i = 0; names[i].name; i++)
126 		if (names[i].addr == (void *)addr)
127 			return (names[i].name);
128 	sprintf(buf, "%jx", (uintmax_t)addr);
129 	return (buf);
130 }
131 
132 void
stacktrace_subr(register_t pc,register_t sp,register_t ra,int (* printfn)(const char *,...))133 stacktrace_subr(register_t pc, register_t sp, register_t ra,
134 	int (*printfn) (const char *,...))
135 {
136 	InstFmt i;
137 	/*
138 	 * Arrays for a0..a3 registers and flags if content
139 	 * of these registers is valid, e.g. obtained from the stack
140 	 */
141 	int valid_args[4];
142 	uintptr_t args[4];
143 	uintptr_t va, subr;
144 	unsigned instr, mask;
145 	unsigned int frames = 0;
146 	int more, stksize, j;
147 
148 /* Jump here when done with a frame, to start a new one */
149 loop:
150 
151 	/*
152 	 * Invalidate arguments values
153 	 */
154 	valid_args[0] = 0;
155 	valid_args[1] = 0;
156 	valid_args[2] = 0;
157 	valid_args[3] = 0;
158 /* Jump here after a nonstandard (interrupt handler) frame */
159 	stksize = 0;
160 	subr = 0;
161 	if (frames++ > 100) {
162 		(*printfn) ("\nstackframe count exceeded\n");
163 		/* return breaks stackframe-size heuristics with gcc -O2 */
164 		goto finish;	/* XXX */
165 	}
166 	/* check for bad SP: could foul up next frame */
167 	/*XXX MIPS64 bad: this hard-coded SP is lame */
168 	if (!MIPS_IS_VALID_KERNELADDR(sp)) {
169 		(*printfn) ("SP 0x%jx: not in kernel\n", sp);
170 		ra = 0;
171 		subr = 0;
172 		goto done;
173 	}
174 #define Between(x, y, z) \
175 		( ((x) <= (y)) && ((y) < (z)) )
176 #define pcBetween(a,b) \
177 		Between((uintptr_t)a, pc, (uintptr_t)b)
178 
179 	/*
180 	 * Check for current PC in  exception handler code that don't have a
181 	 * preceding "j ra" at the tail of the preceding function. Depends
182 	 * on relative ordering of functions in exception.S, swtch.S.
183 	 */
184 	if (pcBetween(MipsKernGenException, MipsUserGenException))
185 		subr = (uintptr_t)MipsKernGenException;
186 	else if (pcBetween(MipsUserGenException, MipsKernIntr))
187 		subr = (uintptr_t)MipsUserGenException;
188 	else if (pcBetween(MipsKernIntr, MipsUserIntr))
189 		subr = (uintptr_t)MipsKernIntr;
190 	else if (pcBetween(MipsUserIntr, MipsTLBInvalidException))
191 		subr = (uintptr_t)MipsUserIntr;
192 	else if (pcBetween(MipsTLBInvalidException, MipsTLBMissException))
193 		subr = (uintptr_t)MipsTLBInvalidException;
194 	else if (pcBetween(fork_trampoline, savectx))
195 		subr = (uintptr_t)fork_trampoline;
196 	else if (pcBetween(savectx, cpu_throw))
197 		subr = (uintptr_t)savectx;
198 	else if (pcBetween(cpu_throw, cpu_switch))
199 		subr = (uintptr_t)cpu_throw;
200 	else if (pcBetween(cpu_switch, MipsSwitchFPState))
201 		subr = (uintptr_t)cpu_switch;
202 	else if (pcBetween(_locore, _locoreEnd)) {
203 		subr = (uintptr_t)_locore;
204 		ra = 0;
205 		goto done;
206 	}
207 	/* check for bad PC */
208 	/*XXX MIPS64 bad: These hard coded constants are lame */
209 	if (!MIPS_IS_VALID_KERNELADDR(pc)) {
210 		(*printfn) ("PC 0x%jx: not in kernel\n", pc);
211 		ra = 0;
212 		goto done;
213 	}
214 	/*
215 	 * Find the beginning of the current subroutine by scanning
216 	 * backwards from the current PC for the end of the previous
217 	 * subroutine.
218 	 */
219 	if (!subr) {
220 		va = pc - sizeof(int);
221 		while (1) {
222 			instr = kdbpeek((int *)va);
223 
224 			if (MIPS_START_OF_FUNCTION(instr))
225 				break;
226 
227 			if (MIPS_END_OF_FUNCTION(instr)) {
228 				/* skip over branch-delay slot instruction */
229 				va += 2 * sizeof(int);
230 				break;
231 			}
232 
233  			va -= sizeof(int);
234 		}
235 
236 		/* skip over nulls which might separate .o files */
237 		while ((instr = kdbpeek((int *)va)) == 0)
238 			va += sizeof(int);
239 		subr = va;
240 	}
241 	/* scan forwards to find stack size and any saved registers */
242 	stksize = 0;
243 	more = 3;
244 	mask = 0;
245 	for (va = subr; more; va += sizeof(int),
246 	    more = (more == 3) ? 3 : more - 1) {
247 		/* stop if hit our current position */
248 		if (va >= pc)
249 			break;
250 		instr = kdbpeek((int *)va);
251 		i.word = instr;
252 		switch (i.JType.op) {
253 		case OP_SPECIAL:
254 			switch (i.RType.func) {
255 			case OP_JR:
256 			case OP_JALR:
257 				more = 2;	/* stop after next instruction */
258 				break;
259 
260 			case OP_SYSCALL:
261 			case OP_BREAK:
262 				more = 1;	/* stop now */
263 			};
264 			break;
265 
266 		case OP_BCOND:
267 		case OP_J:
268 		case OP_JAL:
269 		case OP_BEQ:
270 		case OP_BNE:
271 		case OP_BLEZ:
272 		case OP_BGTZ:
273 			more = 2;	/* stop after next instruction */
274 			break;
275 
276 		case OP_COP0:
277 		case OP_COP1:
278 		case OP_COP2:
279 		case OP_COP3:
280 			switch (i.RType.rs) {
281 			case OP_BCx:
282 			case OP_BCy:
283 				more = 2;	/* stop after next instruction */
284 			};
285 			break;
286 
287 		case OP_SW:
288 			/* look for saved registers on the stack */
289 			if (i.IType.rs != 29)
290 				break;
291 			/* only restore the first one */
292 			if (mask & (1 << i.IType.rt))
293 				break;
294 			mask |= (1 << i.IType.rt);
295 			switch (i.IType.rt) {
296 			case 4:/* a0 */
297 				args[0] = kdbpeek((int *)(sp + (short)i.IType.imm));
298 				valid_args[0] = 1;
299 				break;
300 
301 			case 5:/* a1 */
302 				args[1] = kdbpeek((int *)(sp + (short)i.IType.imm));
303 				valid_args[1] = 1;
304 				break;
305 
306 			case 6:/* a2 */
307 				args[2] = kdbpeek((int *)(sp + (short)i.IType.imm));
308 				valid_args[2] = 1;
309 				break;
310 
311 			case 7:/* a3 */
312 				args[3] = kdbpeek((int *)(sp + (short)i.IType.imm));
313 				valid_args[3] = 1;
314 				break;
315 
316 			case 31:	/* ra */
317 				ra = kdbpeek((int *)(sp + (short)i.IType.imm));
318 			}
319 			break;
320 
321 		case OP_SD:
322 			/* look for saved registers on the stack */
323 			if (i.IType.rs != 29)
324 				break;
325 			/* only restore the first one */
326 			if (mask & (1 << i.IType.rt))
327 				break;
328 			mask |= (1 << i.IType.rt);
329 			switch (i.IType.rt) {
330 			case 4:/* a0 */
331 				args[0] = kdbpeekd((int *)(sp + (short)i.IType.imm));
332 				valid_args[0] = 1;
333 				break;
334 
335 			case 5:/* a1 */
336 				args[1] = kdbpeekd((int *)(sp + (short)i.IType.imm));
337 				valid_args[1] = 1;
338 				break;
339 
340 			case 6:/* a2 */
341 				args[2] = kdbpeekd((int *)(sp + (short)i.IType.imm));
342 				valid_args[2] = 1;
343 				break;
344 
345 			case 7:/* a3 */
346 				args[3] = kdbpeekd((int *)(sp + (short)i.IType.imm));
347 				valid_args[3] = 1;
348 				break;
349 
350 			case 31:	/* ra */
351 				ra = kdbpeekd((int *)(sp + (short)i.IType.imm));
352 			}
353 			break;
354 
355 		case OP_ADDI:
356 		case OP_ADDIU:
357 		case OP_DADDI:
358 		case OP_DADDIU:
359 			/* look for stack pointer adjustment */
360 			if (i.IType.rs != 29 || i.IType.rt != 29)
361 				break;
362 			stksize = -((short)i.IType.imm);
363 		}
364 	}
365 
366 done:
367 	(*printfn) ("%s+%x (", fn_name(subr), pc - subr);
368 	for (j = 0; j < 4; j ++) {
369 		if (j > 0)
370 			(*printfn)(",");
371 		if (valid_args[j])
372 			(*printfn)("%x", args[j]);
373 		else
374 			(*printfn)("?");
375 	}
376 
377 	(*printfn) (") ra %jx sp %jx sz %d\n", ra, sp, stksize);
378 
379 	if (ra) {
380 		if (pc == ra && stksize == 0)
381 			(*printfn) ("stacktrace: loop!\n");
382 		else {
383 			pc = ra;
384 			sp += stksize;
385 			ra = 0;
386 			goto loop;
387 		}
388 	} else {
389 finish:
390 		if (curproc)
391 			(*printfn) ("pid %d\n", curproc->p_pid);
392 		else
393 			(*printfn) ("curproc NULL\n");
394 	}
395 }
396 
397 
398 int
db_md_set_watchpoint(db_expr_t addr,db_expr_t size)399 db_md_set_watchpoint(db_expr_t addr, db_expr_t size)
400 {
401 
402 	return(0);
403 }
404 
405 
406 int
db_md_clr_watchpoint(db_expr_t addr,db_expr_t size)407 db_md_clr_watchpoint(db_expr_t addr, db_expr_t size)
408 {
409 
410 	return(0);
411 }
412 
413 
414 void
db_md_list_watchpoints()415 db_md_list_watchpoints()
416 {
417 }
418 
419 void
db_trace_self(void)420 db_trace_self(void)
421 {
422 	db_trace_thread (curthread, -1);
423 	return;
424 }
425 
426 int
db_trace_thread(struct thread * thr,int count)427 db_trace_thread(struct thread *thr, int count)
428 {
429 	register_t pc, ra, sp;
430 	struct pcb *ctx;
431 
432 	if (thr == curthread) {
433 		sp = (register_t)(intptr_t)__builtin_frame_address(0);
434 		ra = (register_t)(intptr_t)__builtin_return_address(0);
435 
436         	__asm __volatile(
437 			"jal 99f\n"
438 			"nop\n"
439 			"99:\n"
440                          "move %0, $31\n" /* get ra */
441                          "move $31, %1\n" /* restore ra */
442                          : "=r" (pc)
443 			 : "r" (ra));
444 
445 	} else {
446 		ctx = kdb_thr_ctx(thr);
447 		sp = (register_t)ctx->pcb_context[PCB_REG_SP];
448 		pc = (register_t)ctx->pcb_context[PCB_REG_PC];
449 		ra = (register_t)ctx->pcb_context[PCB_REG_RA];
450 	}
451 
452 	stacktrace_subr(pc, sp, ra,
453 	    (int (*) (const char *, ...))db_printf);
454 
455 	return (0);
456 }
457 
458 void
db_show_mdpcpu(struct pcpu * pc)459 db_show_mdpcpu(struct pcpu *pc)
460 {
461 
462 	db_printf("ipis         = 0x%x\n", pc->pc_pending_ipis);
463 	db_printf("next ASID    = %d\n", pc->pc_next_asid);
464 	db_printf("GENID        = %d\n", pc->pc_asid_generation);
465 	return;
466 }
467