2 * Copyright (c) 2000-2004 Apple Computer, Inc. All rights reserved.
4 * @APPLE_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. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
21 * @APPLE_LICENSE_HEADER_END@
29 * Author: David B. Golub, Carnegie Mellon University
36 #include <mach/boolean.h>
37 #include <machine/db_machdep.h>
38 #include <ddb/db_lex.h>
39 #include <ddb/db_break.h>
40 #include <ddb/db_access.h>
41 #include <ddb/db_sym.h>
42 #include <ddb/db_variables.h>
43 #include <ddb/db_command.h>
44 #include <ddb/db_cond.h>
45 #include <ddb/db_expr.h>
46 #include <ddb/db_output.h> /* For db_printf() */
47 #include <ddb/db_task_thread.h>
50 #define NBREAKPOINTS 100
51 #define NTHREAD_LIST (NBREAKPOINTS*3)
53 struct db_breakpoint db_break_table
[NBREAKPOINTS
];
54 db_breakpoint_t db_next_free_breakpoint
= &db_break_table
[0];
55 db_breakpoint_t db_free_breakpoints
= 0;
56 db_breakpoint_t db_breakpoint_list
= 0;
58 static struct db_thread_breakpoint db_thread_break_list
[NTHREAD_LIST
];
59 static db_thread_breakpoint_t db_free_thread_break_list
= 0;
60 static boolean_t db_thread_break_init
= FALSE
;
61 static int db_breakpoint_number
= 0;
63 /* Prototypes for functions local to this file. XXX -- should be static!
65 static int db_add_thread_breakpoint(
66 register db_breakpoint_t bkpt
,
71 static int db_delete_thread_breakpoint(
72 register db_breakpoint_t bkpt
,
73 vm_offset_t task_thd
);
75 static db_thread_breakpoint_t
db_find_thread_breakpoint(
79 static void db_force_delete_breakpoint(
84 db_breakpoint_t
db_breakpoint_alloc(void);
86 void db_breakpoint_free(register db_breakpoint_t bkpt
);
88 void db_delete_breakpoint(
91 vm_offset_t task_thd
);
94 db_delete_all_breakpoints(
97 void db_list_breakpoints(void);
102 db_breakpoint_alloc(void)
104 register db_breakpoint_t bkpt
;
106 if ((bkpt
= db_free_breakpoints
) != 0) {
107 db_free_breakpoints
= bkpt
->link
;
110 if (db_next_free_breakpoint
== &db_break_table
[NBREAKPOINTS
]) {
111 db_printf("All breakpoints used.\n");
114 bkpt
= db_next_free_breakpoint
;
115 db_next_free_breakpoint
++;
121 db_breakpoint_free(register db_breakpoint_t bkpt
)
123 bkpt
->link
= db_free_breakpoints
;
124 db_free_breakpoints
= bkpt
;
128 db_add_thread_breakpoint(
129 register db_breakpoint_t bkpt
,
130 vm_offset_t task_thd
,
134 register db_thread_breakpoint_t tp
;
136 if (db_thread_break_init
== FALSE
) {
137 for (tp
= db_thread_break_list
;
138 tp
< &db_thread_break_list
[NTHREAD_LIST
-1]; tp
++)
141 db_free_thread_break_list
= db_thread_break_list
;
142 db_thread_break_init
= TRUE
;
144 if (db_free_thread_break_list
== 0)
146 tp
= db_free_thread_break_list
;
147 db_free_thread_break_list
= tp
->tb_next
;
148 tp
->tb_is_task
= task_bpt
;
149 tp
->tb_task_thd
= task_thd
;
150 tp
->tb_count
= count
;
151 tp
->tb_init_count
= count
;
153 tp
->tb_number
= ++db_breakpoint_number
;
154 tp
->tb_next
= bkpt
->threads
;
160 db_delete_thread_breakpoint(
161 register db_breakpoint_t bkpt
,
162 vm_offset_t task_thd
)
164 register db_thread_breakpoint_t tp
;
165 register db_thread_breakpoint_t
*tpp
;
168 /* delete all the thread-breakpoints */
170 for (tpp
= &bkpt
->threads
; (tp
= *tpp
) != 0; tpp
= &tp
->tb_next
)
173 *tpp
= db_free_thread_break_list
;
174 db_free_thread_break_list
= bkpt
->threads
;
178 /* delete the specified thread-breakpoint */
180 for (tpp
= &bkpt
->threads
; (tp
= *tpp
) != 0; tpp
= &tp
->tb_next
)
181 if (tp
->tb_task_thd
== task_thd
) {
184 tp
->tb_next
= db_free_thread_break_list
;
185 db_free_thread_break_list
= tp
;
189 return -1; /* not found */
193 static db_thread_breakpoint_t
194 db_find_thread_breakpoint(
195 db_breakpoint_t bkpt
,
198 register db_thread_breakpoint_t tp
;
199 register task_t task
=
200 (thr_act
== THREAD_NULL
)
201 ? TASK_NULL
: thr_act
->task
;
203 for (tp
= bkpt
->threads
; tp
; tp
= tp
->tb_next
) {
204 if (tp
->tb_is_task
) {
205 if (tp
->tb_task_thd
== (vm_offset_t
)task
)
209 if (tp
->tb_task_thd
== (vm_offset_t
)thr_act
|| tp
->tb_task_thd
== 0)
215 db_thread_breakpoint_t
216 db_find_thread_breakpoint_here(
220 db_breakpoint_t bkpt
;
222 bkpt
= db_find_breakpoint(task
, (db_addr_t
)addr
);
225 return(db_find_thread_breakpoint(bkpt
, current_act()));
228 db_thread_breakpoint_t
229 db_find_breakpoint_number(
231 db_breakpoint_t
*bkptp
)
233 register db_thread_breakpoint_t tp
;
234 register db_breakpoint_t bkpt
;
236 for (bkpt
= db_breakpoint_list
; bkpt
!= 0; bkpt
= bkpt
->link
) {
237 for (tp
= bkpt
->threads
; tp
; tp
= tp
->tb_next
) {
238 if (tp
->tb_number
== num
) {
249 db_force_delete_breakpoint(
250 db_breakpoint_t bkpt
,
251 vm_offset_t task_thd
,
254 db_printf("deleted a stale breakpoint at ");
255 if (bkpt
->task
== TASK_NULL
|| db_lookup_task(bkpt
->task
) >= 0)
256 db_task_printsym(bkpt
->address
, DB_STGY_PROC
, bkpt
->task
);
258 db_printf("%#X", bkpt
->address
);
260 db_printf(" in task %X", bkpt
->task
);
262 db_printf(" for %s %X", (is_task
)? "task": "thr_act", task_thd
);
264 db_delete_thread_breakpoint(bkpt
, task_thd
);
268 db_check_breakpoint_valid(void)
270 register db_thread_breakpoint_t tbp
, tbp_next
;
271 register db_breakpoint_t bkpt
, *bkptp
;
273 bkptp
= &db_breakpoint_list
;
274 for (bkpt
= *bkptp
; bkpt
; bkpt
= *bkptp
) {
275 if (bkpt
->task
!= TASK_NULL
) {
276 if (db_lookup_task(bkpt
->task
) < 0) {
277 db_force_delete_breakpoint(bkpt
, 0, FALSE
);
279 db_breakpoint_free(bkpt
);
283 for (tbp
= bkpt
->threads
; tbp
; tbp
= tbp_next
) {
284 tbp_next
= tbp
->tb_next
;
285 if (tbp
->tb_task_thd
== 0)
287 if ((tbp
->tb_is_task
&&
288 db_lookup_task((task_t
)(tbp
->tb_task_thd
)) < 0) ||
290 db_lookup_act((thread_t
)(tbp
->tb_task_thd
)) < 0)) {
291 db_force_delete_breakpoint(bkpt
,
292 tbp
->tb_task_thd
, tbp
->tb_is_task
);
295 if (bkpt
->threads
== 0) {
296 db_put_task_value(bkpt
->address
, BKPT_SIZE
,
297 bkpt
->bkpt_inst
, bkpt
->task
);
299 db_breakpoint_free(bkpt
);
315 register db_breakpoint_t bkpt
;
316 db_breakpoint_t alloc_bkpt
= 0;
317 vm_offset_t task_thd
;
319 bkpt
= db_find_breakpoint(task
, addr
);
321 if (thr_act
== THREAD_NULL
322 || db_find_thread_breakpoint(bkpt
, thr_act
)) {
323 db_printf("Already set.\n");
327 if (!DB_CHECK_ACCESS(addr
, BKPT_SIZE
, task
)) {
329 db_printf("Warning: non-resident page for breakpoint at %llX",
330 (unsigned long long)addr
);
331 db_printf(" in task %lX.\n", task
);
333 db_printf("Cannot set breakpoint at %llX in kernel space.\n",
334 (unsigned long long)addr
);
338 alloc_bkpt
= bkpt
= db_breakpoint_alloc();
340 db_printf("Too many breakpoints.\n");
344 bkpt
->flags
= (task
&& thr_act
== THREAD_NULL
)?
345 (BKPT_USR_GLOBAL
|BKPT_1ST_SET
): 0;
346 bkpt
->address
= addr
;
349 if (db_breakpoint_list
== 0)
350 db_breakpoint_number
= 0;
351 task_thd
= (task_bpt
) ? (vm_offset_t
)(thr_act
->task
)
352 : (vm_offset_t
)thr_act
;
353 if (db_add_thread_breakpoint(bkpt
, task_thd
, count
, task_bpt
) < 0) {
355 db_breakpoint_free(alloc_bkpt
);
356 db_printf("Too many thread_breakpoints.\n");
358 db_printf("set breakpoint #%x\n", db_breakpoint_number
);
360 bkpt
->link
= db_breakpoint_list
;
361 db_breakpoint_list
= bkpt
;
367 db_delete_breakpoint(
370 vm_offset_t task_thd
)
372 register db_breakpoint_t bkpt
;
373 register db_breakpoint_t
*prev
;
375 for (prev
= &db_breakpoint_list
; (bkpt
= *prev
) != 0;
376 prev
= &bkpt
->link
) {
377 if ((bkpt
->task
== task
378 || (task
!= TASK_NULL
&& (bkpt
->flags
& BKPT_USR_GLOBAL
)))
379 && bkpt
->address
== addr
)
382 if (bkpt
&& (bkpt
->flags
& BKPT_SET_IN_MEM
)) {
383 db_printf("cannot delete it now.\n");
387 || db_delete_thread_breakpoint(bkpt
, task_thd
) < 0) {
388 db_printf("Not set.\n");
391 if (bkpt
->threads
== 0) {
393 db_breakpoint_free(bkpt
);
402 register db_breakpoint_t bkpt
;
404 for (bkpt
= db_breakpoint_list
; bkpt
!= 0; bkpt
= bkpt
->link
) {
405 if ((bkpt
->task
== task
406 || (task
!= TASK_NULL
&& (bkpt
->flags
& BKPT_USR_GLOBAL
)))
407 && bkpt
->address
== addr
)
414 db_find_breakpoint_here(
418 register db_breakpoint_t bkpt
;
420 for (bkpt
= db_breakpoint_list
; bkpt
!= 0; bkpt
= bkpt
->link
) {
421 if ((bkpt
->task
== task
422 || (task
!= TASK_NULL
&& (bkpt
->flags
& BKPT_USR_GLOBAL
)))
423 && bkpt
->address
== addr
)
425 if ((bkpt
->flags
& BKPT_USR_GLOBAL
) == 0 &&
426 DB_PHYS_EQ(task
, addr
, bkpt
->task
, bkpt
->address
))
432 boolean_t db_breakpoints_inserted
= TRUE
;
435 db_set_breakpoints(void)
437 register db_breakpoint_t bkpt
;
438 register task_t task
;
440 thread_t cur_act
= current_act();
443 cur_act
->task
: TASK_NULL
;
444 boolean_t inserted
= TRUE
;
446 if (!db_breakpoints_inserted
) {
447 for (bkpt
= db_breakpoint_list
; bkpt
!= 0; bkpt
= bkpt
->link
) {
448 if (bkpt
->flags
& BKPT_SET_IN_MEM
)
451 if (bkpt
->flags
& BKPT_USR_GLOBAL
) {
452 if ((bkpt
->flags
& BKPT_1ST_SET
) == 0) {
453 if (cur_task
== TASK_NULL
)
457 bkpt
->flags
&= ~BKPT_1ST_SET
;
459 if (DB_CHECK_ACCESS(bkpt
->address
, BKPT_SIZE
, task
)) {
460 inst
= db_get_task_value(bkpt
->address
, BKPT_SIZE
, FALSE
,
462 if (inst
== BKPT_SET(inst
))
464 bkpt
->bkpt_inst
= inst
;
465 db_put_task_value(bkpt
->address
,
467 BKPT_SET(bkpt
->bkpt_inst
), task
);
468 bkpt
->flags
|= BKPT_SET_IN_MEM
;
473 db_breakpoints_inserted
= inserted
;
478 db_clear_breakpoints(void)
480 register db_breakpoint_t bkpt
, *bkptp
;
481 register task_t task
;
483 thread_t cur_act
= current_act();
484 task_t cur_task
= (cur_act
) ?
485 cur_act
->task
: TASK_NULL
;
487 if (db_breakpoints_inserted
) {
488 bkptp
= &db_breakpoint_list
;
489 for (bkpt
= *bkptp
; bkpt
; bkpt
= *bkptp
) {
491 if (bkpt
->flags
& BKPT_USR_GLOBAL
) {
492 if (cur_task
== TASK_NULL
) {
498 if ((bkpt
->flags
& BKPT_SET_IN_MEM
)
499 && DB_CHECK_ACCESS(bkpt
->address
, BKPT_SIZE
, task
)) {
500 inst
= db_get_task_value(bkpt
->address
, BKPT_SIZE
, FALSE
,
502 if (inst
!= BKPT_SET(inst
)) {
503 if (bkpt
->flags
& BKPT_USR_GLOBAL
) {
507 db_force_delete_breakpoint(bkpt
, 0, FALSE
);
509 db_breakpoint_free(bkpt
);
512 db_put_task_value(bkpt
->address
, BKPT_SIZE
,
513 bkpt
->bkpt_inst
, task
);
514 bkpt
->flags
&= ~BKPT_SET_IN_MEM
;
518 db_breakpoints_inserted
= FALSE
;
523 * Set a temporary breakpoint.
524 * The instruction is changed immediately,
525 * so the breakpoint does not have to be on the breakpoint list.
528 db_set_temp_breakpoint(
532 register db_breakpoint_t bkpt
;
534 bkpt
= db_breakpoint_alloc();
536 db_printf("Too many breakpoints.\n");
540 bkpt
->address
= addr
;
541 bkpt
->flags
= BKPT_TEMP
;
543 if (db_add_thread_breakpoint(bkpt
, 0, 1, FALSE
) < 0) {
545 db_breakpoint_free(bkpt
);
546 db_printf("Too many thread_breakpoints.\n");
549 bkpt
->bkpt_inst
= db_get_task_value(bkpt
->address
, BKPT_SIZE
,
551 db_put_task_value(bkpt
->address
, BKPT_SIZE
,
552 BKPT_SET(bkpt
->bkpt_inst
), task
);
557 db_delete_temp_breakpoint(
559 db_breakpoint_t bkpt
)
561 db_put_task_value(bkpt
->address
, BKPT_SIZE
, bkpt
->bkpt_inst
, task
);
562 db_delete_thread_breakpoint(bkpt
, 0);
563 db_breakpoint_free(bkpt
);
570 db_list_breakpoints(void)
572 register db_breakpoint_t bkpt
;
574 if (db_breakpoint_list
== 0) {
575 db_printf("No breakpoints set\n");
579 db_printf(" No Space Task.Act Cnt Address(Cond)\n");
580 for (bkpt
= db_breakpoint_list
;
584 register db_thread_breakpoint_t tp
;
589 for (tp
= bkpt
->threads
; tp
; tp
= tp
->tb_next
) {
590 db_printf("%3d ", tp
->tb_number
);
591 if (bkpt
->flags
& BKPT_USR_GLOBAL
)
593 else if (bkpt
->task
== TASK_NULL
)
594 db_printf("kernel ");
595 else if ((task_id
= db_lookup_task(bkpt
->task
)) < 0)
596 db_printf("%0*X ", 2*sizeof(vm_offset_t
), bkpt
->task
);
598 db_printf("task%-3d ", task_id
);
599 if (tp
->tb_task_thd
== 0) {
602 if (tp
->tb_is_task
) {
603 task_id
= db_lookup_task((task_t
)(tp
->tb_task_thd
));
605 db_printf("%0*X ", 2*sizeof(vm_offset_t
),
608 db_printf("task%03d ", task_id
);
610 thread_t thd
= (thread_t
)(tp
->tb_task_thd
);
611 task_id
= db_lookup_task(thd
->task
);
612 act_id
= db_lookup_task_act(thd
->task
, thd
);
613 if (task_id
< 0 || act_id
< 0)
614 db_printf("%0*X ", 2*sizeof(vm_offset_t
),
617 db_printf("task%03d.%-3d ", task_id
, act_id
);
620 db_printf("%3d ", tp
->tb_init_count
);
621 db_task_printsym(bkpt
->address
, DB_STGY_PROC
, bkpt
->task
);
622 if (tp
->tb_cond
> 0) {
630 if (bkpt
->task
== TASK_NULL
)
631 db_printf(" ? kernel ");
633 db_printf("%*X ", 2*sizeof(vm_offset_t
), bkpt
->task
);
635 db_task_printsym(bkpt
->address
, DB_STGY_PROC
, bkpt
->task
);
642 db_delete_all_breakpoints(
645 register db_breakpoint_t bkpt
;
647 bkpt
= db_breakpoint_list
;
648 while ( bkpt
!= 0 ) {
649 if (bkpt
->task
== task
||
650 (task
!= TASK_NULL
&& (bkpt
->flags
& BKPT_USR_GLOBAL
))) {
651 db_delete_breakpoint(task
, bkpt
->address
, 0);
652 bkpt
= db_breakpoint_list
;
660 /* Delete breakpoint */
666 vm_offset_t task_thd
;
667 boolean_t user_global
= FALSE
;
668 boolean_t task_bpt
= FALSE
;
669 boolean_t user_space
= FALSE
;
670 boolean_t thd_bpt
= FALSE
;
678 db_printf("Bad modifier \"%s\"\n", db_tok_string
);
681 user_global
= db_option(db_tok_string
, 'U');
682 user_space
= (user_global
)? TRUE
: db_option(db_tok_string
, 'u');
683 task_bpt
= db_option(db_tok_string
, 'T');
684 thd_bpt
= db_option(db_tok_string
, 't');
685 if (task_bpt
&& user_global
)
686 db_error("Cannot specify both 'T' and 'U' option\n");
691 db_printf("Delete ALL breakpoints\n");
692 db_delete_all_breakpoints( (task_t
)task_bpt
);
697 db_thread_breakpoint_t tbp
;
698 db_breakpoint_t bkpt
;
700 if (db_read_token() != tNUMBER
) {
701 db_printf("Bad break point number #%s\n", db_tok_string
);
704 if ((tbp
= db_find_breakpoint_number(db_tok_number
, &bkpt
)) == 0) {
705 db_printf("No such break point #%d\n", db_tok_number
);
708 db_delete_breakpoint(bkpt
->task
, bkpt
->address
, tbp
->tb_task_thd
);
712 if (!db_expression(&addr
)) {
714 * We attempt to pick up the user_space indication from db_dot,
715 * so that a plain "d" always works.
717 addr
= (db_expr_t
)db_dot
;
718 if (!user_space
&& !DB_VALID_ADDRESS(addr
, FALSE
))
721 if (!DB_VALID_ADDRESS(addr
, user_space
)) {
722 db_printf("Address %#llX is not in %s space\n", (unsigned long long)addr
,
723 (user_space
)? "user": "kernel");
726 if (thd_bpt
|| task_bpt
) {
727 for (n
= 0; db_get_next_act(&thr_act
, n
); n
++) {
728 if (thr_act
== THREAD_NULL
)
729 db_error("No active thr_act\n");
731 if (thr_act
->task
== TASK_NULL
)
732 db_error("No task\n");
733 task_thd
= (vm_offset_t
) (thr_act
->task
);
735 task_thd
= (user_global
)? 0: (vm_offset_t
) thr_act
;
736 db_delete_breakpoint(db_target_space(thr_act
, user_space
),
737 (db_addr_t
)addr
, task_thd
);
740 db_delete_breakpoint(db_target_space(THREAD_NULL
, user_space
),
745 /* Set breakpoint with skip count */
746 #include <mach/machine/vm_param.h>
757 boolean_t user_global
= db_option(modif
, 'U');
758 boolean_t task_bpt
= db_option(modif
, 'T');
759 boolean_t user_space
;
764 if (!task_bpt
&& db_option(modif
,'t'))
768 if (task_bpt
&& user_global
)
769 db_error("Cannot specify both 'T' and 'U'\n");
770 user_space
= (user_global
)? TRUE
: db_option(modif
, 'u');
771 if (user_space
&& db_access_level
< DB_ACCESS_CURRENT
)
772 db_error("User space break point is not supported\n");
773 if ((!task_bpt
|| !user_space
) &&
774 !DB_VALID_ADDRESS(addr
, user_space
)) {
775 /* if the user has explicitly specified user space,
776 do not insert a breakpoint into the kernel */
778 db_error("Invalid user space address\n");
780 db_printf("%#llX is in user space\n", (unsigned long long)addr
);
782 db_printf("kernel is from %#X to %#x\n", VM_MIN_KERNEL_ADDRESS
, vm_last_addr
);
784 db_printf("kernel is from %#X to %#x\n", VM_MIN_KERNEL_ADDRESS
, VM_MAX_KERNEL_ADDRESS
);
787 if (db_option(modif
, 't') || task_bpt
) {
788 for (n
= 0; db_get_next_act(&thr_act
, n
); n
++) {
789 if (thr_act
== THREAD_NULL
)
790 db_error("No active thr_act\n");
791 if (task_bpt
&& thr_act
->task
== TASK_NULL
)
792 db_error("No task\n");
793 if (db_access_level
<= DB_ACCESS_CURRENT
&& user_space
794 && thr_act
->task
!= db_current_space())
795 db_error("Cannot set break point in inactive user space\n");
796 db_set_breakpoint(db_target_space(thr_act
, user_space
),
797 (db_addr_t
)addr
, count
,
798 (user_global
)? THREAD_NULL
: thr_act
,
802 db_set_breakpoint(db_target_space(THREAD_NULL
, user_space
),
804 count
, THREAD_NULL
, FALSE
);
808 /* list breakpoints */
810 db_listbreak_cmd(void)
812 db_list_breakpoints();