FreeBSD/Linux Kernel Cross Reference
sys/i386/db_trace.c
1 /*
2 * Mach Operating System
3 * Copyright (c) 1991,1990 Carnegie Mellon University
4 * All Rights Reserved.
5 *
6 * Permission to use, copy, modify and distribute this software and its
7 * documentation is hereby granted, provided that both the copyright
8 * notice and this permission notice appear in all copies of the
9 * software, derivative works or modified versions, and any portions
10 * thereof, and that both notices appear in supporting documentation.
11 *
12 * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
13 * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
14 * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
15 *
16 * Carnegie Mellon requests users of this software to return to
17 *
18 * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
19 * School of Computer Science
20 * Carnegie Mellon University
21 * Pittsburgh PA 15213-3890
22 *
23 * any improvements or extensions that they make and grant Carnegie Mellon
24 * the rights to redistribute these changes.
25 */
26 /*
27 * HISTORY
28 * $Log: db_trace.c,v $
29 * Revision 2.13 93/05/17 17:13:23 rvb
30 * machparam.h -> machspl.h
31 * Cleanup's to make gcc generate less warnings.
32 *
33 * Revision 2.12 93/01/14 17:28:48 danner
34 * Proper spl typing. db_symbol_values() changed.
35 * [92/11/30 af]
36 *
37 * Revision 2.11 92/03/10 16:25:36 jsb
38 * Changed db_numargs to use patchable variable db_numargs_default
39 * when it needs to guess the number of arguments, instead of just
40 * using the embedded constant '5'.
41 * [92/03/10 14:21:55 jsb]
42 *
43 * Revision 2.10 91/11/12 11:50:39 rvb
44 * Removed redundant printing of the continuation.
45 * Fixed the log.
46 * [91/11/07 rpd]
47 *
48 * Revision 2.9 91/10/09 16:06:49 af
49 * Supported stack trace and register access of non current thread.
50 * Supported user register access.
51 * Fixed incorrect next frame check.
52 * [91/08/29 tak]
53 *
54 * Fixed stack tracing for threads without kernel stacks. Yes, again.
55 * [91/09/26 rpd]
56 *
57 * Revision 2.8 91/08/28 11:11:34 jsb
58 * In stack traces, print file/line info if available.
59 * [91/08/13 18:15:57 jsb]
60 *
61 * Revision 2.7 91/05/14 16:06:12 mrt
62 * Correcting copyright
63 *
64 * Revision 2.6 91/02/05 17:11:21 mrt
65 * Changed to new Mach copyright
66 * [91/02/01 17:31:32 mrt]
67 *
68 * Revision 2.5 91/01/09 19:55:27 rpd
69 * Fixed stack tracing for threads without kernel stacks.
70 * [91/01/09 rpd]
71 *
72 * Revision 2.4 91/01/08 15:10:22 rpd
73 * Reorganized the pcb.
74 * [90/12/11 rpd]
75 *
76 * Revision 2.3 90/11/05 14:27:07 rpd
77 * If we can not guess the number of args to a function, use 5 vs 0.
78 * [90/11/02 rvb]
79 *
80 * Revision 2.2 90/08/27 21:56:20 dbg
81 * Import db_sym.h.
82 * [90/08/21 dbg]
83 * Fix includes.
84 * [90/08/08 dbg]
85 * Created from rvb's code for new debugger.
86 * [90/07/11 dbg]
87 *
88 */
89
90 #include <mach/boolean.h>
91 #include <vm/vm_map.h>
92 #include <kern/thread.h>
93 #include <kern/task.h>
94
95 #include <machine/db_machdep.h>
96 #include <machine/machspl.h>
97
98 #include <ddb/db_access.h>
99 #include <ddb/db_sym.h>
100 #include <ddb/db_variables.h>
101 #include <ddb/db_task_thread.h>
102
103 int db_i386_reg_value();
104 int db_i386_kreg_value();
105
106 struct i386_kernel_state ddb_null_kregs;
107
108 /*
109 * Machine register set.
110 */
111 struct db_variable db_regs[] = {
112 { "cs", (int *)&ddb_regs.cs, db_i386_reg_value },
113 { "ds", (int *)&ddb_regs.ds, db_i386_reg_value },
114 { "es", (int *)&ddb_regs.es, db_i386_reg_value },
115 { "fs", (int *)&ddb_regs.fs, db_i386_reg_value },
116 { "gs", (int *)&ddb_regs.gs, db_i386_reg_value },
117 { "ss", (int *)&ddb_regs.ss, db_i386_reg_value },
118 { "eax",(int *)&ddb_regs.eax, db_i386_reg_value },
119 { "ecx",(int *)&ddb_regs.ecx, db_i386_reg_value },
120 { "edx",(int *)&ddb_regs.edx, db_i386_reg_value },
121 { "ebx",(int *)&ddb_regs.ebx, db_i386_reg_value },
122 { "esp",(int *)&ddb_regs.uesp,db_i386_reg_value },
123 { "ebp",(int *)&ddb_regs.ebp, db_i386_reg_value },
124 { "esi",(int *)&ddb_regs.esi, db_i386_reg_value },
125 { "edi",(int *)&ddb_regs.edi, db_i386_reg_value },
126 { "eip",(int *)&ddb_regs.eip, db_i386_reg_value },
127 { "efl",(int *)&ddb_regs.efl, db_i386_reg_value },
128 };
129 struct db_variable *db_eregs = db_regs + sizeof(db_regs)/sizeof(db_regs[0]);
130
131 /*
132 * Stack trace.
133 */
134 #define INKERNEL(va) (((vm_offset_t)(va)) >= VM_MIN_KERNEL_ADDRESS)
135
136 struct i386_frame {
137 struct i386_frame *f_frame;
138 int f_retaddr;
139 int f_arg0;
140 };
141
142 #define TRAP 1
143 #define INTERRUPT 2
144 #define SYSCALL 3
145
146 db_addr_t db_user_trap_symbol_value = 0;
147 db_addr_t db_kernel_trap_symbol_value = 0;
148 db_addr_t db_interrupt_symbol_value = 0;
149 db_addr_t db_return_to_iret_symbol_value = 0;
150 db_addr_t db_syscall_symbol_value = 0;
151 boolean_t db_trace_symbols_found = FALSE;
152
153 struct i386_kregs {
154 char *name;
155 int offset;
156 } i386_kregs[] = {
157 { "ebx", (int)(&((struct i386_kernel_state *)0)->k_ebx) },
158 { "esp", (int)(&((struct i386_kernel_state *)0)->k_esp) },
159 { "ebp", (int)(&((struct i386_kernel_state *)0)->k_ebp) },
160 { "edi", (int)(&((struct i386_kernel_state *)0)->k_edi) },
161 { "esi", (int)(&((struct i386_kernel_state *)0)->k_esi) },
162 { "eip", (int)(&((struct i386_kernel_state *)0)->k_eip) },
163 { 0 },
164 };
165
166 int *
167 db_lookup_i386_kreg(name, kregp)
168 char *name;
169 int *kregp;
170 {
171 register struct i386_kregs *kp;
172
173 for (kp = i386_kregs; kp->name; kp++) {
174 if (strcmp(name, kp->name) == 0)
175 return((int *)((int)kregp + kp->offset));
176 }
177 return(0);
178 }
179
180 int
181 db_i386_reg_value(vp, valuep, flag, ap)
182 struct db_variable *vp;
183 db_expr_t *valuep;
184 int flag;
185 db_var_aux_param_t ap;
186 {
187 int *dp = 0;
188 db_expr_t null_reg = 0;
189 register thread_t thread = ap->thread;
190 extern unsigned int_stack_high;
191
192 if (db_option(ap->modif, 'u')) {
193 if (thread == THREAD_NULL) {
194 if ((thread = current_thread()) == THREAD_NULL)
195 db_error("no user registers\n");
196 }
197 if (thread == current_thread()) {
198 if (ddb_regs.cs & 0x3)
199 dp = vp->valuep;
200 else if (ddb_regs.ebp < int_stack_high)
201 db_error("cannot get/set user registers in nested interrupt\n");
202 }
203 } else {
204 if (thread == THREAD_NULL || thread == current_thread()) {
205 dp = vp->valuep;
206 } else if ((thread->state & TH_SWAPPED) == 0 &&
207 thread->kernel_stack) {
208 dp = db_lookup_i386_kreg(vp->name,
209 (int *)(STACK_IKS(thread->kernel_stack)));
210 if (dp == 0)
211 dp = &null_reg;
212 } else if ((thread->state & TH_SWAPPED) &&
213 thread->swap_func != thread_exception_return) {
214 /* only EIP is valid */
215 if (vp->valuep == (int *) &ddb_regs.eip) {
216 dp = (int *)(&thread->swap_func);
217 } else {
218 dp = &null_reg;
219 }
220 }
221 }
222 if (dp == 0) {
223 if (thread->pcb == 0)
224 db_error("no pcb\n");
225 dp = (int *)((int)(&thread->pcb->iss) +
226 ((int)vp->valuep - (int)&ddb_regs));
227 }
228 if (flag == DB_VAR_SET)
229 *dp = *valuep;
230 else
231 *valuep = *dp;
232 return(0);
233 }
234
235 void
236 db_find_trace_symbols()
237 {
238 db_expr_t value;
239 if (db_value_of_name("_user_trap", &value))
240 db_user_trap_symbol_value = (db_addr_t) value;
241 if (db_value_of_name("_kernel_trap", &value))
242 db_kernel_trap_symbol_value = (db_addr_t) value;
243 if (db_value_of_name("_interrupt", &value))
244 db_interrupt_symbol_value = (db_addr_t) value;
245 if (db_value_of_name("_return_to_iret", &value))
246 db_return_to_iret_symbol_value = (db_addr_t) value;
247 if (db_value_of_name("_syscall", &value))
248 db_syscall_symbol_value = (db_addr_t) value;
249 db_trace_symbols_found = TRUE;
250 }
251
252 /*
253 * Figure out how many arguments were passed into the frame at "fp".
254 */
255 int db_numargs_default = 5;
256
257 int
258 db_numargs(fp, task)
259 struct i386_frame *fp;
260 task_t task;
261 {
262 int *argp;
263 int inst;
264 int args;
265 extern char etext[];
266
267 argp = (int *)db_get_task_value((int)&fp->f_retaddr, 4, FALSE, task);
268 if (argp < (int *)VM_MIN_KERNEL_ADDRESS || argp > (int *)etext)
269 args = db_numargs_default;
270 else if (!DB_CHECK_ACCESS((int)argp, 4, task))
271 args = db_numargs_default;
272 else {
273 inst = db_get_task_value((int)argp, 4, FALSE, task);
274 if ((inst & 0xff) == 0x59) /* popl %ecx */
275 args = 1;
276 else if ((inst & 0xffff) == 0xc483) /* addl %n, %esp */
277 args = ((inst >> 16) & 0xff) / 4;
278 else
279 args = db_numargs_default;
280 }
281 return (args);
282 }
283
284 struct interrupt_frame {
285 struct i386_frame *if_frame; /* point to next frame */
286 int if_retaddr; /* return address to _interrupt */
287 int if_unit; /* unit number */
288 spl_t if_spl; /* saved spl */
289 int if_iretaddr; /* _return_to_{iret,iret_i} */
290 int if_edx; /* old sp(iret) or saved edx(iret_i) */
291 int if_ecx; /* saved ecx(iret_i) */
292 int if_eax; /* saved eax(iret_i) */
293 int if_eip; /* saved eip(iret_i) */
294 int if_cs; /* saved cs(iret_i) */
295 int if_efl; /* saved efl(iret_i) */
296 };
297
298 /*
299 * Figure out the next frame up in the call stack.
300 * For trap(), we print the address of the faulting instruction and
301 * proceed with the calling frame. We return the ip that faulted.
302 * If the trap was caused by jumping through a bogus pointer, then
303 * the next line in the backtrace will list some random function as
304 * being called. It should get the argument list correct, though.
305 * It might be possible to dig out from the next frame up the name
306 * of the function that faulted, but that could get hairy.
307 */
308 void
309 db_nextframe(lfp, fp, ip, frame_type, thread)
310 struct i386_frame **lfp; /* in/out */
311 struct i386_frame **fp; /* in/out */
312 db_addr_t *ip; /* out */
313 int frame_type; /* in */
314 thread_t thread; /* in */
315 {
316 extern char * trap_type[];
317 extern int TRAP_TYPES;
318
319 struct i386_saved_state *saved_regs;
320 struct interrupt_frame *ifp;
321 struct i386_interrupt_state *isp;
322 task_t task = (thread != THREAD_NULL)? thread->task: TASK_NULL;
323
324 switch(frame_type) {
325 case TRAP:
326 /*
327 * We know that trap() has 1 argument and we know that
328 * it is an (strcut i386_saved_state *).
329 */
330 saved_regs = (struct i386_saved_state *)
331 db_get_task_value((int)&((*fp)->f_arg0),4,FALSE,task);
332 if (saved_regs->trapno >= 0 && saved_regs->trapno < TRAP_TYPES) {
333 db_printf(">>>>> %s trap at ",
334 trap_type[saved_regs->trapno]);
335 } else {
336 db_printf(">>>>> trap (number %d) at ",
337 saved_regs->trapno & 0xffff);
338 }
339 db_task_printsym(saved_regs->eip, DB_STGY_PROC, task);
340 db_printf(" <<<<<\n");
341 *fp = (struct i386_frame *)saved_regs->ebp;
342 *ip = (db_addr_t)saved_regs->eip;
343 break;
344 case INTERRUPT:
345 if (*lfp == 0) {
346 db_printf(">>>>> interrupt <<<<<\n");
347 goto miss_frame;
348 }
349 db_printf(">>>>> interrupt at ");
350 ifp = (struct interrupt_frame *)(*lfp);
351 *fp = ifp->if_frame;
352 if (ifp->if_iretaddr == db_return_to_iret_symbol_value)
353 *ip = ((struct i386_interrupt_state *) ifp->if_edx)->eip;
354 else
355 *ip = (db_addr_t) ifp->if_eip;
356 db_task_printsym(*ip, DB_STGY_PROC, task);
357 db_printf(" <<<<<\n");
358 break;
359 case SYSCALL:
360 if (thread != THREAD_NULL && thread->pcb) {
361 *ip = (db_addr_t) thread->pcb->iss.eip;
362 *fp = (struct i386_frame *) thread->pcb->iss.ebp;
363 break;
364 }
365 /* falling down for unknown case */
366 default:
367 miss_frame:
368 *ip = (db_addr_t)
369 db_get_task_value((int)&(*fp)->f_retaddr, 4, FALSE, task);
370 *lfp = *fp;
371 *fp = (struct i386_frame *)
372 db_get_task_value((int)&(*fp)->f_frame, 4, FALSE, task);
373 break;
374 }
375 }
376
377 void
378 db_stack_trace_cmd(addr, have_addr, count, modif)
379 db_expr_t addr;
380 boolean_t have_addr;
381 db_expr_t count;
382 char *modif;
383 {
384 struct i386_frame *frame, *lastframe;
385 int *argp;
386 db_addr_t callpc;
387 int frame_type;
388 boolean_t kernel_only = TRUE;
389 boolean_t trace_thread = FALSE;
390 char *filename;
391 int linenum;
392 task_t task;
393 thread_t th;
394 int user_frame = 0;
395 extern unsigned db_maxoff;
396
397 if (!db_trace_symbols_found)
398 db_find_trace_symbols();
399
400 {
401 register char *cp = modif;
402 register char c;
403
404 while ((c = *cp++) != 0) {
405 if (c == 't')
406 trace_thread = TRUE;
407 if (c == 'u')
408 kernel_only = FALSE;
409 }
410 }
411
412 if (count == -1)
413 count = 65535;
414
415 if (!have_addr && !trace_thread) {
416 frame = (struct i386_frame *)ddb_regs.ebp;
417 callpc = (db_addr_t)ddb_regs.eip;
418 th = current_thread();
419 task = (th != THREAD_NULL)? th->task: TASK_NULL;
420 } else if (trace_thread) {
421 if (have_addr) {
422 th = (thread_t) addr;
423 if (!db_check_thread_address_valid(th))
424 return;
425 } else {
426 th = db_default_thread;
427 if (th == THREAD_NULL)
428 th = current_thread();
429 if (th == THREAD_NULL) {
430 db_printf("no active thread\n");
431 return;
432 }
433 }
434 task = th->task;
435 if (th == current_thread()) {
436 frame = (struct i386_frame *)ddb_regs.ebp;
437 callpc = (db_addr_t)ddb_regs.eip;
438 } else {
439 if (th->pcb == 0) {
440 db_printf("thread has no pcb\n");
441 return;
442 }
443 if ((th->state & TH_SWAPPED) || th->kernel_stack == 0) {
444 register struct i386_saved_state *iss = &th->pcb->iss;
445
446 db_printf("Continuation ");
447 db_task_printsym((db_expr_t)th->swap_func, DB_STGY_PROC, task);
448 db_printf("\n");
449
450 frame = (struct i386_frame *) (iss->ebp);
451 callpc = (db_addr_t) (iss->eip);
452 } else {
453 register struct i386_kernel_state *iks;
454 iks = STACK_IKS(th->kernel_stack);
455 frame = (struct i386_frame *) (iks->k_ebp);
456 callpc = (db_addr_t) (iks->k_eip);
457 }
458 }
459 } else {
460 frame = (struct i386_frame *)addr;
461 th = (db_default_thread)? db_default_thread: current_thread();
462 task = (th != THREAD_NULL)? th->task: TASK_NULL;
463 callpc = (db_addr_t)db_get_task_value((int)&frame->f_retaddr, 4,
464 FALSE, task);
465 }
466
467 if (!INKERNEL((unsigned)callpc) && !INKERNEL((unsigned)frame)) {
468 db_printf(">>>>> user space <<<<<\n");
469 user_frame++;
470 }
471
472 lastframe = 0;
473 while (count-- && frame != 0) {
474 register int narg;
475 char * name;
476 db_expr_t offset;
477
478 if (INKERNEL((unsigned)callpc) && user_frame == 0) {
479 db_addr_t call_func = 0;
480
481 db_symbol_values(0, db_search_task_symbol(callpc,
482 DB_STGY_XTRN, (db_addr_t *)&offset,
483 TASK_NULL),
484 &name, (db_expr_t *)&call_func);
485 if (call_func == db_user_trap_symbol_value ||
486 call_func == db_kernel_trap_symbol_value) {
487 frame_type = TRAP;
488 narg = 1;
489 } else if (call_func == db_interrupt_symbol_value) {
490 frame_type = INTERRUPT;
491 goto next_frame;
492 } else if (call_func == db_syscall_symbol_value) {
493 frame_type = SYSCALL;
494 goto next_frame;
495 } else {
496 frame_type = 0;
497 narg = db_numargs(frame, task);
498 }
499 } else if (INKERNEL((unsigned)callpc) ^ INKERNEL((unsigned)frame)) {
500 frame_type = 0;
501 narg = -1;
502 } else {
503 frame_type = 0;
504 narg = db_numargs(frame, task);
505 }
506
507 db_find_task_sym_and_offset(callpc, &name, (db_addr_t *)&offset, task);
508 if (name == 0 || offset > db_maxoff) {
509 db_printf("0x%x(", callpc);
510 offset = 0;
511 } else
512 db_printf("%s(", name);
513
514 argp = &frame->f_arg0;
515 while (narg > 0) {
516 db_printf("%x", db_get_task_value((int)argp,4,FALSE,task));
517 argp++;
518 if (--narg != 0)
519 db_printf(",");
520 }
521 if (narg < 0)
522 db_printf("...");
523 db_printf(")");
524 if (offset) {
525 db_printf("+%x", offset);
526 }
527 if (db_line_at_pc(0, &filename, &linenum, callpc)) {
528 db_printf(" [%s", filename);
529 if (linenum > 0)
530 db_printf(":%d", linenum);
531 printf("]");
532 }
533 db_printf("\n");
534
535 next_frame:
536 db_nextframe(&lastframe, &frame, &callpc, frame_type, th);
537
538 if (frame == 0) {
539 /* end of chain */
540 break;
541 }
542 if (!INKERNEL(lastframe) ||
543 (!INKERNEL((unsigned)callpc) && !INKERNEL((unsigned)frame)))
544 user_frame++;
545 if (user_frame == 1) {
546 db_printf(">>>>> user space <<<<<\n");
547 if (kernel_only)
548 break;
549 }
550 if (frame <= lastframe) {
551 if (INKERNEL(lastframe) && !INKERNEL(frame))
552 continue;
553 db_printf("Bad frame pointer: 0x%x\n", frame);
554 break;
555 }
556 }
557 }
Cache object: 638589381db93c233fb999d535119573
|