Home | History | Annotate | Line # | Download | only in x86
      1 /*	$NetBSD: db_trace.c,v 1.6 2022/12/24 14:14:52 uwe Exp $	*/
      2 
      3 /*
      4  * Mach Operating System
      5  * Copyright (c) 1991,1990 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 (at) 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 the
     26  * rights to redistribute these changes.
     27  */
     28 
     29 #include <sys/cdefs.h>
     30 __KERNEL_RCSID(0, "$NetBSD: db_trace.c,v 1.6 2022/12/24 14:14:52 uwe Exp $");
     31 
     32 #include <sys/param.h>
     33 #include <sys/systm.h>
     34 #include <sys/proc.h>
     35 
     36 #include <uvm/uvm_prot.h>
     37 #include <uvm/uvm_pmap.h>
     38 
     39 #include <machine/frame.h>
     40 #include <machine/trap.h>
     41 #include <machine/intrdefs.h>
     42 #include <machine/pmap.h>
     43 
     44 #include <machine/db_machdep.h>
     45 #include <ddb/db_sym.h>
     46 #include <ddb/db_access.h>
     47 #include <ddb/db_variables.h>
     48 #include <ddb/db_output.h>
     49 #include <ddb/db_interface.h>
     50 #include <ddb/db_user.h>
     51 #include <ddb/db_proc.h>
     52 #include <ddb/db_command.h>
     53 #include <x86/db_machdep.h>
     54 
     55 int
     56 db_x86_regop(const struct db_variable *vp, db_expr_t *val, int opcode)
     57 {
     58 	db_expr_t *regaddr =
     59 	    (db_expr_t *)(((uint8_t *)DDB_REGS) + ((size_t)vp->valuep));
     60 
     61 	switch (opcode) {
     62 	case DB_VAR_GET:
     63 		*val = *regaddr;
     64 		break;
     65 	case DB_VAR_SET:
     66 		*regaddr = *val;
     67 		break;
     68 	default:
     69 		db_printf("db_x86_regop: unknown op %d", opcode);
     70 		db_error(NULL);
     71 	}
     72 	return 0;
     73 }
     74 
     75 /*
     76  * Stack trace.
     77  */
     78 
     79 #if 0
     80 db_addr_t db_trap_symbol_value = 0;
     81 db_addr_t db_syscall_symbol_value = 0;
     82 db_addr_t db_kdintr_symbol_value = 0;
     83 bool db_trace_symbols_found = false;
     84 
     85 void db_find_trace_symbols(void);
     86 
     87 void
     88 db_find_trace_symbols(void)
     89 {
     90 	db_expr_t value;
     91 
     92 	if (db_value_of_name("_trap", &value))
     93 		db_trap_symbol_value = (db_addr_t) value;
     94 	if (db_value_of_name("_kdintr", &value))
     95 		db_kdintr_symbol_value = (db_addr_t) value;
     96 	if (db_value_of_name("_syscall", &value))
     97 		db_syscall_symbol_value = (db_addr_t) value;
     98 	db_trace_symbols_found = true;
     99 }
    100 #endif
    101 
    102 #define set_frame_callpc() do {				\
    103 		frame = (long *)ddb_regs.tf_bp;		\
    104 		callpc = (db_addr_t)ddb_regs.tf_ip;	\
    105 	} while (/*CONSTCCOND*/0)
    106 
    107 void
    108 db_stack_trace_print(db_expr_t addr, bool have_addr, db_expr_t count,
    109     const char *modif, void (*pr)(const char *, ...))
    110 {
    111 	long *frame, *lastframe;
    112 	long *retaddr, *arg0;
    113 	long *argp;
    114 	db_addr_t callpc;
    115 	int is_trap;
    116 	bool kernel_only = true;
    117 	bool trace_thread = false;
    118 	bool lwpaddr = false;
    119 
    120 #if 0
    121 	if (!db_trace_symbols_found)
    122 		db_find_trace_symbols();
    123 #endif
    124 
    125 	{
    126 		const char *cp = modif;
    127 		char c;
    128 
    129 		while ((c = *cp++) != 0) {
    130 			if (c == 'a') {
    131 				lwpaddr = true;
    132 				trace_thread = true;
    133 			}
    134 			if (c == 't')
    135 				trace_thread = true;
    136 			if (c == 'u')
    137 				kernel_only = false;
    138 		}
    139 	}
    140 
    141 	if (have_addr && trace_thread) {
    142 		struct pcb *pcb;
    143 		proc_t p;
    144 		lwp_t l;
    145 
    146 		if (lwpaddr) {
    147 			db_read_bytes(addr, sizeof(l),
    148 			    (char *)&l);
    149 			db_read_bytes((db_addr_t)l.l_proc,
    150 			    sizeof(p), (char *)&p);
    151 			(*pr)("trace: pid %d ", p.p_pid);
    152 		} else {
    153 			proc_t	*pp;
    154 
    155 			(*pr)("trace: pid %d ", (int)addr);
    156 			if ((pp = db_proc_find((pid_t)addr)) == 0) {
    157 				(*pr)("not found\n");
    158 				return;
    159 			}
    160 			db_read_bytes((db_addr_t)pp, sizeof(p), (char *)&p);
    161 			addr = (db_addr_t)p.p_lwps.lh_first;
    162 			db_read_bytes(addr, sizeof(l), (char *)&l);
    163 		}
    164 		(*pr)("lid %d ", l.l_lid);
    165 		pcb = lwp_getpcb(&l);
    166 #ifdef _KERNEL
    167 		if (l.l_proc == curproc && (lwp_t *)addr == curlwp)
    168 			set_frame_callpc();
    169 		else
    170 #endif
    171 		{
    172 			db_read_bytes((db_addr_t)&pcb->pcb_bp,
    173 			    sizeof(frame), (char *)&frame);
    174 			db_read_bytes((db_addr_t)(frame + 1),
    175 			    sizeof(callpc), (char *)&callpc);
    176 			db_read_bytes((db_addr_t)frame,
    177 			    sizeof(frame), (char *)&frame);
    178 		}
    179 		(*pr)("at %p\n", frame);
    180 	} else if (have_addr) {
    181 		frame = (long *)addr;
    182 		db_read_bytes((db_addr_t)(frame + 1),
    183 		    sizeof(callpc), (char *)&callpc);
    184 		db_read_bytes((db_addr_t)frame,
    185 		    sizeof(frame), (char *)&frame);
    186 	} else {
    187 		set_frame_callpc();
    188 	}
    189 
    190 	retaddr = frame + 1;
    191 	arg0 = frame + 2;
    192 
    193 	lastframe = NULL;
    194 	while (count && frame != 0) {
    195 		int narg;
    196 		const char *name;
    197 		db_expr_t offset;
    198 		db_sym_t sym;
    199 		char *argnames[MAXNARG], **argnp = NULL;
    200 		db_addr_t lastcallpc;
    201 
    202 		name = "?";
    203 		is_trap = NONE;
    204 		offset = 0;
    205 		sym = db_frame_info(frame, callpc, &name, &offset, &is_trap,
    206 		    &narg);
    207 
    208 		if (lastframe == NULL && sym == DB_SYM_NULL && callpc != 0) {
    209 			/* Symbol not found, peek at code */
    210 			u_long instr = db_get_value(callpc, 4, false);
    211 
    212 			offset = 1;
    213 			if (
    214 #ifdef __x86_64__
    215 			   (instr == 0xe5894855 ||
    216 					/* enter: pushq %rbp, movq %rsp, %rbp */
    217 			    (instr & 0x00ffffff) == 0x0048e589
    218 					/* enter+1: movq %rsp, %rbp */)
    219 #else
    220 			   ((instr & 0x00ffffff) == 0x00e58955 ||
    221 					/* enter: pushl %ebp, movl %esp, %ebp */
    222 			    (instr & 0x0000ffff) == 0x0000e589
    223 					/* enter+1: movl %esp, %ebp */)
    224 #endif
    225 			    )
    226 			{
    227 				offset = 0;
    228 			}
    229 		}
    230 
    231 		if (is_trap == NONE) {
    232 			if (db_sym_numargs(sym, &narg, argnames))
    233 				argnp = argnames;
    234 			else
    235 				narg = db_numargs(frame);
    236 		}
    237 
    238 		(*pr)("%s(", name);
    239 
    240 		if (lastframe == NULL && offset == 0 && !have_addr) {
    241 			/*
    242 			 * We have a breakpoint before the frame is set up
    243 			 * Use %[er]sp instead
    244 			 */
    245 			argp = (long *)&((struct x86_frame *)
    246 			    (ddb_regs.tf_sp-sizeof(long)))->f_arg0;
    247 		} else {
    248 			argp = frame + 2;
    249 		}
    250 
    251 		while (narg) {
    252 			if (argnp)
    253 				(*pr)("%s=", *argnp++);
    254 			(*pr)("%lx", db_get_value((long)argp, sizeof(long),
    255 			    false));
    256 			argp++;
    257 			if (--narg != 0)
    258 				(*pr)(",");
    259 		}
    260 		(*pr)(") at ");
    261 		db_printsym(callpc, DB_STGY_PROC, pr);
    262 		(*pr)("\n");
    263 
    264 		if (lastframe == NULL && offset == 0 && !have_addr) {
    265 			/* Frame really belongs to next callpc */
    266 			struct x86_frame *fp = (void *)
    267 			    (ddb_regs.tf_sp-sizeof(long));
    268 			lastframe = (long *)fp;
    269 			callpc = (db_addr_t)
    270 			    db_get_value((db_addr_t)&fp->f_retaddr,
    271 				sizeof(long), false);
    272 
    273 			continue;
    274 		}
    275 
    276 		lastframe = frame;
    277 		lastcallpc = callpc;
    278 		if (!db_nextframe(&frame, &retaddr, &arg0, &callpc,
    279 		    frame + 2, is_trap, pr))
    280 			break;
    281 
    282 		if (INKERNEL((long)frame)) {
    283 			/* staying in kernel */
    284 #ifdef __i386__
    285 			if (!db_intrstack_p(frame) &&
    286 			    db_intrstack_p(lastframe)) {
    287 				(*pr)("--- switch to interrupt stack ---\n");
    288 			} else
    289 #endif
    290 			if (frame < lastframe ||
    291 			    (frame == lastframe && callpc == lastcallpc)) {
    292 				(*pr)("Bad frame pointer: %p\n", frame);
    293 				break;
    294 			}
    295 		} else if (INKERNEL((long)lastframe)) {
    296 			/* switch from user to kernel */
    297 			if (kernel_only)
    298 				break;	/* kernel stack only */
    299 		} else {
    300 			/* in user */
    301 			if (frame <= lastframe) {
    302 				(*pr)("Bad user frame pointer: %p\n", frame);
    303 				break;
    304 			}
    305 		}
    306 		--count;
    307 	}
    308 
    309 	if (count && is_trap != NONE) {
    310 		db_printsym(callpc, DB_STGY_XTRN, pr);
    311 		(*pr)(":\n");
    312 	}
    313 }
    314