xref: /openbsd-src/sys/arch/arm64/arm64/db_trace.c (revision 949c1c4ec8cc03255798b09f6078e1d0aed70a6a)
1 /*	$OpenBSD: db_trace.c,v 1.17 2024/11/07 16:02:29 miod Exp $	*/
2 /*	$NetBSD: db_trace.c,v 1.8 2003/01/17 22:28:48 thorpej Exp $	*/
3 
4 /*
5  * Copyright (c) 2000, 2001 Ben Harris
6  * Copyright (c) 1996 Scott K. Stevens
7  *
8  * Mach Operating System
9  * Copyright (c) 1991,1990 Carnegie Mellon University
10  * All Rights Reserved.
11  *
12  * Permission to use, copy, modify and distribute this software and its
13  * documentation is hereby granted, provided that both the copyright
14  * notice and this permission notice appear in all copies of the
15  * software, derivative works or modified versions, and any portions
16  * thereof, and that both notices appear in supporting documentation.
17  *
18  * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
19  * CONDITION.  CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
20  * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
21  *
22  * Carnegie Mellon requests users of this software to return to
23  *
24  *  Software Distribution Coordinator  or  Software.Distribution@CS.CMU.EDU
25  *  School of Computer Science
26  *  Carnegie Mellon University
27  *  Pittsburgh PA 15213-3890
28  *
29  * any improvements or extensions that they make and grant Carnegie the
30  * rights to redistribute these changes.
31  */
32 
33 #include <sys/param.h>
34 #include <sys/systm.h>
35 
36 #include <sys/proc.h>
37 #include <sys/stacktrace.h>
38 #include <sys/user.h>
39 #include <arm64/armreg.h>
40 #include <machine/db_machdep.h>
41 
42 #include <ddb/db_access.h>
43 #include <ddb/db_interface.h>
44 #include <ddb/db_variables.h>
45 #include <ddb/db_sym.h>
46 #include <ddb/db_output.h>
47 
48 db_regs_t ddb_regs;
49 
50 #define INKERNEL(va)	(((vaddr_t)(va)) & (1ULL << 63))
51 
52 void
53 db_stack_trace_print(db_expr_t addr, int have_addr, db_expr_t count,
54     char *modif, int (*pr)(const char *, ...))
55 {
56 	vaddr_t		frame, lastframe, lr, lastlr, sp;
57 	char		c, *cp = modif;
58 	db_expr_t	offset;
59 	Elf_Sym *	sym;
60 	const char	*name;
61 	int		kernel_only = 1;
62 	int		trace_thread = 0;
63 
64 	while ((c = *cp++) != 0) {
65 		if (c == 'u')
66 			kernel_only = 0;
67 		if (c == 't')
68 			trace_thread = 1;
69 	}
70 
71 	if (!have_addr) {
72 		sp = ddb_regs.tf_sp;
73 		lr = ddb_regs.tf_lr;
74 		lastlr = ddb_regs.tf_elr;
75 		frame = ddb_regs.tf_x[29];
76 	} else {
77 		if (trace_thread) {
78 			struct proc *p = tfind((pid_t)addr);
79 			if (p == NULL) {
80 				(*pr)("not found\n");
81 				return;
82 			}
83 			frame = p->p_addr->u_pcb.pcb_tf->tf_x[29];
84 			sp =  p->p_addr->u_pcb.pcb_tf->tf_sp;
85 			lr =  p->p_addr->u_pcb.pcb_tf->tf_lr;
86 			lastlr =  p->p_addr->u_pcb.pcb_tf->tf_elr;
87 		} else {
88 			sp = addr;
89 			db_read_bytes(sp, sizeof(vaddr_t),
90 			    (char *)&frame);
91 			db_read_bytes(sp + 8, sizeof(vaddr_t),
92 			    (char *)&lr);
93 			lastlr = 0;
94 		}
95 	}
96 
97 	while (count-- && frame != 0) {
98 		lastframe = frame;
99 
100 		if (INKERNEL(frame)) {
101 			sym = db_search_symbol(lastlr, DB_STGY_ANY, &offset);
102 			db_symbol_values(sym, &name, NULL);
103 		} else {
104 			sym = NULL;
105 			name = NULL;
106 		}
107 
108 		if (name == NULL || strcmp(name, "end") == 0) {
109 			(*pr)("%llx at 0x%lx", lastlr, lr - 4);
110 		} else {
111 			(*pr)("%s() at ", name);
112 			db_printsym(lr - 4, DB_STGY_PROC, pr);
113 		}
114 		(*pr)("\n");
115 
116 		if (name != NULL) {
117 			if ((strcmp (name, "handle_el0_irq") == 0) ||
118 			    (strcmp (name, "handle_el1_irq") == 0)) {
119 				(*pr)("--- interrupt ---\n");
120 			} else if (
121 			    (strcmp (name, "handle_el0_sync") == 0) ||
122 			    (strcmp (name, "handle_el1_sync") == 0)) {
123 				(*pr)("--- trap ---\n");
124 			}
125 		}
126 
127 		lastframe = frame;
128 		db_read_bytes(frame, sizeof(vaddr_t), (char *)&frame);
129 
130 		if (frame == 0) {
131 			/* end of chain */
132 			break;
133 		}
134 
135 		if (INKERNEL(frame)) {
136 			/* staying in kernel */
137 			if (frame <= lastframe) {
138 				(*pr)("Bad frame pointer: 0x%lx\n", frame);
139 				break;
140 			}
141 		} else if (INKERNEL(lastframe)) {
142 			/* switch from user to kernel */
143 			if (kernel_only) {
144 				(*pr)("end of kernel\n");
145 				break;	/* kernel stack only */
146 			}
147 		} else {
148 			/* in user */
149 			if (frame <= lastframe) {
150 				(*pr)("Bad user frame pointer: 0x%lx\n",
151 					frame);
152 				break;
153 			}
154 		}
155 
156 		lastlr = lr;
157 		db_read_bytes(frame + 8, sizeof(vaddr_t), (char *)&lr);
158 
159 		--count;
160 	}
161 }
162 
163 void
164 stacktrace_save_at(struct stacktrace *st, unsigned int skip)
165 {
166 	struct callframe *frame, *lastframe, *limit;
167 	struct proc *p = curproc;
168 
169 	st->st_count = 0;
170 
171 	if (p == NULL)
172 		return;
173 
174 	frame = __builtin_frame_address(0);
175 	KASSERT(INKERNEL(frame));
176 	limit = (struct callframe *)STACKALIGN(p->p_addr + USPACE -
177 	    sizeof(struct trapframe) - 0x10);
178 
179 	while (st->st_count < STACKTRACE_MAX) {
180 		if (skip == 0)
181 			st->st_pc[st->st_count++] = frame->f_lr;
182 		else
183 			skip--;
184 
185 		lastframe = frame;
186 		frame = frame->f_frame;
187 
188 		if (frame <= lastframe)
189 			break;
190 		if (frame >= limit)
191 			break;
192 		if (!INKERNEL(frame->f_lr))
193 			break;
194 	}
195 }
196 
197 void
198 stacktrace_save_utrace(struct stacktrace *st)
199 {
200 	st->st_count = 0;
201 }
202