xref: /netbsd-src/sys/arch/m68k/m68k/db_trace.c (revision 5ae0a955493425be9125d9bd3239f6810ff39e12)
1 /*	$NetBSD: db_trace.c,v 1.63 2023/09/26 14:33:55 tsutsui Exp $	*/
2 
3 /*
4  * Mach Operating System
5  * Copyright (c) 1992 Carnegie Mellon University
6  * All Rights Reserved.
7  *
8  * Permission to use, copy, modify and distribute this software and its
9  * documentation is hereby granted, provided that both the copyright
10  * notice and this permission notice appear in all copies of the
11  * software, derivative works or modified versions, and any portions
12  * thereof, and that both notices appear in supporting documentation.
13  *
14  * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
15  * CONDITION.  CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
16  * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
17  *
18  * Carnegie Mellon requests users of this software to return to
19  *
20  *  Software Distribution Coordinator  or  Software.Distribution@CS.CMU.EDU
21  *  School of Computer Science
22  *  Carnegie Mellon University
23  *  Pittsburgh PA 15213-3890
24  *
25  * any improvements or extensions that they make and grant Carnegie Mellon
26  * the rights to redistribute these changes.
27  */
28 
29 #include <sys/cdefs.h>
30 __KERNEL_RCSID(0, "$NetBSD: db_trace.c,v 1.63 2023/09/26 14:33:55 tsutsui Exp $");
31 
32 #include <sys/param.h>
33 #include <sys/proc.h>
34 #include <sys/systm.h>
35 
36 #include <machine/db_machdep.h>
37 
38 #include <ddb/db_interface.h>
39 #include <ddb/db_output.h>
40 #include <ddb/db_access.h>
41 #include <ddb/db_sym.h>
42 #include <ddb/db_variables.h>
43 
44 /*
45  * Register list
46  */
47 static int db_var_short(const struct db_variable *, db_expr_t *, int);
48 
49 const struct db_variable db_regs[] = {
50 	/* D0-D7 */
51 	{ "d0",	(long *)&ddb_regs.tf_regs[0],	FCN_NULL, NULL },
52 	{ "d1",	(long *)&ddb_regs.tf_regs[1],	FCN_NULL, NULL },
53 	{ "d2",	(long *)&ddb_regs.tf_regs[2],	FCN_NULL, NULL },
54 	{ "d3",	(long *)&ddb_regs.tf_regs[3],	FCN_NULL, NULL },
55 	{ "d4",	(long *)&ddb_regs.tf_regs[4],	FCN_NULL, NULL },
56 	{ "d5",	(long *)&ddb_regs.tf_regs[5],	FCN_NULL, NULL },
57 	{ "d6",	(long *)&ddb_regs.tf_regs[6],	FCN_NULL, NULL },
58 	{ "d7",	(long *)&ddb_regs.tf_regs[7],	FCN_NULL, NULL },
59 	/* A0-A7 */
60 	{ "a0",	(long *)&ddb_regs.tf_regs[8+0],	FCN_NULL, NULL },
61 	{ "a1",	(long *)&ddb_regs.tf_regs[8+1],	FCN_NULL, NULL },
62 	{ "a2",	(long *)&ddb_regs.tf_regs[8+2],	FCN_NULL, NULL },
63 	{ "a3",	(long *)&ddb_regs.tf_regs[8+3],	FCN_NULL, NULL },
64 	{ "a4",	(long *)&ddb_regs.tf_regs[8+4],	FCN_NULL, NULL },
65 	{ "a5",	(long *)&ddb_regs.tf_regs[8+5],	FCN_NULL, NULL },
66 	{ "a6",	(long *)&ddb_regs.tf_regs[8+6],	FCN_NULL, NULL },
67 	{ "sp",	(long *)&ddb_regs.tf_regs[8+7],	FCN_NULL, NULL },
68 	/* misc. */
69 	{ "pc",	(long *)&ddb_regs.tf_pc,	FCN_NULL, NULL },
70 	{ "sr",	(long *)&ddb_regs.tf_sr,	db_var_short, NULL }
71 };
72 const struct db_variable * const db_eregs =
73     db_regs + sizeof(db_regs)/sizeof(db_regs[0]);
74 
75 static int
db_var_short(const struct db_variable * varp,db_expr_t * valp,int op)76 db_var_short(const struct db_variable *varp, db_expr_t *valp, int op)
77 {
78 
79     if (op == DB_VAR_GET)
80 	*valp = (db_expr_t)*((short*)varp->valuep);
81     else
82 	*((short*)varp->valuep) = (short) *valp;
83     return 0;
84 }
85 
86 #define	MAXINT	0x7fffffff
87 
88 #define	INKERNEL(va,pcb)	(((u_int)(va) > (u_int)(pcb)) && \
89 				 ((u_int)(va) < ((u_int)(pcb) + USPACE)))
90 
91 #define	get(addr, space) \
92 		(db_get_value((db_addr_t)(addr), sizeof(int), false))
93 #define	get16(addr, space) \
94 		(db_get_value((db_addr_t)(addr), sizeof(u_short), false))
95 
96 #define	NREGISTERS	16
97 
98 struct stackpos {
99 	 int	k_pc;
100 	 int	k_fp;
101 	 int	k_nargs;
102 	 int	k_entry;
103 	 int	k_caller;
104 	 int	k_flags;
105 	 int	k_regloc[NREGISTERS];
106 };
107 
108 static void	findentry(struct stackpos *, void (*)(const char *, ...));
109 #ifdef _KERNEL
110 static void	findregs(struct stackpos *, db_addr_t);
111 static int	nextframe(struct stackpos *, struct pcb *, int,
112 		    void (*)(const char *, ...));
113 #endif
114 static void	stacktop(db_regs_t *, struct stackpos *,
115 		    void (*)(const char *, ...));
116 
117 
118 #define FR_SAVFP	0
119 #define FR_SAVPC	4
120 
121 static void
stacktop(db_regs_t * regs,struct stackpos * sp,void (* pr)(const char *,...))122 stacktop(db_regs_t *regs, struct stackpos *sp, void (*pr)(const char *, ...))
123 {
124 	int i;
125 
126 	/* Note: leave out a6, a7 */
127 	for (i = 0; i < (8+6); i++) {
128 		sp->k_regloc[i] = (int) &regs->tf_regs[i];
129 	}
130 
131 	sp->k_fp = get(&regs->tf_regs[8+6], 0);
132 	/* skip sp (a7) */
133 	sp->k_pc = get(&regs->tf_pc, 0);
134 	sp->k_flags = 0;
135 
136 	findentry(sp, pr);
137 }
138 
139 
140 /*
141  * The VAX has a very nice calling convention, and it is quite easy to
142  * find saved registers, and the number of parameters. We are not nearly
143  * so lucky. We must grub around in code for much of this information
144  * (remember the PDP-11?), and the saved register list seems to be
145  * especially hard to find.
146  */
147 
148 #define HIWORD	0xffff0000
149 #define LOWORD	0x0000ffff
150 #define LINKLA6	0x480e0000	/* linkl a6,#x    */
151 #define LINKWA6	0x4e560000	/* linkw a6,#x    */
152 #define ADDLSP	0xdffc0000	/* addl #x,sp    */
153 #define ADDWSP	0xdefc0000	/* addw #x,sp    */
154 #define LEASP	0x4fef0000	/* lea	sp@(x),sp*/
155 #define TSTBSP	0x4a2f0000	/* tstb sp@(x)   */
156 #define INSMSK	0xfff80000
157 #define MOVLSP	0x2e800000	/* movl dx,sp@   */
158 #define MOVLD0	0x20000000	/* movl d0,dx	 */
159 #define MOVLA0	0x20400000	/* movl d0,ax	 */
160 #define MVLMSK	0xf1ff0000
161 #define MOVEML	0x48d70000	/* moveml #x,sp@ */
162 #define JSR	0x4eb80000	/* jsr x.[WL]    */
163 #define JSRPC	0x4eba0000	/* jsr PC@( )    */
164 #define LONGBIT 0x00010000
165 #define BSR	0x61000000	/* bsr x	 */
166 #define BSRL	0x61ff0000	/* bsrl x	 */
167 #define BYTE3	0x0000ff00
168 #define LOBYTE	0x000000ff
169 #define ADQMSK	0xf1ff0000
170 #define ADDQSP	0x508f0000	/* addql #x,sp   */
171 #define ADDQWSP	0x504f0000	/* addqw #x,sp   */
172 
173 #if 0
174 static struct nlist *	trampsym = 0;
175 static struct nlist *	funcsym = 0;
176 #endif
177 
178 #ifdef _KERNEL
179 static int
nextframe(struct stackpos * sp,struct pcb * pcb,int kerneltrace,void (* pr)(const char *,...))180 nextframe(struct stackpos *sp, struct pcb *pcb, int kerneltrace,
181     void (*pr)(const char *, ...))
182 {
183 	int		i;
184 	db_addr_t	addr;
185 	db_addr_t	calladdr;
186 	db_addr_t	oldfp = sp->k_fp;
187 
188 	/*
189 	 * Find our entry point. Then find out
190 	 * which registers we saved, and map them.
191 	 * Our entry point is the address our caller called.
192 	 */
193 
194 	calladdr = sp->k_caller;
195 	addr     = sp->k_entry;
196 	if (addr == MAXINT) {
197 
198 		/*
199 		 * we don't know what registers are involved here,
200 		 * invalidate them all.
201 		 */
202 		for (i = 0; i < NREGISTERS; i++)
203 			sp->k_regloc[i] = -1;
204 	} else
205 		findregs(sp, addr);
206 
207 	/* find caller's pc and fp */
208 	sp->k_pc = calladdr;
209 	sp->k_fp = get(sp->k_fp + FR_SAVFP, DSP);
210 
211 	/*
212 	 * Now that we have assumed the identity of our caller, find
213 	 * how many longwords of argument WE were called with.
214 	 */
215 	sp->k_flags = 0;
216 
217 	/*
218 	 * Don't dig around in user stack to find no. of args and
219 	 * entry point if just tracing the kernel
220 	 */
221 	if (kerneltrace && !INKERNEL(sp->k_fp, pcb)) {
222 		sp->k_nargs = 0;
223 		sp->k_entry = MAXINT;
224 	} else
225 		findentry(sp, pr);
226 
227 	if (sp->k_fp == 0 || oldfp == (db_addr_t)sp->k_fp)
228 		return 0;
229 	return sp->k_fp;
230 }
231 #endif
232 
233 static void
findentry(struct stackpos * sp,void (* pr)(const char *,...))234 findentry(struct stackpos *sp, void (*pr)(const char *, ...))
235 {
236 	/*
237 	 * Set the k_nargs and k_entry fields in the stackpos structure.  This
238 	 * is called from stacktop() and from nextframe().  Our caller will do
239 	 * an addq or addl or addw to sp just after we return to pop off our
240 	 * arguments.  Find that instruction and extract the value.
241 	 */
242 	int		instruc;
243 	int		val;
244 	db_addr_t	addr, nextword;
245 
246 	addr = get(sp->k_fp + FR_SAVPC, DSP);
247 	if (addr == 0) {
248 		/* oops -- we touched something we ought not to have */
249 		/* cannot trace caller of "start" */
250 		sp->k_entry = MAXINT;
251 		sp->k_nargs = 0;
252 		return;
253 	}
254 	instruc  = get(addr - 6, ISP);
255 	nextword = get(addr - 4, ISP);
256 
257 	if ((instruc & HIWORD) == (JSR | LONGBIT)) {
258 		/* longword offset here */
259 		sp->k_caller = addr - 6;
260 		sp->k_entry  = nextword;
261 	} else if ((instruc & HIWORD) == BSRL) {
262 		/* longword self-relative offset */
263 		sp->k_caller = addr - 6;
264 		sp->k_entry  = nextword + (addr - 4);
265 	} else {
266 		instruc = nextword;
267 		if ((instruc & HIWORD) == JSR) {
268 			/* short word offset */
269 			sp->k_caller = addr - 4;
270 			sp->k_entry  = instruc & LOWORD;
271 		} else if ((instruc & HIWORD) == BSR) {
272 			/* short word, self-relative offset */
273 			sp->k_caller = addr - 4;
274 			sp->k_entry  = (addr - 2) + (short)(instruc & LOWORD);
275 		} else if ((instruc & HIWORD) == JSRPC) {
276 			/* PC-relative, short word offset */
277 			sp->k_caller = addr - 4;
278 			sp->k_entry  = (addr - 2) + (instruc & LOWORD);
279 		} else {
280 			if ((instruc & BYTE3) == (BSR >> 16)) {
281 				/* byte, self-relative offset */
282 				sp->k_caller = addr - 2;
283 				sp->k_entry  = addr + (char)(instruc & LOBYTE);
284 			} else {
285 				/* was a call through a proc parameter */
286 				sp->k_caller = addr - 2;
287 				sp->k_entry  = MAXINT;
288 			}
289 		}
290 	}
291 	instruc = get(addr, ISP);
292 	/* on bad days, the compiler dumps a register move here */
293 	if ((instruc & MVLMSK) == MOVLA0 ||
294 	    (instruc & MVLMSK) == MOVLD0)
295 		instruc = get(addr += 2, ISP);
296 	if ((instruc & ADQMSK) == ADDQSP ||
297 	    (instruc & ADQMSK) == ADDQWSP) {
298 		val = 0;
299 		do {
300 			int n;
301 			n = (instruc >> (16+9)) & 07;
302 			if (n == 0)
303 				n = 8;
304 			val += n;
305 			instruc = get(addr += 2, ISP);
306 		} while ((instruc & ADQMSK) == ADDQSP ||
307 			 (instruc & ADQMSK) == ADDQWSP);
308 	} else if ((instruc & HIWORD) == ADDLSP)
309 		val = get(addr + 2, ISP);
310 	else if ((instruc & HIWORD) == ADDWSP ||
311 		 (instruc & HIWORD) == LEASP)
312 		val = instruc & LOWORD;
313 	else
314 		val = 20;
315 	sp->k_nargs = val / 4;
316 }
317 
318 #ifdef _KERNEL
319 /*
320  * Look at the procedure prolog of the current called procedure.
321  * Figure out which registers we saved, and where they are
322  */
323 static void
findregs(struct stackpos * sp,db_addr_t addr)324 findregs(struct stackpos *sp, db_addr_t addr)
325 {
326 	long instruc, val, i;
327 	int  regp;
328 
329 	regp = 0;
330 	instruc = get(addr, ISP);
331 	if ((instruc & HIWORD) == LINKLA6) {
332 		instruc = get(addr + 2, ISP);
333 		addr += 6;
334 		regp = sp->k_fp + instruc;
335 	} else if ((instruc & HIWORD) == LINKWA6) {
336 		addr += 4;
337 		if ((instruc &= LOWORD) == 0) {
338 			/* look for addl */
339 			instruc = get(addr, ISP);
340 			if ((instruc & HIWORD) == ADDLSP) {
341 				instruc = get(addr + 2, ISP);
342 				addr += 6;
343 			}
344 			/* else frame is really size 0 */
345 		} else {
346 			/* link offset was non-zero -- sign extend it */
347 			instruc <<= 16;
348 			instruc >>= 16;
349 		}
350 		/* we now have the negative frame size */
351 		regp = sp->k_fp + instruc;
352 	}
353 
354 	/* find which registers were saved */
355 	/* (expecting probe instruction next) */
356 	instruc = get(addr, ISP);
357 	if ((instruc & HIWORD) == TSTBSP)
358 		addr += 4;
359 
360 	/* now we expect either a moveml or a movl */
361 	instruc = get(addr, ISP);
362 	if ((instruc & INSMSK) == MOVLSP) {
363 		/* only saving one register */
364 		i = (instruc >> 16) & 07;
365 		sp->k_regloc[i] = regp;
366 	} else if ((instruc & HIWORD) == MOVEML) {
367 		/* saving multiple registers or unoptimized code */
368 		val = instruc & LOWORD;
369 		i = 0;
370 		while (val) {
371 			if (val & 1) {
372 				sp->k_regloc[i] = regp;
373 				regp += sizeof(int);
374 			}
375 			val >>= 1;
376 			i++;
377 		}
378 	}
379 	/* else no registers saved */
380 }
381 #endif
382 
383 /*
384  *	Frame tracing.
385  */
386 void
db_stack_trace_print(db_expr_t addr,bool have_addr,db_expr_t count,const char * modif,void (* pr)(const char *,...))387 db_stack_trace_print(db_expr_t addr, bool have_addr, db_expr_t count,
388     const char *modif, void (*pr)(const char *, ...))
389 {
390 	int i, nargs;
391 	long val;
392 	db_addr_t	regp;
393 	const char *	name;
394 	struct stackpos pos;
395 	struct pcb	*pcb;
396 	struct lwp	*l;
397 #ifdef _KERNEL
398 	bool		kernel_only = true;
399 #endif
400 	bool		trace_thread = false;
401 	bool		lwpaddr = false;
402 	int		fault_pc = 0;
403 
404 	{
405 		const char *cp = modif;
406 		char c;
407 
408 		while ((c = *cp++) != 0) {
409 			if (c == 'a') {
410 				lwpaddr = true;
411 				trace_thread = true;
412 			} else if (c == 't')
413 				trace_thread = true;
414 #ifdef _KERNEL
415 			else if (c == 'u')
416 				kernel_only = false;
417 #endif
418 		}
419 	}
420 
421 #ifdef _KERNEL
422 	l = curlwp;
423 #endif
424 	if (!have_addr)
425 		stacktop(&ddb_regs, &pos, pr);
426 	else {
427 		if (trace_thread) {
428 			struct proc *p;
429 
430 			if (lwpaddr) {
431 				l = (struct lwp *)addr;
432 				p = l->l_proc;
433 				(*pr)("trace: pid %d ", p->p_pid);
434 			} else {
435 				(*pr)("trace: pid %d ", (int)addr);
436 #ifdef _KERNEL
437 				p = proc_find_raw(addr);
438 				if (p == NULL) {
439 					(*pr)("not found\n");
440 					return;
441 				}
442 				l = LIST_FIRST(&p->p_lwps);
443 				KASSERT(l != NULL);
444 #else
445 				(*pr)("no proc_find_raw() in crash\n");
446                                 return;
447 #endif
448 			}
449 			(*pr)("lid %d ", l->l_lid);
450 			pcb = lwp_getpcb(l);
451 			pos.k_fp = pcb->pcb_regs[PCB_REGS_FP];
452 			/*
453 			 * Note: The following only works because cpu_switch()
454 			 * doesn't push anything on the stack before it saves
455 			 * the process' context in the pcb.
456 			 */
457 			pos.k_pc = get(pcb->pcb_regs[PCB_REGS_SP], DSP);
458 			(*pr)("at %p\n", (void *)pos.k_fp);
459 		} else {
460 			pos.k_fp = addr;
461 			pos.k_pc = MAXINT;
462 		}
463 
464 		pos.k_flags = 0;
465 		pos.k_nargs = 0;
466 		pos.k_entry = MAXINT;
467 
468 		for (i = 0; i < NREGISTERS; i++)
469 			pos.k_regloc[i] = 0;
470 
471 		findentry(&pos, pr);
472 	}
473 
474 	while (count) {
475 		count--;
476 
477 		/* HACK */
478 		if (pos.k_pc == MAXINT) {
479 			name = "?";
480 			pos.k_pc = 0;
481 			val = MAXINT;
482 		} else {
483 			db_find_sym_and_offset(pos.k_pc, &name, &val);
484 			if (name == 0) {
485 				name = "?";
486 				val = MAXINT;
487 			}
488 		}
489 
490 		/*
491 		 * Since faultstkadj doesn't set up a valid stack frame,
492 		 * we would assume it was the source of the fault. To
493 		 * get around this we peek just past the fourth argument of
494 		 * "trap()" (the stack frame at the time of the fault)
495 		 * to determine the _real_ value of PC when things went
496 		 * wrong.
497 		 *
498 		 * NOTE: If the argument list for 'trap()' ever changes,
499 		 * we lose.
500 		 */
501 		if (strcmp(___STRING(_C_LABEL(trap)), name) == 0) {
502 			int tfp;
503 
504 			/* Point to frame structure just past 'trap()'s 4th argument */
505 			tfp = pos.k_fp + FR_SAVFP + 4 + (5 * 4);
506 
507 			/* Determine if fault was from kernel or user mode */
508 			regp = tfp + offsetof(struct frame, f_sr);
509 			if (!USERMODE(get16(regp, DSP))) {
510 
511 				/*
512 				 * Definitely a kernel mode fault,
513 				 * so get the PC at the time of the fault.
514 				 */
515 				regp = tfp + offsetof(struct frame, f_pc);
516 				fault_pc = get(regp, DSP);
517 			}
518 		} else if (fault_pc) {
519 			if (strcmp("faultstkadj", name) == 0) {
520 				db_find_sym_and_offset(fault_pc, &name, &val);
521 				if (name == 0) {
522 					name = "?";
523 					val = MAXINT;
524 				}
525 			}
526 			fault_pc = 0;
527 		}
528 
529 		(*pr)("%s", name);
530 		if (pos.k_entry != MAXINT && name) {
531 			const char *entry_name;
532 			long	e_val;
533 
534 			db_find_sym_and_offset(pos.k_entry, &entry_name,
535 			    &e_val);
536 			if (entry_name != 0 && entry_name != name &&
537 			    e_val != val) {
538 				(*pr)("(?)\n%s", entry_name);
539 			}
540 		}
541 		(*pr)("(");
542 		regp = pos.k_fp + FR_SAVFP + 4;
543 		if ((nargs = pos.k_nargs)) {
544 			while (nargs--) {
545 				(*pr)("%lx", get(regp += 4, DSP));
546 				if (nargs)
547 					(*pr)(",");
548 			}
549 		}
550 		if (val == MAXINT)
551 			(*pr)(") at %x\n", pos.k_pc);
552 		else
553 			(*pr)(") + %lx\n", val);
554 
555 #ifdef _KERNEL
556 		/*
557 		 * Stop tracing if frame ptr no longer points into kernel
558 		 * stack.
559 		 */
560 		pcb = lwp_getpcb(l);
561 		if (kernel_only && !INKERNEL(pos.k_fp, pcb))
562 			break;
563 		if (nextframe(&pos, pcb, kernel_only, pr) == 0)
564 			break;
565 #endif
566 	}
567 }
568