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