xref: /openbsd-src/sys/arch/i386/i386/db_trace.c (revision 949c1c4ec8cc03255798b09f6078e1d0aed70a6a)
1 /*	$OpenBSD: db_trace.c,v 1.46 2024/11/07 16:02:29 miod Exp $	*/
2 /*	$NetBSD: db_trace.c,v 1.18 1996/05/03 19:42:01 christos Exp $	*/
3 
4 /*
5  * Mach Operating System
6  * Copyright (c) 1991,1990 Carnegie Mellon University
7  * All Rights Reserved.
8  *
9  * Permission to use, copy, modify and distribute this software and its
10  * documentation is hereby granted, provided that both the copyright
11  * notice and this permission notice appear in all copies of the
12  * software, derivative works or modified versions, and any portions
13  * thereof, and that both notices appear in supporting documentation.
14  *
15  * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
16  * CONDITION.  CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
17  * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
18  *
19  * Carnegie Mellon requests users of this software to return to
20  *
21  *  Software Distribution Coordinator  or  Software.Distribution@CS.CMU.EDU
22  *  School of Computer Science
23  *  Carnegie Mellon University
24  *  Pittsburgh PA 15213-3890
25  *
26  * any improvements or extensions that they make and grant Carnegie Mellon
27  * the rights to redistribute these changes.
28  */
29 
30 #include <sys/param.h>
31 #include <sys/systm.h>
32 #include <sys/proc.h>
33 #include <sys/stacktrace.h>
34 #include <sys/user.h>
35 
36 #include <machine/db_machdep.h>
37 
38 #include <ddb/db_sym.h>
39 #include <ddb/db_access.h>
40 #include <ddb/db_variables.h>
41 #include <ddb/db_interface.h>
42 
43 /*
44  * Machine register set.
45  */
46 struct db_variable db_regs[] = {
47 	{ "ds",		(long *)&ddb_regs.tf_ds,     FCN_NULL },
48 	{ "es",		(long *)&ddb_regs.tf_es,     FCN_NULL },
49 	{ "fs",		(long *)&ddb_regs.tf_fs,     FCN_NULL },
50 	{ "gs",		(long *)&ddb_regs.tf_gs,     FCN_NULL },
51 	{ "edi",	(long *)&ddb_regs.tf_edi,    FCN_NULL },
52 	{ "esi",	(long *)&ddb_regs.tf_esi,    FCN_NULL },
53 	{ "ebp",	(long *)&ddb_regs.tf_ebp,    FCN_NULL },
54 	{ "ebx",	(long *)&ddb_regs.tf_ebx,    FCN_NULL },
55 	{ "edx",	(long *)&ddb_regs.tf_edx,    FCN_NULL },
56 	{ "ecx",	(long *)&ddb_regs.tf_ecx,    FCN_NULL },
57 	{ "eax",	(long *)&ddb_regs.tf_eax,    FCN_NULL },
58 	{ "eip",	(long *)&ddb_regs.tf_eip,    FCN_NULL },
59 	{ "cs",		(long *)&ddb_regs.tf_cs,     FCN_NULL },
60 	{ "eflags",	(long *)&ddb_regs.tf_eflags, FCN_NULL },
61 	{ "esp",	(long *)&ddb_regs.tf_esp,    FCN_NULL },
62 	{ "ss",		(long *)&ddb_regs.tf_ss,     FCN_NULL },
63 };
64 struct db_variable *db_eregs = db_regs + nitems(db_regs);
65 
66 /*
67  * Stack trace.
68  */
69 #define	INKERNEL(va)	(((vaddr_t)(va)) >= VM_MIN_KERNEL_ADDRESS)
70 
71 int db_i386_numargs(struct callframe *);
72 
73 /*
74  * Figure out how many arguments were passed into the frame at "fp".
75  */
76 int
77 db_i386_numargs(struct callframe *fp)
78 {
79 	int	*argp;
80 	int	inst;
81 	int	args;
82 	extern char	etext[];
83 
84 	argp = (int *)db_get_value((int)&fp->f_retaddr, 4, 0);
85 	if (argp < (int *)VM_MIN_KERNEL_ADDRESS || argp > (int *)etext) {
86 		args = 5;
87 	} else {
88 		inst = db_get_value((int)argp, 4, 0);
89 		if ((inst & 0xff) == 0x59)	/* popl %ecx */
90 			args = 1;
91 		else if ((inst & 0xffff) == 0xc483)	/* addl %n, %esp */
92 			args = ((inst >> 16) & 0xff) / 4;
93 		else
94 			args = 5;
95 	}
96 	return args;
97 }
98 
99 void
100 db_stack_trace_print(db_expr_t addr, int have_addr, db_expr_t count,
101     char *modif, int (*pr)(const char *, ...))
102 {
103 	struct callframe *frame, *lastframe;
104 	int		*argp, *arg0;
105 	vaddr_t		callpc;
106 	unsigned int	cr4save = CR4_SMEP|CR4_SMAP;
107 	int		kernel_only = 1;
108 	int		trace_proc = 0;
109 	struct proc	*p;
110 
111 	{
112 		char *cp = modif;
113 		char c;
114 
115 		while ((c = *cp++) != 0) {
116 			if (c == 't')
117 				trace_proc = 1;
118 			if (c == 'u')
119 				kernel_only = 0;
120 		}
121 	}
122 
123 	if (count == -1)
124 		count = 65535;
125 
126 	if (trace_proc) {
127 		p = tfind((pid_t)addr);
128 		if (p == NULL) {
129 			(*pr) ("not found\n");
130 			return;
131 		}
132 	}
133 
134 	if (curcpu()->ci_feature_sefflags_ebx & SEFF0EBX_SMAP) {
135 		cr4save = rcr4();
136 		if (cr4save & CR4_SMAP)
137 			lcr4(cr4save & ~CR4_SMAP);
138 	} else {
139 		cr4save = 0;
140 	}
141 
142 	if (!have_addr) {
143 		frame = (struct callframe *)ddb_regs.tf_ebp;
144 		callpc = (vaddr_t)ddb_regs.tf_eip;
145 	} else if (trace_proc) {
146 		frame = (struct callframe *)p->p_addr->u_pcb.pcb_ebp;
147 		callpc = (vaddr_t)
148 		    db_get_value((int)&frame->f_retaddr, 4, 0);
149 	} else {
150 		frame = (struct callframe *)addr;
151 		callpc = (vaddr_t)
152 		    db_get_value((int)&frame->f_retaddr, 4, 0);
153 	}
154 
155 	lastframe = 0;
156 	while (count && frame != 0) {
157 		int		narg;
158 		const char *	name;
159 		db_expr_t	offset;
160 		Elf_Sym		*sym;
161 
162 		if (INKERNEL(frame)) {
163 			sym = db_search_symbol(callpc, DB_STGY_ANY, &offset);
164 			db_symbol_values(sym, &name, NULL);
165 		} else {
166 			sym = NULL;
167 			name = NULL;
168 		}
169 
170 		if (lastframe == 0 && sym == NULL) {
171 			/* Symbol not found, peek at code */
172 			int	instr = db_get_value(callpc, 4, 0);
173 
174 			offset = 1;
175 			if ((instr & 0x00ffffff) == 0x00e58955 ||
176 					/* enter: pushl %ebp, movl %esp, %ebp */
177 			    (instr & 0x0000ffff) == 0x0000e589
178 					/* enter+1: movl %esp, %ebp */) {
179 				offset = 0;
180 			}
181 		}
182 
183 		narg = db_ctf_func_numargs(sym);
184 		if (narg < 0)
185 			narg = db_i386_numargs(frame);
186 
187 		if (name == NULL)
188 			(*pr)("%lx(", callpc);
189 		else
190 			(*pr)("%s(", name);
191 
192 		if (lastframe == 0 && offset == 0 && !have_addr) {
193 			/*
194 			 * We have a breakpoint before the frame is set up
195 			 * Use %esp instead
196 			 */
197 			arg0 =
198 			    &((struct callframe *)(ddb_regs.tf_esp-4))->f_arg0;
199 		} else {
200 			arg0 = &frame->f_arg0;
201 		}
202 
203 		for (argp = arg0; narg > 0; ) {
204 			(*pr)("%x", db_get_value((int)argp, 4, 0));
205 			argp++;
206 			if (--narg != 0)
207 				(*pr)(",");
208 		}
209 		(*pr)(") at ");
210 		db_printsym(callpc, DB_STGY_PROC, pr);
211 		(*pr)("\n");
212 
213 		if (lastframe == 0 && offset == 0 && !have_addr) {
214 			/* Frame really belongs to next callpc */
215 			lastframe = (struct callframe *)(ddb_regs.tf_esp-4);
216 			callpc = (vaddr_t)
217 				 db_get_value((int)&lastframe->f_retaddr, 4, 0);
218 			continue;
219 		}
220 
221 		lastframe = frame;
222 		callpc = db_get_value((int)&frame->f_retaddr, 4, 0);
223 		frame = (void *)db_get_value((int)&frame->f_frame, 4, 0);
224 
225 		if (frame == 0) {
226 			/* end of chain */
227 			break;
228 		}
229 		if (INKERNEL(frame)) {
230 			/* staying in kernel */
231 			if (frame <= lastframe) {
232 				(*pr)("Bad frame pointer: %p\n", frame);
233 				break;
234 			}
235 		} else if (INKERNEL(lastframe)) {
236 			/* switch from user to kernel */
237 			if (kernel_only) {
238 				(*pr)("end of kernel\n");
239 				break;	/* kernel stack only */
240 			}
241 		} else {
242 			/* in user */
243 			if (frame <= lastframe) {
244 				(*pr)("Bad user frame pointer: %p\n",
245 					  frame);
246 				break;
247 			}
248 		}
249 		--count;
250 	}
251 
252 	if (cr4save & CR4_SMAP)
253 		lcr4(cr4save);
254 }
255 
256 void
257 stacktrace_save_at(struct stacktrace *st, unsigned int skip)
258 {
259 	struct callframe *frame, *lastframe, *limit;
260 	struct pcb *pcb = curpcb;
261 
262 	st->st_count = 0;
263 
264 	if (pcb == NULL)
265 		return;
266 
267 	frame = __builtin_frame_address(0);
268 	KASSERT(INKERNEL(frame));
269 	limit = (struct callframe *)((struct trapframe *)pcb->pcb_kstack - 1);
270 
271 	while (st->st_count < STACKTRACE_MAX) {
272 		if (skip == 0)
273 			st->st_pc[st->st_count++] = frame->f_retaddr;
274 		else
275 			skip--;
276 
277 		lastframe = frame;
278 		frame = frame->f_frame;
279 
280 		if (frame <= lastframe)
281 			break;
282 		if (frame >= limit)
283 			break;
284 		if (!INKERNEL(frame->f_retaddr))
285 			break;
286 	}
287 }
288 
289 void
290 stacktrace_save_utrace(struct stacktrace *st)
291 {
292 	struct callframe f, *frame, *lastframe;
293 	struct pcb *pcb = curpcb;
294 
295 	st->st_count = 0;
296 
297 	if (pcb == NULL)
298 		return;
299 
300 	frame = __builtin_frame_address(0);
301 	KASSERT(INKERNEL(frame));
302 	f = *frame;
303 
304 	while (st->st_count < STACKTRACE_MAX) {
305 		if (f.f_retaddr != 0 && !INKERNEL(f.f_retaddr))
306 			st->st_pc[st->st_count++] = f.f_retaddr;
307 
308 		lastframe = frame;
309 		frame = f.f_frame;
310 
311 		if (frame == NULL)
312 			break;
313 		if (INKERNEL(f.f_retaddr)) {
314 			if (frame <= lastframe)
315 				break;
316 			f = *frame;
317 			continue;
318 		}
319 		if (!INKERNEL(lastframe) && frame <= lastframe)
320 			break;
321 		if (copyin(frame, &f, sizeof(f)) != 0)
322 			break;
323 	}
324 }
325 
326 vaddr_t
327 db_get_pc(struct trapframe *tf)
328 {
329 	struct callframe *cf;
330 
331 	if (KERNELMODE(tf->tf_cs, tf->tf_eflags))
332 		cf = (struct callframe *)((long)&tf->tf_esp - sizeof(long));
333 	else
334 		cf = (struct callframe *)(tf->tf_esp - sizeof(long));
335 
336 	return db_get_value((vaddr_t)&cf->f_retaddr, sizeof(long), 0);
337 }
338 
339 vaddr_t
340 db_get_probe_addr(struct trapframe *tf)
341 {
342 	return tf->tf_eip - BKPT_SIZE;
343 }
344