xref: /netbsd-src/sys/arch/x86/x86/db_trace.c (revision 1d8f2e36c14dbbf48a8a6bb15b7ce3da97cef785)
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@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
db_x86_regop(const struct db_variable * vp,db_expr_t * val,int opcode)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
db_stack_trace_print(db_expr_t addr,bool have_addr,db_expr_t count,const char * modif,void (* pr)(const char *,...))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