2 * Copyright (c) 2000 Apple Computer, Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
6 * Copyright (c) 1999-2003 Apple Computer, Inc. All Rights Reserved.
8 * This file contains Original Code and/or Modifications of Original Code
9 * as defined in and that are subject to the Apple Public Source License
10 * Version 2.0 (the 'License'). You may not use this file except in
11 * compliance with the License. Please obtain a copy of the License at
12 * http://www.opensource.apple.com/apsl/ and read it before using this
15 * The Original Code and all software distributed under the License are
16 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
17 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
18 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
19 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
20 * Please see the License for the specific language governing rights and
21 * limitations under the License.
23 * @APPLE_LICENSE_HEADER_END@
29 * Mach Operating System
30 * Copyright (c) 1991,1990 Carnegie Mellon University
31 * All Rights Reserved.
33 * Permission to use, copy, modify and distribute this software and its
34 * documentation is hereby granted, provided that both the copyright
35 * notice and this permission notice appear in all copies of the
36 * software, derivative works or modified versions, and any portions
37 * thereof, and that both notices appear in supporting documentation.
39 * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
40 * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
41 * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
43 * Carnegie Mellon requests users of this software to return to
45 * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
46 * School of Computer Science
47 * Carnegie Mellon University
48 * Pittsburgh PA 15213-3890
50 * any improvements or extensions that they make and grant Carnegie Mellon
51 * the rights to redistribute these changes.
58 #include <mach/boolean.h>
59 #include <vm/vm_map.h>
60 #include <kern/thread.h>
61 #include <kern/task.h>
63 #include <machine/asm.h>
64 #include <machine/db_machdep.h>
65 #include <machine/setjmp.h>
66 #include <mach/machine.h>
68 #include <ddb/db_access.h>
69 #include <ddb/db_sym.h>
70 #include <ddb/db_variables.h>
71 #include <ddb/db_command.h>
72 #include <ddb/db_task_thread.h>
73 #include <ddb/db_output.h>
75 extern jmp_buf_t
*db_recover
;
76 extern struct i386_saved_state
*saved_state
[];
78 struct i386_kernel_state ddb_null_kregs
;
84 extern vm_offset_t vm_min_inks_addr
; /* set by db_clone_symtabXXX */
85 #define INKSERVER(va) (((vm_offset_t)(va)) >= vm_min_inks_addr)
88 extern vm_offset_t interrupt_stack
[];
89 #define ININTSTACK(va) \
90 (((vm_offset_t)(va)) >= interrupt_stack[cpu_number()] &&\
91 (((vm_offset_t)(va)) < interrupt_stack[cpu_number()] + \
94 extern char intstack
[];
95 #define ININTSTACK(va) \
96 (((vm_offset_t)(va)) >= (vm_offset_t)intstack && \
97 (((vm_offset_t)(va)) < ((vm_offset_t)&intstack) + \
99 #endif /* NCPUS > 1 */
101 #define INKERNELSTACK(va, th) \
102 (th == THR_ACT_NULL || \
103 (((vm_offset_t)(va)) >= th->thread->kernel_stack && \
104 (((vm_offset_t)(va)) < th->thread->kernel_stack + \
105 KERNEL_STACK_SIZE)) || \
109 struct i386_frame
*f_frame
;
118 db_addr_t db_user_trap_symbol_value
= 0;
119 db_addr_t db_kernel_trap_symbol_value
= 0;
120 db_addr_t db_interrupt_symbol_value
= 0;
121 db_addr_t db_return_to_iret_symbol_value
= 0;
122 db_addr_t db_syscall_symbol_value
= 0;
123 boolean_t db_trace_symbols_found
= FALSE
;
129 { "ebx", (int)(&((struct i386_kernel_state
*)0)->k_ebx
) },
130 { "esp", (int)(&((struct i386_kernel_state
*)0)->k_esp
) },
131 { "ebp", (int)(&((struct i386_kernel_state
*)0)->k_ebp
) },
132 { "edi", (int)(&((struct i386_kernel_state
*)0)->k_edi
) },
133 { "esi", (int)(&((struct i386_kernel_state
*)0)->k_esi
) },
134 { "eip", (int)(&((struct i386_kernel_state
*)0)->k_eip
) },
140 extern int * db_lookup_i386_kreg(
143 extern int db_i386_reg_value(
144 struct db_variable
* vp
,
147 db_var_aux_param_t ap
);
148 extern void db_find_trace_symbols(void);
149 extern int db_numargs(
150 struct i386_frame
*fp
,
152 extern void db_nextframe(
153 struct i386_frame
**lfp
,
154 struct i386_frame
**fp
,
157 thread_act_t thr_act
);
162 * Machine register set.
164 struct db_variable db_regs
[] = {
165 { "cs", (int *)&ddb_regs
.cs
, db_i386_reg_value
, 0, 0, 0, 0, TRUE
},
166 { "ds", (int *)&ddb_regs
.ds
, db_i386_reg_value
, 0, 0, 0, 0, TRUE
},
167 { "es", (int *)&ddb_regs
.es
, db_i386_reg_value
, 0, 0, 0, 0, TRUE
},
168 { "fs", (int *)&ddb_regs
.fs
, db_i386_reg_value
, 0, 0, 0, 0, TRUE
},
169 { "gs", (int *)&ddb_regs
.gs
, db_i386_reg_value
, 0, 0, 0, 0, TRUE
},
170 { "ss", (int *)&ddb_regs
.ss
, db_i386_reg_value
, 0, 0, 0, 0, TRUE
},
171 { "eax",(int *)&ddb_regs
.eax
, db_i386_reg_value
, 0, 0, 0, 0, TRUE
},
172 { "ecx",(int *)&ddb_regs
.ecx
, db_i386_reg_value
, 0, 0, 0, 0, TRUE
},
173 { "edx",(int *)&ddb_regs
.edx
, db_i386_reg_value
, 0, 0, 0, 0, TRUE
},
174 { "ebx",(int *)&ddb_regs
.ebx
, db_i386_reg_value
, 0, 0, 0, 0, TRUE
},
175 { "esp",(int *)&ddb_regs
.uesp
,db_i386_reg_value
, 0, 0, 0, 0, TRUE
},
176 { "ebp",(int *)&ddb_regs
.ebp
, db_i386_reg_value
, 0, 0, 0, 0, TRUE
},
177 { "esi",(int *)&ddb_regs
.esi
, db_i386_reg_value
, 0, 0, 0, 0, TRUE
},
178 { "edi",(int *)&ddb_regs
.edi
, db_i386_reg_value
, 0, 0, 0, 0, TRUE
},
179 { "eip",(int *)&ddb_regs
.eip
, db_i386_reg_value
, 0, 0, 0, 0, TRUE
},
180 { "efl",(int *)&ddb_regs
.efl
, db_i386_reg_value
, 0, 0, 0, 0, TRUE
},
182 struct db_variable
*db_eregs
= db_regs
+ sizeof(db_regs
)/sizeof(db_regs
[0]);
189 register struct i386_kregs
*kp
;
191 for (kp
= i386_kregs
; kp
->name
; kp
++) {
192 if (strcmp(name
, kp
->name
) == 0)
193 return((int *)((int)kregp
+ kp
->offset
));
200 struct db_variable
*vp
,
203 db_var_aux_param_t ap
)
207 db_expr_t null_reg
= 0;
208 register thread_act_t thr_act
= ap
->thr_act
;
209 extern unsigned int_stack_high
;
212 if (db_option(ap
->modif
, 'u')) {
213 if (thr_act
== THR_ACT_NULL
) {
214 if ((thr_act
= current_act()) == THR_ACT_NULL
)
215 db_error("no user registers\n");
217 if (thr_act
== current_act()) {
218 if (IS_USER_TRAP(&ddb_regs
, &etext
))
220 else if (ddb_regs
.ebp
< int_stack_high
)
221 db_error("cannot get/set user registers in nested interrupt\n");
224 if (thr_act
== THR_ACT_NULL
|| thr_act
== current_act()) {
227 if (thr_act
->thread
&&
228 !(thr_act
->thread
->state
& TH_STACK_HANDOFF
) &&
229 thr_act
->thread
->kernel_stack
) {
232 for (cpu
= 0; cpu
< NCPUS
; cpu
++) {
233 if (machine_slot
[cpu
].running
== TRUE
&&
234 cpu_data
[cpu
].active_thread
== thr_act
->thread
&& saved_state
[cpu
]) {
235 dp
= (int *) (((int)saved_state
[cpu
]) +
236 (((int) vp
->valuep
) -
241 if (dp
== 0 && thr_act
&& thr_act
->thread
)
242 dp
= db_lookup_i386_kreg(vp
->name
,
243 (int *)(STACK_IKS(thr_act
->thread
->kernel_stack
)));
246 } else if (thr_act
->thread
&&
247 (thr_act
->thread
->state
&TH_STACK_HANDOFF
)){
248 /* only EIP is valid */
249 if (vp
->valuep
== (int *) &ddb_regs
.eip
) {
250 dp
= (int *)(&thr_act
->thread
->continuation
);
260 if (!db_option(ap
->modif
, 'u')) {
261 for (cpu
= 0; cpu
< NCPUS
; cpu
++) {
262 if (machine_slot
[cpu
].running
== TRUE
&&
263 cpu_data
[cpu
].active_thread
== thr_act
->thread
&& saved_state
[cpu
]) {
264 dp
= (int *) (((int)saved_state
[cpu
]) +
265 (((int) vp
->valuep
) -
272 if (!thr_act
|| thr_act
->mact
.pcb
== 0)
273 db_error("no pcb\n");
274 dp
= (int *)((int)(&thr_act
->mact
.pcb
->iss
) +
275 ((int)vp
->valuep
- (int)&ddb_regs
));
278 if (flag
== DB_VAR_SET
)
286 db_find_trace_symbols(void)
289 boolean_t found_some
;
292 if (db_value_of_name(CC_SYM_PREFIX
"user_trap", &value
)) {
293 db_user_trap_symbol_value
= (db_addr_t
) value
;
296 if (db_value_of_name(CC_SYM_PREFIX
"kernel_trap", &value
)) {
297 db_kernel_trap_symbol_value
= (db_addr_t
) value
;
300 if (db_value_of_name(CC_SYM_PREFIX
"interrupt", &value
)) {
301 db_interrupt_symbol_value
= (db_addr_t
) value
;
304 if (db_value_of_name(CC_SYM_PREFIX
"return_to_iret", &value
)) {
305 db_return_to_iret_symbol_value
= (db_addr_t
) value
;
308 if (db_value_of_name(CC_SYM_PREFIX
"syscall", &value
)) {
309 db_syscall_symbol_value
= (db_addr_t
) value
;
313 db_trace_symbols_found
= TRUE
;
317 * Figure out how many arguments were passed into the frame at "fp".
319 int db_numargs_default
= 5;
323 struct i386_frame
*fp
,
331 argp
= (int *)db_get_task_value((int)&fp
->f_retaddr
, 4, FALSE
, task
);
332 if (argp
< (int *)VM_MIN_KERNEL_ADDRESS
|| (char *)argp
> &etext
)
333 args
= db_numargs_default
;
334 else if (!DB_CHECK_ACCESS((int)argp
, 4, task
))
335 args
= db_numargs_default
;
337 inst
= db_get_task_value((int)argp
, 4, FALSE
, task
);
338 if ((inst
& 0xff) == 0x59) /* popl %ecx */
340 else if ((inst
& 0xffff) == 0xc483) /* addl %n, %esp */
341 args
= ((inst
>> 16) & 0xff) / 4;
343 args
= db_numargs_default
;
348 struct interrupt_frame
{
349 struct i386_frame
*if_frame
; /* point to next frame */
350 int if_retaddr
; /* return address to _interrupt */
351 int if_unit
; /* unit number */
352 int if_spl
; /* saved spl */
353 int if_iretaddr
; /* _return_to_{iret,iret_i} */
354 int if_edx
; /* old sp(iret) or saved edx(iret_i) */
355 int if_ecx
; /* saved ecx(iret_i) */
356 int if_eax
; /* saved eax(iret_i) */
357 int if_eip
; /* saved eip(iret_i) */
358 int if_cs
; /* saved cs(iret_i) */
359 int if_efl
; /* saved efl(iret_i) */
363 * Figure out the next frame up in the call stack.
364 * For trap(), we print the address of the faulting instruction and
365 * proceed with the calling frame. We return the ip that faulted.
366 * If the trap was caused by jumping through a bogus pointer, then
367 * the next line in the backtrace will list some random function as
368 * being called. It should get the argument list correct, though.
369 * It might be possible to dig out from the next frame up the name
370 * of the function that faulted, but that could get hairy.
374 struct i386_frame
**lfp
, /* in/out */
375 struct i386_frame
**fp
, /* in/out */
376 db_addr_t
*ip
, /* out */
377 int frame_type
, /* in */
378 thread_act_t thr_act
) /* in */
380 extern char * trap_type
[];
381 extern int TRAP_TYPES
;
383 struct i386_saved_state
*saved_regs
;
384 struct interrupt_frame
*ifp
;
385 struct i386_interrupt_state
*isp
;
386 task_t task
= (thr_act
!= THR_ACT_NULL
)? thr_act
->task
: TASK_NULL
;
391 * We know that trap() has 1 argument and we know that
392 * it is an (strcut i386_saved_state *).
394 saved_regs
= (struct i386_saved_state
*)
395 db_get_task_value((int)&((*fp
)->f_arg0
),4,FALSE
,task
);
396 if (saved_regs
->trapno
>= 0 && saved_regs
->trapno
< TRAP_TYPES
) {
397 db_printf(">>>>> %s trap at ",
398 trap_type
[saved_regs
->trapno
]);
400 db_printf(">>>>> trap (number %d) at ",
401 saved_regs
->trapno
& 0xffff);
403 db_task_printsym(saved_regs
->eip
, DB_STGY_PROC
, task
);
404 db_printf(" <<<<<\n");
405 *fp
= (struct i386_frame
*)saved_regs
->ebp
;
406 *ip
= (db_addr_t
)saved_regs
->eip
;
410 db_printf(">>>>> interrupt <<<<<\n");
413 db_printf(">>>>> interrupt at ");
414 ifp
= (struct interrupt_frame
*)(*lfp
);
416 if (ifp
->if_iretaddr
== db_return_to_iret_symbol_value
)
417 *ip
= ((struct i386_interrupt_state
*) ifp
->if_edx
)->eip
;
419 *ip
= (db_addr_t
) ifp
->if_eip
;
420 db_task_printsym(*ip
, DB_STGY_PROC
, task
);
421 db_printf(" <<<<<\n");
424 if (thr_act
!= THR_ACT_NULL
&& thr_act
->mact
.pcb
) {
425 *ip
= (db_addr_t
) thr_act
->mact
.pcb
->iss
.eip
;
426 *fp
= (struct i386_frame
*) thr_act
->mact
.pcb
->iss
.ebp
;
429 /* falling down for unknown case */
433 db_get_task_value((int)&(*fp
)->f_retaddr
, 4, FALSE
, task
);
435 *fp
= (struct i386_frame
*)
436 db_get_task_value((int)&(*fp
)->f_frame
, 4, FALSE
, task
);
448 struct i386_frame
*frame
, *lastframe
;
450 db_addr_t callpc
, lastcallpc
;
452 boolean_t kernel_only
= TRUE
;
453 boolean_t trace_thread
= FALSE
;
454 boolean_t trace_all_threads
= FALSE
;
459 thread_act_t th
, top_act
;
463 jmp_buf_t db_jmp_buf
;
464 queue_entry_t act_list
;
466 if (!db_trace_symbols_found
)
467 db_find_trace_symbols();
470 register char *cp
= modif
;
473 while ((c
= *cp
++) != 0) {
477 trace_all_threads
= TRUE
;
485 if (trace_all_threads
) {
486 if (!have_addr
&& !trace_thread
) {
489 act_list
= &(current_task()->thr_acts
);
490 addr
= (db_expr_t
) queue_first(act_list
);
491 } else if (trace_thread
) {
493 if (!db_check_act_address_valid((thread_act_t
)addr
)) {
494 if (db_lookup_task((task_t
)addr
) == -1)
496 act_list
= &(((task_t
)addr
)->thr_acts
);
497 addr
= (db_expr_t
) queue_first(act_list
);
499 act_list
= &(((thread_act_t
)addr
)->task
->thr_acts
);
500 thcount
= db_lookup_task_act(((thread_act_t
)addr
)->task
,
505 if (th
== THR_ACT_NULL
)
507 if (th
== THR_ACT_NULL
) {
508 db_printf("no active thr_act\n");
512 act_list
= &th
->task
->thr_acts
;
513 addr
= (db_expr_t
) queue_first(act_list
);
522 top_act
= THR_ACT_NULL
;
527 if (!have_addr
&& !trace_thread
) {
528 frame
= (struct i386_frame
*)ddb_regs
.ebp
;
529 callpc
= (db_addr_t
)ddb_regs
.eip
;
531 task
= (th
!= THR_ACT_NULL
)? th
->task
: TASK_NULL
;
532 } else if (trace_thread
) {
534 th
= (thread_act_t
) addr
;
535 if (!db_check_act_address_valid(th
))
539 if (th
== THR_ACT_NULL
)
541 if (th
== THR_ACT_NULL
) {
542 db_printf("no active thread\n");
546 if (trace_all_threads
)
547 db_printf("---------- Thread 0x%x (#%d of %d) ----------\n",
548 addr
, thcount
, th
->task
->thr_act_count
);
554 if (th
== current_act()) {
555 frame
= (struct i386_frame
*)ddb_regs
.ebp
;
556 callpc
= (db_addr_t
)ddb_regs
.eip
;
558 if (th
->mact
.pcb
== 0) {
559 db_printf("thread has no pcb\n");
563 register struct i386_saved_state
*iss
=
566 db_printf("thread has no shuttle\n");
568 frame
= (struct i386_frame
*) (iss
->ebp
);
569 callpc
= (db_addr_t
) (iss
->eip
);
574 else if ((th
->thread
->state
& TH_STACK_HANDOFF
) ||
575 th
->thread
->kernel_stack
== 0) {
576 register struct i386_saved_state
*iss
=
579 db_printf("Continuation ");
580 db_task_printsym((db_expr_t
)th
->thread
->continuation
,
583 frame
= (struct i386_frame
*) (iss
->ebp
);
584 callpc
= (db_addr_t
) (iss
->eip
);
588 for (cpu
= 0; cpu
< NCPUS
; cpu
++) {
589 if (machine_slot
[cpu
].running
== TRUE
&&
590 cpu_data
[cpu
].active_thread
== th
->thread
&&
595 if (top_act
!= THR_ACT_NULL
) {
597 * Trying to get the backtrace of an activation
598 * which is not the top_most one in the RPC chain:
599 * use the activation's pcb.
601 register struct i386_saved_state
*iss
=
603 frame
= (struct i386_frame
*) (iss
->ebp
);
604 callpc
= (db_addr_t
) (iss
->eip
);
607 register struct i386_kernel_state
*iks
;
610 iks
= STACK_IKS(th
->thread
->kernel_stack
);
612 if ((r
= _setjmp(db_recover
= &db_jmp_buf
)) == 0) {
613 frame
= (struct i386_frame
*) (iks
->k_ebp
);
614 callpc
= (db_addr_t
) (iks
->k_eip
);
617 * The kernel stack has probably been
618 * paged out (swapped out activation).
621 if (r
== 2) /* 'q' from db_more() */
623 db_printf("<kernel stack (0x%x) error "
624 "(probably swapped out)>\n",
630 db_printf(">>>>> active on cpu %d <<<<<\n",
632 frame
= (struct i386_frame
*)
633 saved_state
[cpu
]->ebp
;
634 callpc
= (db_addr_t
) saved_state
[cpu
]->eip
;
640 frame
= (struct i386_frame
*)addr
;
641 th
= (db_default_act
)? db_default_act
: current_act();
642 task
= (th
!= THR_ACT_NULL
)? th
->task
: TASK_NULL
;
643 callpc
= (db_addr_t
)db_get_task_value((int)&frame
->f_retaddr
,
646 (user_frame
) ? task
: 0);
649 if (!INKERNELSTACK((unsigned)frame
, th
)) {
650 db_printf(">>>>> user space <<<<<\n");
654 } else if (INKSERVER(callpc
) && INKSERVER(frame
)) {
655 db_printf(">>>>> INKserver space <<<<<\n");
659 lastcallpc
= (db_addr_t
) 0;
660 while (frame_count
-- && frame
!= 0) {
664 db_addr_t call_func
= 0;
667 db_symbol_values(NULL
,
668 db_search_task_symbol_and_line(
674 (user_frame
) ? task
: 0,
676 &name
, (db_expr_t
*)&call_func
);
677 if (user_frame
== 0) {
678 if (call_func
== db_user_trap_symbol_value
||
679 call_func
== db_kernel_trap_symbol_value
) {
682 } else if (call_func
== db_interrupt_symbol_value
) {
683 frame_type
= INTERRUPT
;
685 } else if (call_func
== db_syscall_symbol_value
) {
686 frame_type
= SYSCALL
;
691 if ((r
= _setjmp(db_recover
= &db_jmp_buf
)) == 0) {
693 narg
= db_numargs(frame
,
694 (user_frame
) ? task
: 0);
704 if ((r
= _setjmp(db_recover
= &db_jmp_buf
)) == 0) {
706 narg
= db_numargs(frame
,
707 (user_frame
) ? task
: 0);
715 if (name
== 0 || offset
> db_maxoff
) {
716 db_printf("0x%x 0x%x(", frame
, callpc
);
719 db_printf("0x%x %s(", frame
, name
);
721 argp
= &frame
->f_arg0
;
726 if ((r
= _setjmp(db_recover
= &db_jmp_buf
)) == 0) {
727 value
= db_get_task_value((int)argp
,
730 (user_frame
) ? task
: 0);
733 if (r
== 2) /* 'q' from db_more() */
735 db_printf("... <stack error>)");
737 db_printf("+%x", offset
);
739 db_printf(" [%s", filename
);
741 db_printf(":%d", linenum
);
748 db_printf("%x", value
);
757 db_printf("+%x", offset
);
760 db_printf(" [%s", filename
);
762 db_printf(":%d", linenum
);
769 db_nextframe(&lastframe
, &frame
, &callpc
, frame_type
,
770 (user_frame
) ? th
: THR_ACT_NULL
);
773 if (th
->lower
!= THR_ACT_NULL
) {
774 if (top_act
== THR_ACT_NULL
)
777 db_printf(">>>>> next activation 0x%x ($task%d.%d) <<<<<\n",
779 db_lookup_task(th
->task
),
780 db_lookup_task_act(th
->task
, th
));
781 goto next_activation
;
786 if (!INKERNELSTACK(lastframe
, th
) ||
787 !INKERNELSTACK((unsigned)frame
, th
))
789 if (user_frame
== 1) {
790 db_printf(">>>>> user space <<<<<\n");
793 } else if ((!INKSERVER(lastframe
) || !INKSERVER(lastcallpc
)) &&
794 (INKSERVER(callpc
) && INKSERVER(frame
))) {
795 db_printf(">>>>> inkserver space <<<<<\n");
797 if (frame
<= lastframe
) {
798 if ((INKERNELSTACK(lastframe
, th
) &&
799 !INKERNELSTACK(frame
, th
)) ||
800 (INKSERVER(lastframe
) ^ INKSERVER(frame
)))
802 db_printf("Bad frame pointer: 0x%x\n", frame
);
808 if (trace_all_threads
) {
809 if (top_act
!= THR_ACT_NULL
)
811 th
= (thread_act_t
) queue_next(&th
->thr_acts
);
812 if (! queue_end(act_list
, (queue_entry_t
) th
)) {
814 addr
= (db_expr_t
) th
;