xref: /openbsd-src/sys/arch/amd64/amd64/db_trace.c (revision 949c1c4ec8cc03255798b09f6078e1d0aed70a6a)
1 /*	$OpenBSD: db_trace.c,v 1.56 2024/11/07 16:02:29 miod Exp $	*/
2 /*	$NetBSD: db_trace.c,v 1.1 2003/04/26 18:39:27 fvdl 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 the
27  * 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 #include <machine/frame.h>
38 #include <machine/trap.h>
39 
40 #include <ddb/db_sym.h>
41 #include <ddb/db_access.h>
42 #include <ddb/db_variables.h>
43 #include <ddb/db_output.h>
44 
45 /*
46  * Machine register set.
47  */
48 struct db_variable db_regs[] = {
49 	{ "rdi",	(long *)&ddb_regs.tf_rdi,    FCN_NULL },
50 	{ "rsi",	(long *)&ddb_regs.tf_rsi,    FCN_NULL },
51 	{ "rbp",	(long *)&ddb_regs.tf_rbp,    FCN_NULL },
52 	{ "rbx",	(long *)&ddb_regs.tf_rbx,    FCN_NULL },
53 	{ "rdx",	(long *)&ddb_regs.tf_rdx,    FCN_NULL },
54 	{ "rcx",	(long *)&ddb_regs.tf_rcx,    FCN_NULL },
55 	{ "rax",	(long *)&ddb_regs.tf_rax,    FCN_NULL },
56 	{ "r8",		(long *)&ddb_regs.tf_r8,     FCN_NULL },
57 	{ "r9",		(long *)&ddb_regs.tf_r9,     FCN_NULL },
58 	{ "r10",	(long *)&ddb_regs.tf_r10,    FCN_NULL },
59 	{ "r11",	(long *)&ddb_regs.tf_r11,    FCN_NULL },
60 	{ "r12",	(long *)&ddb_regs.tf_r12,    FCN_NULL },
61 	{ "r13",	(long *)&ddb_regs.tf_r13,    FCN_NULL },
62 	{ "r14",	(long *)&ddb_regs.tf_r14,    FCN_NULL },
63 	{ "r15",	(long *)&ddb_regs.tf_r15,    FCN_NULL },
64 	{ "rip",	(long *)&ddb_regs.tf_rip,    FCN_NULL },
65 	{ "cs",		(long *)&ddb_regs.tf_cs,     FCN_NULL },
66 	{ "rflags",	(long *)&ddb_regs.tf_rflags, FCN_NULL },
67 	{ "rsp",	(long *)&ddb_regs.tf_rsp,    FCN_NULL },
68 	{ "ss",		(long *)&ddb_regs.tf_ss,     FCN_NULL },
69 };
70 struct db_variable * db_eregs = db_regs + nitems(db_regs);
71 
72 /*
73  * Stack trace.
74  */
75 #define	INKERNEL(va)	(((vaddr_t)(va)) >= VM_MIN_KERNEL_ADDRESS)
76 
77 
78 const unsigned long *db_reg_args[6] = {
79 	(unsigned long *)&ddb_regs.tf_rdi,
80 	(unsigned long *)&ddb_regs.tf_rsi,
81 	(unsigned long *)&ddb_regs.tf_rdx,
82 	(unsigned long *)&ddb_regs.tf_rcx,
83 	(unsigned long *)&ddb_regs.tf_r8,
84 	(unsigned long *)&ddb_regs.tf_r9,
85 };
86 
87 void
88 db_stack_trace_print(db_expr_t addr, int have_addr, db_expr_t count,
89     char *modif, int (*pr)(const char *, ...))
90 {
91 	struct callframe *frame, *lastframe;
92 	unsigned long	*argp, *arg0;
93 	vaddr_t		callpc;
94 	unsigned int	cr4save = CR4_SMEP|CR4_SMAP;
95 	int		kernel_only = 1;
96 	int		trace_proc = 0;
97 	struct proc	*p;
98 
99 	{
100 		char *cp = modif;
101 		char c;
102 
103 		while ((c = *cp++) != 0) {
104 			if (c == 't')
105 				trace_proc = 1;
106 			if (c == 'u')
107 				kernel_only = 0;
108 		}
109 	}
110 
111 	if (trace_proc) {
112 		p = tfind((pid_t)addr);
113 		if (p == NULL) {
114 			(*pr) ("not found\n");
115 			return;
116 		}
117 	}
118 
119 	cr4save = rcr4();
120 	if (cr4save & CR4_SMAP)
121 		lcr4(cr4save & ~CR4_SMAP);
122 
123 	if (!have_addr) {
124 		frame = (struct callframe *)ddb_regs.tf_rbp;
125 		callpc = (vaddr_t)ddb_regs.tf_rip;
126 	} else if (trace_proc) {
127 		frame = (struct callframe *)p->p_addr->u_pcb.pcb_rbp;
128 		callpc = (vaddr_t)
129 		    db_get_value((vaddr_t)&frame->f_retaddr, 8, 0);
130 		frame = (struct callframe *)frame->f_frame;
131 	} else {
132 		frame = (struct callframe *)addr;
133 		callpc = (vaddr_t)
134 		    db_get_value((vaddr_t)&frame->f_retaddr, 8, 0);
135 		frame = (struct callframe *)frame->f_frame;
136 	}
137 
138 	lastframe = 0;
139 	while (count && frame != 0) {
140 		int		narg;
141 		unsigned int	i;
142 		const char *	name;
143 		db_expr_t	offset;
144 		Elf_Sym *	sym;
145 
146 		if (INKERNEL(frame)) {
147 			sym = db_search_symbol(callpc, DB_STGY_ANY, &offset);
148 			db_symbol_values(sym, &name, NULL);
149 		} else {
150 			sym = NULL;
151 			name = NULL;
152 		}
153 
154 		if (lastframe == 0 && sym == NULL && callpc != 0) {
155 			/* Symbol not found, peek at code */
156 			unsigned long instr = db_get_value(callpc, 8, 0);
157 
158 			offset = 1;
159 			if (instr == 0xe5894855 ||
160 					/* enter: pushq %rbp, movq %rsp, %rbp */
161 			    (instr & 0x00ffffff) == 0x00e58948
162 					/* enter+1: movq %rsp, %rbp */) {
163 				offset = 0;
164 			}
165 		}
166 
167 		if ((narg = db_ctf_func_numargs(sym)) < 0)
168 			narg = 6;
169 
170 		if (name == NULL)
171 			(*pr)("%lx(", callpc);
172 		else
173 			(*pr)("%s(", name);
174 
175 		if (lastframe == 0 && offset == 0 && !have_addr) {
176 			/* We have a breakpoint before the frame is set up */
177 			for (i = 0; i < narg; i++) {
178 				(*pr)("%lx", *db_reg_args[i]);
179 				if (--narg != 0)
180 					(*pr)(",");
181 			}
182 
183 			/* Use %rsp instead */
184 			arg0 =
185 			    &((struct callframe *)(ddb_regs.tf_rsp-8))->f_arg0;
186 		} else {
187 			argp = (unsigned long *)frame;
188 			for (i = narg; i > 0; i--) {
189 				argp--;
190 				(*pr)("%lx", db_get_value((vaddr_t)argp,
191 				    sizeof(*argp), 0));
192 				if (--narg != 0)
193 					(*pr)(",");
194 			}
195 
196 			arg0 = &frame->f_arg0;
197 		}
198 
199 		for (argp = arg0; narg > 0; ) {
200 			(*pr)("%lx", db_get_value((vaddr_t)argp,
201 			    sizeof(*argp), 0));
202 			argp++;
203 			if (--narg != 0)
204 				(*pr)(",");
205 		}
206 		(*pr)(") at ");
207 		db_printsym(callpc, DB_STGY_PROC, pr);
208 		(*pr)("\n");
209 
210 		if (lastframe == 0 && offset == 0 && !have_addr) {
211 			/* Frame really belongs to next callpc */
212 			lastframe = (struct callframe *)(ddb_regs.tf_rsp-8);
213 			callpc = (vaddr_t)
214 				 db_get_value((vaddr_t)&lastframe->f_retaddr,
215 				    8, 0);
216 			continue;
217 		}
218 
219 		lastframe = frame;
220 		callpc = (vaddr_t)db_get_value(
221 		    (vaddr_t)&frame->f_retaddr, 8, 0);
222 		frame = (struct callframe *)db_get_value(
223 		    (vaddr_t)&frame->f_frame, 8, 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 	(*pr)("end trace frame: 0x%lx, count: %d\n", frame, count);
252 
253 	if (cr4save & CR4_SMAP)
254 		lcr4(cr4save);
255 }
256 
257 void
258 stacktrace_save_at(struct stacktrace *st, unsigned int skip)
259 {
260 	struct callframe *frame, *lastframe, *limit;
261 	struct pcb *pcb = curpcb;
262 
263 	st->st_count = 0;
264 
265 	if (pcb == NULL)
266 		return;
267 
268 	frame = __builtin_frame_address(0);
269 	KASSERT(INKERNEL(frame));
270 	limit = (struct callframe *)((struct trapframe *)pcb->pcb_kstack - 1);
271 
272 	while (st->st_count < STACKTRACE_MAX) {
273 		if (skip == 0)
274 			st->st_pc[st->st_count++] = frame->f_retaddr;
275 		else
276 			skip--;
277 
278 		lastframe = frame;
279 		frame = frame->f_frame;
280 
281 		if (frame <= lastframe)
282 			break;
283 		if (frame >= limit)
284 			break;
285 		if (!INKERNEL(frame->f_retaddr))
286 			break;
287 	}
288 }
289 
290 void
291 stacktrace_save_utrace(struct stacktrace *st)
292 {
293 	struct callframe f, *frame, *lastframe;
294 	struct pcb *pcb = curpcb;
295 
296 	st->st_count = 0;
297 
298 	if (pcb == NULL)
299 		return;
300 
301 	frame = __builtin_frame_address(0);
302 	KASSERT(INKERNEL(frame));
303 	f = *frame;
304 
305 	while (st->st_count < STACKTRACE_MAX) {
306 		if (f.f_retaddr != 0 && !INKERNEL(f.f_retaddr))
307 			st->st_pc[st->st_count++] = f.f_retaddr;
308 
309 		lastframe = frame;
310 		frame = f.f_frame;
311 
312 		if (frame == NULL)
313 			break;
314 		if (INKERNEL(f.f_retaddr)) {
315 			if (frame <= lastframe)
316 				break;
317 			f = *frame;
318 			continue;
319 		}
320 		if (!INKERNEL(lastframe) && frame <= lastframe)
321 			break;
322 		if (copyin(frame, &f, sizeof(f)) != 0)
323 			break;
324 	}
325 }
326 
327 vaddr_t
328 db_get_pc(struct trapframe *tf)
329 {
330 	struct callframe *cf = (struct callframe *)(tf->tf_rsp - sizeof(long));
331 
332 	return db_get_value((vaddr_t)&cf->f_retaddr, sizeof(long), 0);
333 }
334 
335 vaddr_t
336 db_get_probe_addr(struct trapframe *tf)
337 {
338 	return tf->tf_rip - BKPT_SIZE;
339 }
340