1/* $NetBSD: db_trace.c,v 1.3 2011/04/21 00:24:07 enami 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.3 2011/04/21 00:24:07 enami 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
55int
56db_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
80db_addr_t db_trap_symbol_value = 0;
81db_addr_t db_syscall_symbol_value = 0;
82db_addr_t db_kdintr_symbol_value = 0;
83bool db_trace_symbols_found = false;
84
85void db_find_trace_symbols(void);
86
87void
88db_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
102void
103db_stack_trace_print(db_expr_t addr, bool have_addr, db_expr_t count,
104 const char *modif, void (*pr)(const char *, ...))
105{
106 long *frame, *lastframe;
107 long *retaddr, *arg0;
108 long *argp;
109 db_addr_t callpc;
110 int is_trap;
111 bool kernel_only = true;
112 bool trace_thread = false;
113 bool lwpaddr = false;
114
115#if 0
116 if (!db_trace_symbols_found)
117 db_find_trace_symbols();
118#endif
119
120 {
121 const char *cp = modif;
122 char c;
123
124 while ((c = *cp++) != 0) {
125 if (c == 'a') {
126 lwpaddr = true;
127 trace_thread = true;
128 }
129 if (c == 't')
130 trace_thread = true;
131 if (c == 'u')
132 kernel_only = false;
133 }
134 }
135
136#define set_frame_callpc() do { \
137 frame = (long *)ddb_regs.tf_bp; \
138 callpc = (db_addr_t)ddb_regs.tf_ip; \
139 } while (/*CONSTCCOND*/0)
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 retaddr = frame + 1;
189 arg0 = frame + 2;
190
191 lastframe = 0;
192 while (count && frame != 0) {
193 int narg;
194 const char * name;
195 db_expr_t offset;
196 db_sym_t sym;
197 char *argnames[MAXNARG], **argnp = NULL;
198 db_addr_t lastcallpc;
199
200 name = "?";
201 is_trap = NONE;
202 offset = 0;
203 sym = db_frame_info(frame, callpc, &name, &offset, &is_trap,
204 &narg);
205
206 if (lastframe == 0 && sym == (db_sym_t)0 && callpc != 0) {
207 /* Symbol not found, peek at code */
208 u_long instr = db_get_value(callpc, 4, false);
209
210 offset = 1;
211 if (
212#ifdef __x86_64__
213 instr == 0xe5894855 ||
214 /* enter: pushq %rbp, movq %rsp, %rbp */
215 (instr & 0x00ffffff) == 0x0048e589
216 /* enter+1: movq %rsp, %rbp */)
217#else
218 (instr & 0x00ffffff) == 0x00e58955 ||
219 /* enter: pushl %ebp, movl %esp, %ebp */
220 (instr & 0x0000ffff) == 0x0000e589
221 /* enter+1: movl %esp, %ebp */)
222#endif
223 {
224 offset = 0;
225 }
226 }
227
228 if (is_trap == NONE) {
229 if (db_sym_numargs(sym, &narg, argnames))
230 argnp = argnames;
231 else
232 narg = db_numargs(frame);
233 }
234
235 (*pr)("%s(", name);
236
237 if (lastframe == 0 && offset == 0 && !have_addr) {
238 /*
239 * We have a breakpoint before the frame is set up
240 * Use %[er]sp instead
241 */
242 argp = (long *)&((struct x86_frame *)
243 (ddb_regs.tf_sp-sizeof(long)))->f_arg0;
244 } else {
245 argp = frame + 2;
246 }
247
248 while (narg) {
249 if (argnp)
250 (*pr)("%s=", *argnp++);
251 (*pr)("%lx", db_get_value((long)argp, sizeof(long),
252 false));
253 argp++;
254 if (--narg != 0)
255 (*pr)(",");
256 }
257 (*pr)(") at ");
258 db_printsym(callpc, DB_STGY_PROC, pr);
259 (*pr)("\n");
260
261 if (lastframe == 0 && offset == 0 && !have_addr) {
262 /* Frame really belongs to next callpc */
263 struct x86_frame *fp = (void *)
264 (ddb_regs.tf_sp-sizeof(long));
265 lastframe = (long *)fp;
266 callpc = (db_addr_t)
267 db_get_value((db_addr_t)&fp->f_retaddr,
268 sizeof(long), false);
269
270 continue;
271 }
272
273 lastframe = frame;
274 lastcallpc = callpc;
275 if (!db_nextframe(&frame, &retaddr, &arg0,
276 &callpc, frame + 2, is_trap, pr))
277 break;
278
279 if (INKERNEL((long)frame)) {
280 /* staying in kernel */
281#ifdef __i386__
282 if (!db_intrstack_p(frame)
283 && db_intrstack_p(lastframe)) {
284 (*pr)("--- switch to interrupt stack ---\n");
285 } else
286#endif
287 if (frame < lastframe ||
288 (frame == lastframe && callpc == lastcallpc)) {
289 (*pr)("Bad frame pointer: %p\n", frame);
290 break;
291 }
292 } else if (INKERNEL((long)lastframe)) {
293 /* switch from user to kernel */
294 if (kernel_only)
295 break; /* kernel stack only */
296 } else {
297 /* in user */
298 if (frame <= lastframe) {
299 (*pr)("Bad user frame pointer: %p\n", frame);
300 break;
301 }
302 }
303 --count;
304 }
305
306 if (count && is_trap != NONE) {
307 db_printsym(callpc, DB_STGY_XTRN, pr);
308 (*pr)(":\n");
309 }
310#undef set_frame_callpc
311}
312