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@
31 * Author: David B. Golub, Carnegie Mellon University
38 #include <mach/boolean.h>
39 #include <machine/db_machdep.h>
40 #include <ddb/db_lex.h>
41 #include <ddb/db_break.h>
42 #include <ddb/db_access.h>
43 #include <ddb/db_sym.h>
44 #include <ddb/db_variables.h>
45 #include <ddb/db_command.h>
46 #include <ddb/db_cond.h>
47 #include <ddb/db_expr.h>
48 #include <ddb/db_output.h> /* For db_printf() */
49 #include <ddb/db_task_thread.h>
52 #define NBREAKPOINTS 100
53 #define NTHREAD_LIST (NBREAKPOINTS*3)
55 struct db_breakpoint db_break_table
[NBREAKPOINTS
];
56 db_breakpoint_t db_next_free_breakpoint
= &db_break_table
[0];
57 db_breakpoint_t db_free_breakpoints
= 0;
58 db_breakpoint_t db_breakpoint_list
= 0;
60 static struct db_thread_breakpoint db_thread_break_list
[NTHREAD_LIST
];
61 static db_thread_breakpoint_t db_free_thread_break_list
= 0;
62 static boolean_t db_thread_break_init
= FALSE
;
63 static int db_breakpoint_number
= 0;
65 /* Prototypes for functions local to this file. XXX -- should be static!
67 static int db_add_thread_breakpoint(
68 register db_breakpoint_t bkpt
,
73 static int db_delete_thread_breakpoint(
74 register db_breakpoint_t bkpt
,
75 vm_offset_t task_thd
);
77 static db_thread_breakpoint_t
db_find_thread_breakpoint(
79 thread_act_t thr_act
);
81 static void db_force_delete_breakpoint(
86 db_breakpoint_t
db_breakpoint_alloc(void);
88 void db_breakpoint_free(register db_breakpoint_t bkpt
);
90 void db_delete_breakpoint(
93 vm_offset_t task_thd
);
96 db_delete_all_breakpoints(
99 void db_list_breakpoints(void);
104 db_breakpoint_alloc(void)
106 register db_breakpoint_t bkpt
;
108 if ((bkpt
= db_free_breakpoints
) != 0) {
109 db_free_breakpoints
= bkpt
->link
;
112 if (db_next_free_breakpoint
== &db_break_table
[NBREAKPOINTS
]) {
113 db_printf("All breakpoints used.\n");
116 bkpt
= db_next_free_breakpoint
;
117 db_next_free_breakpoint
++;
123 db_breakpoint_free(register db_breakpoint_t bkpt
)
125 bkpt
->link
= db_free_breakpoints
;
126 db_free_breakpoints
= bkpt
;
130 db_add_thread_breakpoint(
131 register db_breakpoint_t bkpt
,
132 vm_offset_t task_thd
,
136 register db_thread_breakpoint_t tp
;
138 if (db_thread_break_init
== FALSE
) {
139 for (tp
= db_thread_break_list
;
140 tp
< &db_thread_break_list
[NTHREAD_LIST
-1]; tp
++)
143 db_free_thread_break_list
= db_thread_break_list
;
144 db_thread_break_init
= TRUE
;
146 if (db_free_thread_break_list
== 0)
148 tp
= db_free_thread_break_list
;
149 db_free_thread_break_list
= tp
->tb_next
;
150 tp
->tb_is_task
= task_bpt
;
151 tp
->tb_task_thd
= task_thd
;
152 tp
->tb_count
= count
;
153 tp
->tb_init_count
= count
;
155 tp
->tb_number
= ++db_breakpoint_number
;
156 tp
->tb_next
= bkpt
->threads
;
162 db_delete_thread_breakpoint(
163 register db_breakpoint_t bkpt
,
164 vm_offset_t task_thd
)
166 register db_thread_breakpoint_t tp
;
167 register db_thread_breakpoint_t
*tpp
;
170 /* delete all the thread-breakpoints */
172 for (tpp
= &bkpt
->threads
; (tp
= *tpp
) != 0; tpp
= &tp
->tb_next
)
175 *tpp
= db_free_thread_break_list
;
176 db_free_thread_break_list
= bkpt
->threads
;
180 /* delete the specified thread-breakpoint */
182 for (tpp
= &bkpt
->threads
; (tp
= *tpp
) != 0; tpp
= &tp
->tb_next
)
183 if (tp
->tb_task_thd
== task_thd
) {
186 tp
->tb_next
= db_free_thread_break_list
;
187 db_free_thread_break_list
= tp
;
191 return -1; /* not found */
195 static db_thread_breakpoint_t
196 db_find_thread_breakpoint(
197 db_breakpoint_t bkpt
,
198 thread_act_t thr_act
)
200 register db_thread_breakpoint_t tp
;
201 register task_t task
=
202 (thr_act
== THR_ACT_NULL
)
203 ? TASK_NULL
: thr_act
->task
;
205 for (tp
= bkpt
->threads
; tp
; tp
= tp
->tb_next
) {
206 if (tp
->tb_is_task
) {
207 if (tp
->tb_task_thd
== (vm_offset_t
)task
)
211 if (tp
->tb_task_thd
== (vm_offset_t
)thr_act
|| tp
->tb_task_thd
== 0)
217 db_thread_breakpoint_t
218 db_find_thread_breakpoint_here(
222 db_breakpoint_t bkpt
;
224 bkpt
= db_find_breakpoint(task
, (db_addr_t
)addr
);
227 return(db_find_thread_breakpoint(bkpt
, current_act()));
230 db_thread_breakpoint_t
231 db_find_breakpoint_number(
233 db_breakpoint_t
*bkptp
)
235 register db_thread_breakpoint_t tp
;
236 register db_breakpoint_t bkpt
;
238 for (bkpt
= db_breakpoint_list
; bkpt
!= 0; bkpt
= bkpt
->link
) {
239 for (tp
= bkpt
->threads
; tp
; tp
= tp
->tb_next
) {
240 if (tp
->tb_number
== num
) {
251 db_force_delete_breakpoint(
252 db_breakpoint_t bkpt
,
253 vm_offset_t task_thd
,
256 db_printf("deleted a stale breakpoint at ");
257 if (bkpt
->task
== TASK_NULL
|| db_lookup_task(bkpt
->task
) >= 0)
258 db_task_printsym(bkpt
->address
, DB_STGY_PROC
, bkpt
->task
);
260 db_printf("%#X", bkpt
->address
);
262 db_printf(" in task %X", bkpt
->task
);
264 db_printf(" for %s %X", (is_task
)? "task": "thr_act", task_thd
);
266 db_delete_thread_breakpoint(bkpt
, task_thd
);
270 db_check_breakpoint_valid(void)
272 register db_thread_breakpoint_t tbp
, tbp_next
;
273 register db_breakpoint_t bkpt
, *bkptp
;
275 bkptp
= &db_breakpoint_list
;
276 for (bkpt
= *bkptp
; bkpt
; bkpt
= *bkptp
) {
277 if (bkpt
->task
!= TASK_NULL
) {
278 if (db_lookup_task(bkpt
->task
) < 0) {
279 db_force_delete_breakpoint(bkpt
, 0, FALSE
);
281 db_breakpoint_free(bkpt
);
285 for (tbp
= bkpt
->threads
; tbp
; tbp
= tbp_next
) {
286 tbp_next
= tbp
->tb_next
;
287 if (tbp
->tb_task_thd
== 0)
289 if ((tbp
->tb_is_task
&&
290 db_lookup_task((task_t
)(tbp
->tb_task_thd
)) < 0) ||
292 db_lookup_act((thread_act_t
)(tbp
->tb_task_thd
)) < 0)) {
293 db_force_delete_breakpoint(bkpt
,
294 tbp
->tb_task_thd
, tbp
->tb_is_task
);
297 if (bkpt
->threads
== 0) {
298 db_put_task_value(bkpt
->address
, BKPT_SIZE
,
299 bkpt
->bkpt_inst
, bkpt
->task
);
301 db_breakpoint_free(bkpt
);
314 thread_act_t thr_act
,
317 register db_breakpoint_t bkpt
;
318 db_breakpoint_t alloc_bkpt
= 0;
319 vm_offset_t task_thd
;
321 bkpt
= db_find_breakpoint(task
, addr
);
323 if (thr_act
== THR_ACT_NULL
324 || db_find_thread_breakpoint(bkpt
, thr_act
)) {
325 db_printf("Already set.\n");
329 if (!DB_CHECK_ACCESS(addr
, BKPT_SIZE
, task
)) {
331 db_printf("Warning: non-resident page for breakpoint at %lX",
333 db_printf(" in task %lX.\n", task
);
335 db_printf("Cannot set breakpoint at %lX in kernel space.\n",
340 alloc_bkpt
= bkpt
= db_breakpoint_alloc();
342 db_printf("Too many breakpoints.\n");
346 bkpt
->flags
= (task
&& thr_act
== THR_ACT_NULL
)?
347 (BKPT_USR_GLOBAL
|BKPT_1ST_SET
): 0;
348 bkpt
->address
= addr
;
351 if (db_breakpoint_list
== 0)
352 db_breakpoint_number
= 0;
353 task_thd
= (task_bpt
) ? (vm_offset_t
)(thr_act
->task
)
354 : (vm_offset_t
)thr_act
;
355 if (db_add_thread_breakpoint(bkpt
, task_thd
, count
, task_bpt
) < 0) {
357 db_breakpoint_free(alloc_bkpt
);
358 db_printf("Too many thread_breakpoints.\n");
360 db_printf("set breakpoint #%x\n", db_breakpoint_number
);
362 bkpt
->link
= db_breakpoint_list
;
363 db_breakpoint_list
= bkpt
;
369 db_delete_breakpoint(
372 vm_offset_t task_thd
)
374 register db_breakpoint_t bkpt
;
375 register db_breakpoint_t
*prev
;
377 for (prev
= &db_breakpoint_list
; (bkpt
= *prev
) != 0;
378 prev
= &bkpt
->link
) {
379 if ((bkpt
->task
== task
380 || (task
!= TASK_NULL
&& (bkpt
->flags
& BKPT_USR_GLOBAL
)))
381 && bkpt
->address
== addr
)
384 if (bkpt
&& (bkpt
->flags
& BKPT_SET_IN_MEM
)) {
385 db_printf("cannot delete it now.\n");
389 || db_delete_thread_breakpoint(bkpt
, task_thd
) < 0) {
390 db_printf("Not set.\n");
393 if (bkpt
->threads
== 0) {
395 db_breakpoint_free(bkpt
);
404 register db_breakpoint_t bkpt
;
406 for (bkpt
= db_breakpoint_list
; bkpt
!= 0; bkpt
= bkpt
->link
) {
407 if ((bkpt
->task
== task
408 || (task
!= TASK_NULL
&& (bkpt
->flags
& BKPT_USR_GLOBAL
)))
409 && bkpt
->address
== addr
)
416 db_find_breakpoint_here(
420 register db_breakpoint_t bkpt
;
422 for (bkpt
= db_breakpoint_list
; bkpt
!= 0; bkpt
= bkpt
->link
) {
423 if ((bkpt
->task
== task
424 || (task
!= TASK_NULL
&& (bkpt
->flags
& BKPT_USR_GLOBAL
)))
425 && bkpt
->address
== addr
)
427 if ((bkpt
->flags
& BKPT_USR_GLOBAL
) == 0 &&
428 DB_PHYS_EQ(task
, addr
, bkpt
->task
, bkpt
->address
))
434 boolean_t db_breakpoints_inserted
= TRUE
;
437 db_set_breakpoints(void)
439 register db_breakpoint_t bkpt
;
440 register task_t task
;
442 thread_act_t cur_act
= current_act();
445 cur_act
->task
: TASK_NULL
;
446 boolean_t inserted
= TRUE
;
448 if (!db_breakpoints_inserted
) {
449 for (bkpt
= db_breakpoint_list
; bkpt
!= 0; bkpt
= bkpt
->link
) {
450 if (bkpt
->flags
& BKPT_SET_IN_MEM
)
453 if (bkpt
->flags
& BKPT_USR_GLOBAL
) {
454 if ((bkpt
->flags
& BKPT_1ST_SET
) == 0) {
455 if (cur_task
== TASK_NULL
)
459 bkpt
->flags
&= ~BKPT_1ST_SET
;
461 if (DB_CHECK_ACCESS(bkpt
->address
, BKPT_SIZE
, task
)) {
462 inst
= db_get_task_value(bkpt
->address
, BKPT_SIZE
, FALSE
,
464 if (inst
== BKPT_SET(inst
))
466 bkpt
->bkpt_inst
= inst
;
467 db_put_task_value(bkpt
->address
,
469 BKPT_SET(bkpt
->bkpt_inst
), task
);
470 bkpt
->flags
|= BKPT_SET_IN_MEM
;
475 db_breakpoints_inserted
= inserted
;
480 db_clear_breakpoints(void)
482 register db_breakpoint_t bkpt
, *bkptp
;
483 register task_t task
;
485 thread_act_t cur_act
= current_act();
486 task_t cur_task
= (cur_act
) ?
487 cur_act
->task
: TASK_NULL
;
489 if (db_breakpoints_inserted
) {
490 bkptp
= &db_breakpoint_list
;
491 for (bkpt
= *bkptp
; bkpt
; bkpt
= *bkptp
) {
493 if (bkpt
->flags
& BKPT_USR_GLOBAL
) {
494 if (cur_task
== TASK_NULL
) {
500 if ((bkpt
->flags
& BKPT_SET_IN_MEM
)
501 && DB_CHECK_ACCESS(bkpt
->address
, BKPT_SIZE
, task
)) {
502 inst
= db_get_task_value(bkpt
->address
, BKPT_SIZE
, FALSE
,
504 if (inst
!= BKPT_SET(inst
)) {
505 if (bkpt
->flags
& BKPT_USR_GLOBAL
) {
509 db_force_delete_breakpoint(bkpt
, 0, FALSE
);
511 db_breakpoint_free(bkpt
);
514 db_put_task_value(bkpt
->address
, BKPT_SIZE
,
515 bkpt
->bkpt_inst
, task
);
516 bkpt
->flags
&= ~BKPT_SET_IN_MEM
;
520 db_breakpoints_inserted
= FALSE
;
525 * Set a temporary breakpoint.
526 * The instruction is changed immediately,
527 * so the breakpoint does not have to be on the breakpoint list.
530 db_set_temp_breakpoint(
534 register db_breakpoint_t bkpt
;
536 bkpt
= db_breakpoint_alloc();
538 db_printf("Too many breakpoints.\n");
542 bkpt
->address
= addr
;
543 bkpt
->flags
= BKPT_TEMP
;
545 if (db_add_thread_breakpoint(bkpt
, 0, 1, FALSE
) < 0) {
547 db_breakpoint_free(bkpt
);
548 db_printf("Too many thread_breakpoints.\n");
551 bkpt
->bkpt_inst
= db_get_task_value(bkpt
->address
, BKPT_SIZE
,
553 db_put_task_value(bkpt
->address
, BKPT_SIZE
,
554 BKPT_SET(bkpt
->bkpt_inst
), task
);
559 db_delete_temp_breakpoint(
561 db_breakpoint_t bkpt
)
563 db_put_task_value(bkpt
->address
, BKPT_SIZE
, bkpt
->bkpt_inst
, task
);
564 db_delete_thread_breakpoint(bkpt
, 0);
565 db_breakpoint_free(bkpt
);
572 db_list_breakpoints(void)
574 register db_breakpoint_t bkpt
;
576 if (db_breakpoint_list
== 0) {
577 db_printf("No breakpoints set\n");
581 db_printf(" No Space Task.Act Cnt Address(Cond)\n");
582 for (bkpt
= db_breakpoint_list
;
586 register db_thread_breakpoint_t tp
;
591 for (tp
= bkpt
->threads
; tp
; tp
= tp
->tb_next
) {
592 db_printf("%3d ", tp
->tb_number
);
593 if (bkpt
->flags
& BKPT_USR_GLOBAL
)
595 else if (bkpt
->task
== TASK_NULL
)
596 db_printf("kernel ");
597 else if ((task_id
= db_lookup_task(bkpt
->task
)) < 0)
598 db_printf("%0*X ", 2*sizeof(vm_offset_t
), bkpt
->task
);
600 db_printf("task%-3d ", task_id
);
601 if (tp
->tb_task_thd
== 0) {
604 if (tp
->tb_is_task
) {
605 task_id
= db_lookup_task((task_t
)(tp
->tb_task_thd
));
607 db_printf("%0*X ", 2*sizeof(vm_offset_t
),
610 db_printf("task%03d ", task_id
);
612 thread_act_t thd
= (thread_act_t
)(tp
->tb_task_thd
);
613 task_id
= db_lookup_task(thd
->task
);
614 act_id
= db_lookup_task_act(thd
->task
, thd
);
615 if (task_id
< 0 || act_id
< 0)
616 db_printf("%0*X ", 2*sizeof(vm_offset_t
),
619 db_printf("task%03d.%-3d ", task_id
, act_id
);
622 db_printf("%3d ", tp
->tb_init_count
);
623 db_task_printsym(bkpt
->address
, DB_STGY_PROC
, bkpt
->task
);
624 if (tp
->tb_cond
> 0) {
632 if (bkpt
->task
== TASK_NULL
)
633 db_printf(" ? kernel ");
635 db_printf("%*X ", 2*sizeof(vm_offset_t
), bkpt
->task
);
637 db_task_printsym(bkpt
->address
, DB_STGY_PROC
, bkpt
->task
);
644 db_delete_all_breakpoints(
647 register db_breakpoint_t bkpt
;
649 bkpt
= db_breakpoint_list
;
650 while ( bkpt
!= 0 ) {
651 if (bkpt
->task
== task
||
652 (task
!= TASK_NULL
&& (bkpt
->flags
& BKPT_USR_GLOBAL
))) {
653 db_delete_breakpoint(task
, bkpt
->address
, 0);
654 bkpt
= db_breakpoint_list
;
662 /* Delete breakpoint */
667 thread_act_t thr_act
;
668 vm_offset_t task_thd
;
669 boolean_t user_global
= FALSE
;
670 boolean_t task_bpt
= FALSE
;
671 boolean_t user_space
= FALSE
;
672 boolean_t thd_bpt
= FALSE
;
680 db_printf("Bad modifier \"%s\"\n", db_tok_string
);
683 user_global
= db_option(db_tok_string
, 'U');
684 user_space
= (user_global
)? TRUE
: db_option(db_tok_string
, 'u');
685 task_bpt
= db_option(db_tok_string
, 'T');
686 thd_bpt
= db_option(db_tok_string
, 't');
687 if (task_bpt
&& user_global
)
688 db_error("Cannot specify both 'T' and 'U' option\n");
693 db_printf("Delete ALL breakpoints\n");
694 db_delete_all_breakpoints( (task_t
)task_bpt
);
699 db_thread_breakpoint_t tbp
;
700 db_breakpoint_t bkpt
;
702 if (db_read_token() != tNUMBER
) {
703 db_printf("Bad break point number #%s\n", db_tok_string
);
706 if ((tbp
= db_find_breakpoint_number(db_tok_number
, &bkpt
)) == 0) {
707 db_printf("No such break point #%d\n", db_tok_number
);
710 db_delete_breakpoint(bkpt
->task
, bkpt
->address
, tbp
->tb_task_thd
);
714 if (!db_expression(&addr
)) {
716 * We attempt to pick up the user_space indication from db_dot,
717 * so that a plain "d" always works.
719 addr
= (db_expr_t
)db_dot
;
720 if (!user_space
&& !DB_VALID_ADDRESS(addr
, FALSE
))
723 if (!DB_VALID_ADDRESS(addr
, user_space
)) {
724 db_printf("Address %#X is not in %s space\n", addr
,
725 (user_space
)? "user": "kernel");
728 if (thd_bpt
|| task_bpt
) {
729 for (n
= 0; db_get_next_act(&thr_act
, n
); n
++) {
730 if (thr_act
== THR_ACT_NULL
)
731 db_error("No active thr_act\n");
733 if (thr_act
->task
== TASK_NULL
)
734 db_error("No task\n");
735 task_thd
= (vm_offset_t
) (thr_act
->task
);
737 task_thd
= (user_global
)? 0: (vm_offset_t
) thr_act
;
738 db_delete_breakpoint(db_target_space(thr_act
, user_space
),
739 (db_addr_t
)addr
, task_thd
);
742 db_delete_breakpoint(db_target_space(THR_ACT_NULL
, user_space
),
747 /* Set breakpoint with skip count */
748 #include <mach/machine/vm_param.h>
758 thread_act_t thr_act
;
759 boolean_t user_global
= db_option(modif
, 'U');
760 boolean_t task_bpt
= db_option(modif
, 'T');
761 boolean_t user_space
;
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("%#X is in user space\n", 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
== THR_ACT_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
)? THR_ACT_NULL
: thr_act
,
804 db_set_breakpoint(db_target_space(THR_ACT_NULL
, user_space
),
806 count
, THR_ACT_NULL
, FALSE
);
810 /* list breakpoints */
812 db_listbreak_cmd(void)
814 db_list_breakpoints();