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) ®s->tf_regs[i];
129 }
130
131 sp->k_fp = get(®s->tf_regs[8+6], 0);
132 /* skip sp (a7) */
133 sp->k_pc = get(®s->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