2 * Copyright (c) 2000-2005 Apple Computer, Inc. All rights reserved.
4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. The rights granted to you under the License
10 * may not be used to create, or enable the creation or redistribution of,
11 * unlawful or unlicensed copies of an Apple operating system, or to
12 * circumvent, violate, or enable the circumvention or violation of, any
13 * terms of an Apple operating system software license agreement.
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
18 * The Original Code and all software distributed under the License are
19 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
22 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
23 * Please see the License for the specific language governing rights and
24 * limitations under the License.
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
34 * Author: David B. Golub, Carnegie Mellon University
41 #include <mach/boolean.h>
42 #include <machine/db_machdep.h>
43 #include <ddb/db_lex.h>
44 #include <ddb/db_break.h>
45 #include <ddb/db_access.h>
46 #include <ddb/db_sym.h>
47 #include <ddb/db_variables.h>
48 #include <ddb/db_command.h>
49 #include <ddb/db_cond.h>
50 #include <ddb/db_expr.h>
51 #include <ddb/db_output.h> /* For db_printf() */
52 #include <ddb/db_task_thread.h>
53 #include <kern/thread.h>
55 #define NBREAKPOINTS 100
56 #define NTHREAD_LIST (NBREAKPOINTS*3)
58 struct db_breakpoint db_break_table
[NBREAKPOINTS
];
59 db_breakpoint_t db_next_free_breakpoint
= &db_break_table
[0];
60 db_breakpoint_t db_free_breakpoints
= 0;
61 db_breakpoint_t db_breakpoint_list
= 0;
63 static struct db_thread_breakpoint db_thread_break_list
[NTHREAD_LIST
];
64 static db_thread_breakpoint_t db_free_thread_break_list
= 0;
65 static boolean_t db_thread_break_init
= FALSE
;
66 static int db_breakpoint_number
= 0;
68 /* Prototypes for functions local to this file. XXX -- should be static!
70 static int db_add_thread_breakpoint(
71 register db_breakpoint_t bkpt
,
76 static int db_delete_thread_breakpoint(
77 register db_breakpoint_t bkpt
,
78 vm_offset_t task_thd
);
80 static db_thread_breakpoint_t
db_find_thread_breakpoint(
84 static void db_force_delete_breakpoint(
89 db_breakpoint_t
db_breakpoint_alloc(void);
91 void db_breakpoint_free(register db_breakpoint_t bkpt
);
93 void db_delete_breakpoint(
96 vm_offset_t task_thd
);
99 db_delete_all_breakpoints(
102 void db_list_breakpoints(void);
107 db_breakpoint_alloc(void)
109 register db_breakpoint_t bkpt
;
111 if ((bkpt
= db_free_breakpoints
) != 0) {
112 db_free_breakpoints
= bkpt
->link
;
115 if (db_next_free_breakpoint
== &db_break_table
[NBREAKPOINTS
]) {
116 db_printf("All breakpoints used.\n");
119 bkpt
= db_next_free_breakpoint
;
120 db_next_free_breakpoint
++;
126 db_breakpoint_free(register db_breakpoint_t bkpt
)
128 bkpt
->link
= db_free_breakpoints
;
129 db_free_breakpoints
= bkpt
;
133 db_add_thread_breakpoint(
134 register db_breakpoint_t bkpt
,
135 vm_offset_t task_thd
,
139 register db_thread_breakpoint_t tp
;
141 if (db_thread_break_init
== FALSE
) {
142 for (tp
= db_thread_break_list
;
143 tp
< &db_thread_break_list
[NTHREAD_LIST
-1]; tp
++)
146 db_free_thread_break_list
= db_thread_break_list
;
147 db_thread_break_init
= TRUE
;
149 if (db_free_thread_break_list
== 0)
151 tp
= db_free_thread_break_list
;
152 db_free_thread_break_list
= tp
->tb_next
;
153 tp
->tb_is_task
= task_bpt
;
154 tp
->tb_task_thd
= task_thd
;
155 tp
->tb_count
= count
;
156 tp
->tb_init_count
= count
;
158 tp
->tb_number
= ++db_breakpoint_number
;
159 tp
->tb_next
= bkpt
->threads
;
165 db_delete_thread_breakpoint(
166 register db_breakpoint_t bkpt
,
167 vm_offset_t task_thd
)
169 register db_thread_breakpoint_t tp
;
170 register db_thread_breakpoint_t
*tpp
;
173 /* delete all the thread-breakpoints */
175 for (tpp
= &bkpt
->threads
; (tp
= *tpp
) != 0; tpp
= &tp
->tb_next
)
178 *tpp
= db_free_thread_break_list
;
179 db_free_thread_break_list
= bkpt
->threads
;
183 /* delete the specified thread-breakpoint */
185 for (tpp
= &bkpt
->threads
; (tp
= *tpp
) != 0; tpp
= &tp
->tb_next
)
186 if (tp
->tb_task_thd
== task_thd
) {
189 tp
->tb_next
= db_free_thread_break_list
;
190 db_free_thread_break_list
= tp
;
194 return -1; /* not found */
198 static db_thread_breakpoint_t
199 db_find_thread_breakpoint(
200 db_breakpoint_t bkpt
,
203 register db_thread_breakpoint_t tp
;
204 register task_t task
=
205 (thr_act
== THREAD_NULL
)
206 ? TASK_NULL
: thr_act
->task
;
208 for (tp
= bkpt
->threads
; tp
; tp
= tp
->tb_next
) {
209 if (tp
->tb_is_task
) {
210 if (tp
->tb_task_thd
== (vm_offset_t
)task
)
214 if (tp
->tb_task_thd
== (vm_offset_t
)thr_act
|| tp
->tb_task_thd
== 0)
220 db_thread_breakpoint_t
221 db_find_thread_breakpoint_here(
225 db_breakpoint_t bkpt
;
227 bkpt
= db_find_breakpoint(task
, (db_addr_t
)addr
);
230 return(db_find_thread_breakpoint(bkpt
, current_thread()));
233 db_thread_breakpoint_t
234 db_find_breakpoint_number(
236 db_breakpoint_t
*bkptp
)
238 register db_thread_breakpoint_t tp
;
239 register db_breakpoint_t bkpt
;
241 for (bkpt
= db_breakpoint_list
; bkpt
!= 0; bkpt
= bkpt
->link
) {
242 for (tp
= bkpt
->threads
; tp
; tp
= tp
->tb_next
) {
243 if (tp
->tb_number
== num
) {
254 db_force_delete_breakpoint(
255 db_breakpoint_t bkpt
,
256 vm_offset_t task_thd
,
259 db_printf("deleted a stale breakpoint at ");
260 if (bkpt
->task
== TASK_NULL
|| db_lookup_task(bkpt
->task
) >= 0)
261 db_task_printsym(bkpt
->address
, DB_STGY_PROC
, bkpt
->task
);
263 db_printf("%#X", bkpt
->address
);
265 db_printf(" in task %X", bkpt
->task
);
267 db_printf(" for %s %X", (is_task
)? "task": "thr_act", task_thd
);
269 db_delete_thread_breakpoint(bkpt
, task_thd
);
273 db_check_breakpoint_valid(void)
275 register db_thread_breakpoint_t tbp
, tbp_next
;
276 register db_breakpoint_t bkpt
, *bkptp
;
278 bkptp
= &db_breakpoint_list
;
279 for (bkpt
= *bkptp
; bkpt
; bkpt
= *bkptp
) {
280 if (bkpt
->task
!= TASK_NULL
) {
281 if (db_lookup_task(bkpt
->task
) < 0) {
282 db_force_delete_breakpoint(bkpt
, 0, FALSE
);
284 db_breakpoint_free(bkpt
);
288 for (tbp
= bkpt
->threads
; tbp
; tbp
= tbp_next
) {
289 tbp_next
= tbp
->tb_next
;
290 if (tbp
->tb_task_thd
== 0)
292 if ((tbp
->tb_is_task
&&
293 db_lookup_task((task_t
)(tbp
->tb_task_thd
)) < 0) ||
295 db_lookup_act((thread_t
)(tbp
->tb_task_thd
)) < 0)) {
296 db_force_delete_breakpoint(bkpt
,
297 tbp
->tb_task_thd
, tbp
->tb_is_task
);
300 if (bkpt
->threads
== 0) {
301 db_put_task_value(bkpt
->address
, BKPT_SIZE
,
302 bkpt
->bkpt_inst
, bkpt
->task
);
304 db_breakpoint_free(bkpt
);
320 register db_breakpoint_t bkpt
;
321 db_breakpoint_t alloc_bkpt
= 0;
322 vm_offset_t task_thd
;
324 bkpt
= db_find_breakpoint(task
, addr
);
326 if (thr_act
== THREAD_NULL
327 || db_find_thread_breakpoint(bkpt
, thr_act
)) {
328 db_printf("Already set.\n");
332 if (!DB_CHECK_ACCESS(addr
, BKPT_SIZE
, task
)) {
334 db_printf("Warning: non-resident page for breakpoint at %llX",
335 (unsigned long long)addr
);
336 db_printf(" in task %lX.\n", task
);
338 db_printf("Cannot set breakpoint at %llX in kernel space.\n",
339 (unsigned long long)addr
);
343 alloc_bkpt
= bkpt
= db_breakpoint_alloc();
345 db_printf("Too many breakpoints.\n");
349 bkpt
->flags
= (task
&& thr_act
== THREAD_NULL
)?
350 (BKPT_USR_GLOBAL
|BKPT_1ST_SET
): 0;
351 bkpt
->address
= addr
;
354 if (db_breakpoint_list
== 0)
355 db_breakpoint_number
= 0;
356 task_thd
= (task_bpt
) ? (vm_offset_t
)(thr_act
->task
)
357 : (vm_offset_t
)thr_act
;
358 if (db_add_thread_breakpoint(bkpt
, task_thd
, count
, task_bpt
) < 0) {
360 db_breakpoint_free(alloc_bkpt
);
361 db_printf("Too many thread_breakpoints.\n");
363 db_printf("set breakpoint #%x\n", db_breakpoint_number
);
365 bkpt
->link
= db_breakpoint_list
;
366 db_breakpoint_list
= bkpt
;
372 db_delete_breakpoint(
375 vm_offset_t task_thd
)
377 register db_breakpoint_t bkpt
;
378 register db_breakpoint_t
*prev
;
380 for (prev
= &db_breakpoint_list
; (bkpt
= *prev
) != 0;
381 prev
= &bkpt
->link
) {
382 if ((bkpt
->task
== task
383 || (task
!= TASK_NULL
&& (bkpt
->flags
& BKPT_USR_GLOBAL
)))
384 && bkpt
->address
== addr
)
387 if (bkpt
&& (bkpt
->flags
& BKPT_SET_IN_MEM
)) {
388 db_printf("cannot delete it now.\n");
392 || db_delete_thread_breakpoint(bkpt
, task_thd
) < 0) {
393 db_printf("Not set.\n");
396 if (bkpt
->threads
== 0) {
398 db_breakpoint_free(bkpt
);
407 register db_breakpoint_t bkpt
;
409 for (bkpt
= db_breakpoint_list
; bkpt
!= 0; bkpt
= bkpt
->link
) {
410 if ((bkpt
->task
== task
411 || (task
!= TASK_NULL
&& (bkpt
->flags
& BKPT_USR_GLOBAL
)))
412 && bkpt
->address
== addr
)
419 db_find_breakpoint_here(
423 register db_breakpoint_t bkpt
;
425 for (bkpt
= db_breakpoint_list
; bkpt
!= 0; bkpt
= bkpt
->link
) {
426 if ((bkpt
->task
== task
427 || (task
!= TASK_NULL
&& (bkpt
->flags
& BKPT_USR_GLOBAL
)))
428 && bkpt
->address
== addr
)
430 if ((bkpt
->flags
& BKPT_USR_GLOBAL
) == 0 &&
431 DB_PHYS_EQ(task
, addr
, bkpt
->task
, bkpt
->address
))
437 boolean_t db_breakpoints_inserted
= TRUE
;
440 db_set_breakpoints(void)
442 register db_breakpoint_t bkpt
;
443 register task_t task
;
445 thread_t cur_act
= current_thread();
448 cur_act
->task
: TASK_NULL
;
449 boolean_t inserted
= TRUE
;
451 if (!db_breakpoints_inserted
) {
452 for (bkpt
= db_breakpoint_list
; bkpt
!= 0; bkpt
= bkpt
->link
) {
453 if (bkpt
->flags
& BKPT_SET_IN_MEM
)
456 if (bkpt
->flags
& BKPT_USR_GLOBAL
) {
457 if ((bkpt
->flags
& BKPT_1ST_SET
) == 0) {
458 if (cur_task
== TASK_NULL
)
462 bkpt
->flags
&= ~BKPT_1ST_SET
;
464 if (DB_CHECK_ACCESS(bkpt
->address
, BKPT_SIZE
, task
)) {
465 inst
= db_get_task_value(bkpt
->address
, BKPT_SIZE
, FALSE
,
467 if (inst
== BKPT_SET(inst
))
469 bkpt
->bkpt_inst
= inst
;
470 db_put_task_value(bkpt
->address
,
472 BKPT_SET(bkpt
->bkpt_inst
), task
);
473 bkpt
->flags
|= BKPT_SET_IN_MEM
;
478 db_breakpoints_inserted
= inserted
;
483 db_clear_breakpoints(void)
485 register db_breakpoint_t bkpt
, *bkptp
;
486 register task_t task
;
488 thread_t cur_act
= current_thread();
489 task_t cur_task
= (cur_act
) ?
490 cur_act
->task
: TASK_NULL
;
492 if (db_breakpoints_inserted
) {
493 bkptp
= &db_breakpoint_list
;
494 for (bkpt
= *bkptp
; bkpt
; bkpt
= *bkptp
) {
496 if (bkpt
->flags
& BKPT_USR_GLOBAL
) {
497 if (cur_task
== TASK_NULL
) {
503 if ((bkpt
->flags
& BKPT_SET_IN_MEM
)
504 && DB_CHECK_ACCESS(bkpt
->address
, BKPT_SIZE
, task
)) {
505 inst
= db_get_task_value(bkpt
->address
, BKPT_SIZE
, FALSE
,
507 if (inst
!= BKPT_SET(inst
)) {
508 if (bkpt
->flags
& BKPT_USR_GLOBAL
) {
512 db_force_delete_breakpoint(bkpt
, 0, FALSE
);
514 db_breakpoint_free(bkpt
);
517 db_put_task_value(bkpt
->address
, BKPT_SIZE
,
518 bkpt
->bkpt_inst
, task
);
519 bkpt
->flags
&= ~BKPT_SET_IN_MEM
;
523 db_breakpoints_inserted
= FALSE
;
528 * Set a temporary breakpoint.
529 * The instruction is changed immediately,
530 * so the breakpoint does not have to be on the breakpoint list.
533 db_set_temp_breakpoint(
537 register db_breakpoint_t bkpt
;
539 bkpt
= db_breakpoint_alloc();
541 db_printf("Too many breakpoints.\n");
545 bkpt
->address
= addr
;
546 bkpt
->flags
= BKPT_TEMP
;
548 if (db_add_thread_breakpoint(bkpt
, 0, 1, FALSE
) < 0) {
550 db_breakpoint_free(bkpt
);
551 db_printf("Too many thread_breakpoints.\n");
554 bkpt
->bkpt_inst
= db_get_task_value(bkpt
->address
, BKPT_SIZE
,
556 db_put_task_value(bkpt
->address
, BKPT_SIZE
,
557 BKPT_SET(bkpt
->bkpt_inst
), task
);
562 db_delete_temp_breakpoint(
564 db_breakpoint_t bkpt
)
566 db_put_task_value(bkpt
->address
, BKPT_SIZE
, bkpt
->bkpt_inst
, task
);
567 db_delete_thread_breakpoint(bkpt
, 0);
568 db_breakpoint_free(bkpt
);
575 db_list_breakpoints(void)
577 register db_breakpoint_t bkpt
;
579 if (db_breakpoint_list
== 0) {
580 db_printf("No breakpoints set\n");
584 db_printf(" No Space Task.Act Cnt Address(Cond)\n");
585 for (bkpt
= db_breakpoint_list
;
589 register db_thread_breakpoint_t tp
;
594 for (tp
= bkpt
->threads
; tp
; tp
= tp
->tb_next
) {
595 db_printf("%3d ", tp
->tb_number
);
596 if (bkpt
->flags
& BKPT_USR_GLOBAL
)
598 else if (bkpt
->task
== TASK_NULL
)
599 db_printf("kernel ");
600 else if ((task_id
= db_lookup_task(bkpt
->task
)) < 0)
601 db_printf("%0*X ", 2*sizeof(vm_offset_t
), bkpt
->task
);
603 db_printf("task%-3d ", task_id
);
604 if (tp
->tb_task_thd
== 0) {
607 if (tp
->tb_is_task
) {
608 task_id
= db_lookup_task((task_t
)(tp
->tb_task_thd
));
610 db_printf("%0*X ", 2*sizeof(vm_offset_t
),
613 db_printf("task%03d ", task_id
);
615 thread_t thd
= (thread_t
)(tp
->tb_task_thd
);
616 task_id
= db_lookup_task(thd
->task
);
617 act_id
= db_lookup_task_act(thd
->task
, thd
);
618 if (task_id
< 0 || act_id
< 0)
619 db_printf("%0*X ", 2*sizeof(vm_offset_t
),
622 db_printf("task%03d.%-3d ", task_id
, act_id
);
625 db_printf("%3d ", tp
->tb_init_count
);
626 db_task_printsym(bkpt
->address
, DB_STGY_PROC
, bkpt
->task
);
627 if (tp
->tb_cond
> 0) {
635 if (bkpt
->task
== TASK_NULL
)
636 db_printf(" ? kernel ");
638 db_printf("%*X ", 2*sizeof(vm_offset_t
), bkpt
->task
);
640 db_task_printsym(bkpt
->address
, DB_STGY_PROC
, bkpt
->task
);
647 db_delete_all_breakpoints(
650 register db_breakpoint_t bkpt
;
652 bkpt
= db_breakpoint_list
;
653 while ( bkpt
!= 0 ) {
654 if (bkpt
->task
== task
||
655 (task
!= TASK_NULL
&& (bkpt
->flags
& BKPT_USR_GLOBAL
))) {
656 db_delete_breakpoint(task
, bkpt
->address
, 0);
657 bkpt
= db_breakpoint_list
;
665 /* Delete breakpoint */
671 vm_offset_t task_thd
;
672 boolean_t user_global
= FALSE
;
673 boolean_t task_bpt
= FALSE
;
674 boolean_t user_space
= FALSE
;
675 boolean_t thd_bpt
= FALSE
;
683 db_printf("Bad modifier \"%s\"\n", db_tok_string
);
686 user_global
= db_option(db_tok_string
, 'U');
687 user_space
= (user_global
)? TRUE
: db_option(db_tok_string
, 'u');
688 task_bpt
= db_option(db_tok_string
, 'T');
689 thd_bpt
= db_option(db_tok_string
, 't');
690 if (task_bpt
&& user_global
)
691 db_error("Cannot specify both 'T' and 'U' option\n");
696 db_printf("Delete ALL breakpoints\n");
697 db_delete_all_breakpoints( (task_t
)task_bpt
);
702 db_thread_breakpoint_t tbp
;
703 db_breakpoint_t bkpt
= 0;
705 if (db_read_token() != tNUMBER
) {
706 db_printf("Bad break point number #%s\n", db_tok_string
);
709 if ((tbp
= db_find_breakpoint_number(db_tok_number
, &bkpt
)) == 0) {
710 db_printf("No such break point #%d\n", db_tok_number
);
713 db_delete_breakpoint(bkpt
->task
, bkpt
->address
, tbp
->tb_task_thd
);
717 if (!db_expression(&addr
)) {
719 * We attempt to pick up the user_space indication from db_dot,
720 * so that a plain "d" always works.
722 addr
= (db_expr_t
)db_dot
;
723 if (!user_space
&& !DB_VALID_ADDRESS(addr
, FALSE
))
726 if (!DB_VALID_ADDRESS(addr
, user_space
)) {
727 db_printf("Address %#llX is not in %s space\n", (unsigned long long)addr
,
728 (user_space
)? "user": "kernel");
731 if (thd_bpt
|| task_bpt
) {
732 for (n
= 0; db_get_next_act(&thr_act
, n
); n
++) {
733 if (thr_act
== THREAD_NULL
)
734 db_error("No active thr_act\n");
736 if (thr_act
->task
== TASK_NULL
)
737 db_error("No task\n");
738 task_thd
= (vm_offset_t
) (thr_act
->task
);
740 task_thd
= (user_global
)? 0: (vm_offset_t
) thr_act
;
741 db_delete_breakpoint(db_target_space(thr_act
, user_space
),
742 (db_addr_t
)addr
, task_thd
);
745 db_delete_breakpoint(db_target_space(THREAD_NULL
, user_space
),
750 /* Set breakpoint with skip count */
751 #include <mach/machine/vm_param.h>
754 db_breakpoint_cmd(db_expr_t addr
, __unused boolean_t have_addr
, db_expr_t count
,
759 boolean_t user_global
= db_option(modif
, 'U');
760 boolean_t task_bpt
= db_option(modif
, 'T');
761 boolean_t user_space
;
763 if (count
== (uint64_t)-1)
766 if (!task_bpt
&& db_option(modif
,'t'))
770 if (task_bpt
&& user_global
)
771 db_error("Cannot specify both 'T' and 'U'\n");
772 user_space
= (user_global
)? TRUE
: db_option(modif
, 'u');
773 if (user_space
&& db_access_level
< DB_ACCESS_CURRENT
)
774 db_error("User space break point is not supported\n");
775 if ((!task_bpt
|| !user_space
) &&
776 !DB_VALID_ADDRESS(addr
, user_space
)) {
777 /* if the user has explicitly specified user space,
778 do not insert a breakpoint into the kernel */
780 db_error("Invalid user space address\n");
782 db_printf("%#llX is in user space\n", (unsigned long long)addr
);
784 db_printf("kernel is from %#X to %#x\n", VM_MIN_KERNEL_ADDRESS
, vm_last_addr
);
786 db_printf("kernel is from %#X to %#x\n", VM_MIN_KERNEL_ADDRESS
, VM_MAX_KERNEL_ADDRESS
);
789 if (db_option(modif
, 't') || task_bpt
) {
790 for (n
= 0; db_get_next_act(&thr_act
, n
); n
++) {
791 if (thr_act
== THREAD_NULL
)
792 db_error("No active thr_act\n");
793 if (task_bpt
&& thr_act
->task
== TASK_NULL
)
794 db_error("No task\n");
795 if (db_access_level
<= DB_ACCESS_CURRENT
&& user_space
796 && thr_act
->task
!= db_current_space())
797 db_error("Cannot set break point in inactive user space\n");
798 db_set_breakpoint(db_target_space(thr_act
, user_space
),
799 (db_addr_t
)addr
, count
,
800 (user_global
)? THREAD_NULL
: thr_act
,
804 db_set_breakpoint(db_target_space(THREAD_NULL
, user_space
),
806 count
, THREAD_NULL
, FALSE
);
810 /* list breakpoints */
812 db_listbreak_cmd(__unused db_expr_t addr
, __unused boolean_t have_addr
,
813 __unused db_expr_t count
, __unused
char *modif
)
815 db_list_breakpoints();