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 | |
55 | int |
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 | void |
103 | db_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 | |