2 * Copyright (c) 2000-2004 Apple Computer, Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
6 * The contents of this file constitute Original Code as defined in and
7 * are subject to the Apple Public Source License Version 1.1 (the
8 * "License"). You may not use this file except in compliance with the
9 * License. Please obtain a copy of the License at
10 * http://www.apple.com/publicsource and read it before using this file.
12 * This Original Code and all software distributed under the License are
13 * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER
14 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
15 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the
17 * License for the specific language governing rights and limitations
20 * @APPLE_LICENSE_HEADER_END@
28 * Author: David B. Golub, Carnegie Mellon University
35 #include <mach/boolean.h>
36 #include <machine/db_machdep.h>
37 #include <ddb/db_lex.h>
38 #include <ddb/db_break.h>
39 #include <ddb/db_access.h>
40 #include <ddb/db_sym.h>
41 #include <ddb/db_variables.h>
42 #include <ddb/db_command.h>
43 #include <ddb/db_cond.h>
44 #include <ddb/db_expr.h>
45 #include <ddb/db_output.h> /* For db_printf() */
46 #include <ddb/db_task_thread.h>
49 #define NBREAKPOINTS 100
50 #define NTHREAD_LIST (NBREAKPOINTS*3)
52 struct db_breakpoint db_break_table
[NBREAKPOINTS
];
53 db_breakpoint_t db_next_free_breakpoint
= &db_break_table
[0];
54 db_breakpoint_t db_free_breakpoints
= 0;
55 db_breakpoint_t db_breakpoint_list
= 0;
57 static struct db_thread_breakpoint db_thread_break_list
[NTHREAD_LIST
];
58 static db_thread_breakpoint_t db_free_thread_break_list
= 0;
59 static boolean_t db_thread_break_init
= FALSE
;
60 static int db_breakpoint_number
= 0;
62 /* Prototypes for functions local to this file. XXX -- should be static!
64 static int db_add_thread_breakpoint(
65 register db_breakpoint_t bkpt
,
70 static int db_delete_thread_breakpoint(
71 register db_breakpoint_t bkpt
,
72 vm_offset_t task_thd
);
74 static db_thread_breakpoint_t
db_find_thread_breakpoint(
78 static void db_force_delete_breakpoint(
83 db_breakpoint_t
db_breakpoint_alloc(void);
85 void db_breakpoint_free(register db_breakpoint_t bkpt
);
87 void db_delete_breakpoint(
90 vm_offset_t task_thd
);
93 db_delete_all_breakpoints(
96 void db_list_breakpoints(void);
101 db_breakpoint_alloc(void)
103 register db_breakpoint_t bkpt
;
105 if ((bkpt
= db_free_breakpoints
) != 0) {
106 db_free_breakpoints
= bkpt
->link
;
109 if (db_next_free_breakpoint
== &db_break_table
[NBREAKPOINTS
]) {
110 db_printf("All breakpoints used.\n");
113 bkpt
= db_next_free_breakpoint
;
114 db_next_free_breakpoint
++;
120 db_breakpoint_free(register db_breakpoint_t bkpt
)
122 bkpt
->link
= db_free_breakpoints
;
123 db_free_breakpoints
= bkpt
;
127 db_add_thread_breakpoint(
128 register db_breakpoint_t bkpt
,
129 vm_offset_t task_thd
,
133 register db_thread_breakpoint_t tp
;
135 if (db_thread_break_init
== FALSE
) {
136 for (tp
= db_thread_break_list
;
137 tp
< &db_thread_break_list
[NTHREAD_LIST
-1]; tp
++)
140 db_free_thread_break_list
= db_thread_break_list
;
141 db_thread_break_init
= TRUE
;
143 if (db_free_thread_break_list
== 0)
145 tp
= db_free_thread_break_list
;
146 db_free_thread_break_list
= tp
->tb_next
;
147 tp
->tb_is_task
= task_bpt
;
148 tp
->tb_task_thd
= task_thd
;
149 tp
->tb_count
= count
;
150 tp
->tb_init_count
= count
;
152 tp
->tb_number
= ++db_breakpoint_number
;
153 tp
->tb_next
= bkpt
->threads
;
159 db_delete_thread_breakpoint(
160 register db_breakpoint_t bkpt
,
161 vm_offset_t task_thd
)
163 register db_thread_breakpoint_t tp
;
164 register db_thread_breakpoint_t
*tpp
;
167 /* delete all the thread-breakpoints */
169 for (tpp
= &bkpt
->threads
; (tp
= *tpp
) != 0; tpp
= &tp
->tb_next
)
172 *tpp
= db_free_thread_break_list
;
173 db_free_thread_break_list
= bkpt
->threads
;
177 /* delete the specified thread-breakpoint */
179 for (tpp
= &bkpt
->threads
; (tp
= *tpp
) != 0; tpp
= &tp
->tb_next
)
180 if (tp
->tb_task_thd
== task_thd
) {
183 tp
->tb_next
= db_free_thread_break_list
;
184 db_free_thread_break_list
= tp
;
188 return -1; /* not found */
192 static db_thread_breakpoint_t
193 db_find_thread_breakpoint(
194 db_breakpoint_t bkpt
,
197 register db_thread_breakpoint_t tp
;
198 register task_t task
=
199 (thr_act
== THREAD_NULL
)
200 ? TASK_NULL
: thr_act
->task
;
202 for (tp
= bkpt
->threads
; tp
; tp
= tp
->tb_next
) {
203 if (tp
->tb_is_task
) {
204 if (tp
->tb_task_thd
== (vm_offset_t
)task
)
208 if (tp
->tb_task_thd
== (vm_offset_t
)thr_act
|| tp
->tb_task_thd
== 0)
214 db_thread_breakpoint_t
215 db_find_thread_breakpoint_here(
219 db_breakpoint_t bkpt
;
221 bkpt
= db_find_breakpoint(task
, (db_addr_t
)addr
);
224 return(db_find_thread_breakpoint(bkpt
, current_act()));
227 db_thread_breakpoint_t
228 db_find_breakpoint_number(
230 db_breakpoint_t
*bkptp
)
232 register db_thread_breakpoint_t tp
;
233 register db_breakpoint_t bkpt
;
235 for (bkpt
= db_breakpoint_list
; bkpt
!= 0; bkpt
= bkpt
->link
) {
236 for (tp
= bkpt
->threads
; tp
; tp
= tp
->tb_next
) {
237 if (tp
->tb_number
== num
) {
248 db_force_delete_breakpoint(
249 db_breakpoint_t bkpt
,
250 vm_offset_t task_thd
,
253 db_printf("deleted a stale breakpoint at ");
254 if (bkpt
->task
== TASK_NULL
|| db_lookup_task(bkpt
->task
) >= 0)
255 db_task_printsym(bkpt
->address
, DB_STGY_PROC
, bkpt
->task
);
257 db_printf("%#X", bkpt
->address
);
259 db_printf(" in task %X", bkpt
->task
);
261 db_printf(" for %s %X", (is_task
)? "task": "thr_act", task_thd
);
263 db_delete_thread_breakpoint(bkpt
, task_thd
);
267 db_check_breakpoint_valid(void)
269 register db_thread_breakpoint_t tbp
, tbp_next
;
270 register db_breakpoint_t bkpt
, *bkptp
;
272 bkptp
= &db_breakpoint_list
;
273 for (bkpt
= *bkptp
; bkpt
; bkpt
= *bkptp
) {
274 if (bkpt
->task
!= TASK_NULL
) {
275 if (db_lookup_task(bkpt
->task
) < 0) {
276 db_force_delete_breakpoint(bkpt
, 0, FALSE
);
278 db_breakpoint_free(bkpt
);
282 for (tbp
= bkpt
->threads
; tbp
; tbp
= tbp_next
) {
283 tbp_next
= tbp
->tb_next
;
284 if (tbp
->tb_task_thd
== 0)
286 if ((tbp
->tb_is_task
&&
287 db_lookup_task((task_t
)(tbp
->tb_task_thd
)) < 0) ||
289 db_lookup_act((thread_t
)(tbp
->tb_task_thd
)) < 0)) {
290 db_force_delete_breakpoint(bkpt
,
291 tbp
->tb_task_thd
, tbp
->tb_is_task
);
294 if (bkpt
->threads
== 0) {
295 db_put_task_value(bkpt
->address
, BKPT_SIZE
,
296 bkpt
->bkpt_inst
, bkpt
->task
);
298 db_breakpoint_free(bkpt
);
314 register db_breakpoint_t bkpt
;
315 db_breakpoint_t alloc_bkpt
= 0;
316 vm_offset_t task_thd
;
318 bkpt
= db_find_breakpoint(task
, addr
);
320 if (thr_act
== THREAD_NULL
321 || db_find_thread_breakpoint(bkpt
, thr_act
)) {
322 db_printf("Already set.\n");
326 if (!DB_CHECK_ACCESS(addr
, BKPT_SIZE
, task
)) {
328 db_printf("Warning: non-resident page for breakpoint at %llX",
329 (unsigned long long)addr
);
330 db_printf(" in task %lX.\n", task
);
332 db_printf("Cannot set breakpoint at %llX in kernel space.\n",
333 (unsigned long long)addr
);
337 alloc_bkpt
= bkpt
= db_breakpoint_alloc();
339 db_printf("Too many breakpoints.\n");
343 bkpt
->flags
= (task
&& thr_act
== THREAD_NULL
)?
344 (BKPT_USR_GLOBAL
|BKPT_1ST_SET
): 0;
345 bkpt
->address
= addr
;
348 if (db_breakpoint_list
== 0)
349 db_breakpoint_number
= 0;
350 task_thd
= (task_bpt
) ? (vm_offset_t
)(thr_act
->task
)
351 : (vm_offset_t
)thr_act
;
352 if (db_add_thread_breakpoint(bkpt
, task_thd
, count
, task_bpt
) < 0) {
354 db_breakpoint_free(alloc_bkpt
);
355 db_printf("Too many thread_breakpoints.\n");
357 db_printf("set breakpoint #%x\n", db_breakpoint_number
);
359 bkpt
->link
= db_breakpoint_list
;
360 db_breakpoint_list
= bkpt
;
366 db_delete_breakpoint(
369 vm_offset_t task_thd
)
371 register db_breakpoint_t bkpt
;
372 register db_breakpoint_t
*prev
;
374 for (prev
= &db_breakpoint_list
; (bkpt
= *prev
) != 0;
375 prev
= &bkpt
->link
) {
376 if ((bkpt
->task
== task
377 || (task
!= TASK_NULL
&& (bkpt
->flags
& BKPT_USR_GLOBAL
)))
378 && bkpt
->address
== addr
)
381 if (bkpt
&& (bkpt
->flags
& BKPT_SET_IN_MEM
)) {
382 db_printf("cannot delete it now.\n");
386 || db_delete_thread_breakpoint(bkpt
, task_thd
) < 0) {
387 db_printf("Not set.\n");
390 if (bkpt
->threads
== 0) {
392 db_breakpoint_free(bkpt
);
401 register db_breakpoint_t bkpt
;
403 for (bkpt
= db_breakpoint_list
; bkpt
!= 0; bkpt
= bkpt
->link
) {
404 if ((bkpt
->task
== task
405 || (task
!= TASK_NULL
&& (bkpt
->flags
& BKPT_USR_GLOBAL
)))
406 && bkpt
->address
== addr
)
413 db_find_breakpoint_here(
417 register db_breakpoint_t bkpt
;
419 for (bkpt
= db_breakpoint_list
; bkpt
!= 0; bkpt
= bkpt
->link
) {
420 if ((bkpt
->task
== task
421 || (task
!= TASK_NULL
&& (bkpt
->flags
& BKPT_USR_GLOBAL
)))
422 && bkpt
->address
== addr
)
424 if ((bkpt
->flags
& BKPT_USR_GLOBAL
) == 0 &&
425 DB_PHYS_EQ(task
, addr
, bkpt
->task
, bkpt
->address
))
431 boolean_t db_breakpoints_inserted
= TRUE
;
434 db_set_breakpoints(void)
436 register db_breakpoint_t bkpt
;
437 register task_t task
;
439 thread_t cur_act
= current_act();
442 cur_act
->task
: TASK_NULL
;
443 boolean_t inserted
= TRUE
;
445 if (!db_breakpoints_inserted
) {
446 for (bkpt
= db_breakpoint_list
; bkpt
!= 0; bkpt
= bkpt
->link
) {
447 if (bkpt
->flags
& BKPT_SET_IN_MEM
)
450 if (bkpt
->flags
& BKPT_USR_GLOBAL
) {
451 if ((bkpt
->flags
& BKPT_1ST_SET
) == 0) {
452 if (cur_task
== TASK_NULL
)
456 bkpt
->flags
&= ~BKPT_1ST_SET
;
458 if (DB_CHECK_ACCESS(bkpt
->address
, BKPT_SIZE
, task
)) {
459 inst
= db_get_task_value(bkpt
->address
, BKPT_SIZE
, FALSE
,
461 if (inst
== BKPT_SET(inst
))
463 bkpt
->bkpt_inst
= inst
;
464 db_put_task_value(bkpt
->address
,
466 BKPT_SET(bkpt
->bkpt_inst
), task
);
467 bkpt
->flags
|= BKPT_SET_IN_MEM
;
472 db_breakpoints_inserted
= inserted
;
477 db_clear_breakpoints(void)
479 register db_breakpoint_t bkpt
, *bkptp
;
480 register task_t task
;
482 thread_t cur_act
= current_act();
483 task_t cur_task
= (cur_act
) ?
484 cur_act
->task
: TASK_NULL
;
486 if (db_breakpoints_inserted
) {
487 bkptp
= &db_breakpoint_list
;
488 for (bkpt
= *bkptp
; bkpt
; bkpt
= *bkptp
) {
490 if (bkpt
->flags
& BKPT_USR_GLOBAL
) {
491 if (cur_task
== TASK_NULL
) {
497 if ((bkpt
->flags
& BKPT_SET_IN_MEM
)
498 && DB_CHECK_ACCESS(bkpt
->address
, BKPT_SIZE
, task
)) {
499 inst
= db_get_task_value(bkpt
->address
, BKPT_SIZE
, FALSE
,
501 if (inst
!= BKPT_SET(inst
)) {
502 if (bkpt
->flags
& BKPT_USR_GLOBAL
) {
506 db_force_delete_breakpoint(bkpt
, 0, FALSE
);
508 db_breakpoint_free(bkpt
);
511 db_put_task_value(bkpt
->address
, BKPT_SIZE
,
512 bkpt
->bkpt_inst
, task
);
513 bkpt
->flags
&= ~BKPT_SET_IN_MEM
;
517 db_breakpoints_inserted
= FALSE
;
522 * Set a temporary breakpoint.
523 * The instruction is changed immediately,
524 * so the breakpoint does not have to be on the breakpoint list.
527 db_set_temp_breakpoint(
531 register db_breakpoint_t bkpt
;
533 bkpt
= db_breakpoint_alloc();
535 db_printf("Too many breakpoints.\n");
539 bkpt
->address
= addr
;
540 bkpt
->flags
= BKPT_TEMP
;
542 if (db_add_thread_breakpoint(bkpt
, 0, 1, FALSE
) < 0) {
544 db_breakpoint_free(bkpt
);
545 db_printf("Too many thread_breakpoints.\n");
548 bkpt
->bkpt_inst
= db_get_task_value(bkpt
->address
, BKPT_SIZE
,
550 db_put_task_value(bkpt
->address
, BKPT_SIZE
,
551 BKPT_SET(bkpt
->bkpt_inst
), task
);
556 db_delete_temp_breakpoint(
558 db_breakpoint_t bkpt
)
560 db_put_task_value(bkpt
->address
, BKPT_SIZE
, bkpt
->bkpt_inst
, task
);
561 db_delete_thread_breakpoint(bkpt
, 0);
562 db_breakpoint_free(bkpt
);
569 db_list_breakpoints(void)
571 register db_breakpoint_t bkpt
;
573 if (db_breakpoint_list
== 0) {
574 db_printf("No breakpoints set\n");
578 db_printf(" No Space Task.Act Cnt Address(Cond)\n");
579 for (bkpt
= db_breakpoint_list
;
583 register db_thread_breakpoint_t tp
;
588 for (tp
= bkpt
->threads
; tp
; tp
= tp
->tb_next
) {
589 db_printf("%3d ", tp
->tb_number
);
590 if (bkpt
->flags
& BKPT_USR_GLOBAL
)
592 else if (bkpt
->task
== TASK_NULL
)
593 db_printf("kernel ");
594 else if ((task_id
= db_lookup_task(bkpt
->task
)) < 0)
595 db_printf("%0*X ", 2*sizeof(vm_offset_t
), bkpt
->task
);
597 db_printf("task%-3d ", task_id
);
598 if (tp
->tb_task_thd
== 0) {
601 if (tp
->tb_is_task
) {
602 task_id
= db_lookup_task((task_t
)(tp
->tb_task_thd
));
604 db_printf("%0*X ", 2*sizeof(vm_offset_t
),
607 db_printf("task%03d ", task_id
);
609 thread_t thd
= (thread_t
)(tp
->tb_task_thd
);
610 task_id
= db_lookup_task(thd
->task
);
611 act_id
= db_lookup_task_act(thd
->task
, thd
);
612 if (task_id
< 0 || act_id
< 0)
613 db_printf("%0*X ", 2*sizeof(vm_offset_t
),
616 db_printf("task%03d.%-3d ", task_id
, act_id
);
619 db_printf("%3d ", tp
->tb_init_count
);
620 db_task_printsym(bkpt
->address
, DB_STGY_PROC
, bkpt
->task
);
621 if (tp
->tb_cond
> 0) {
629 if (bkpt
->task
== TASK_NULL
)
630 db_printf(" ? kernel ");
632 db_printf("%*X ", 2*sizeof(vm_offset_t
), bkpt
->task
);
634 db_task_printsym(bkpt
->address
, DB_STGY_PROC
, bkpt
->task
);
641 db_delete_all_breakpoints(
644 register db_breakpoint_t bkpt
;
646 bkpt
= db_breakpoint_list
;
647 while ( bkpt
!= 0 ) {
648 if (bkpt
->task
== task
||
649 (task
!= TASK_NULL
&& (bkpt
->flags
& BKPT_USR_GLOBAL
))) {
650 db_delete_breakpoint(task
, bkpt
->address
, 0);
651 bkpt
= db_breakpoint_list
;
659 /* Delete breakpoint */
665 vm_offset_t task_thd
;
666 boolean_t user_global
= FALSE
;
667 boolean_t task_bpt
= FALSE
;
668 boolean_t user_space
= FALSE
;
669 boolean_t thd_bpt
= FALSE
;
677 db_printf("Bad modifier \"%s\"\n", db_tok_string
);
680 user_global
= db_option(db_tok_string
, 'U');
681 user_space
= (user_global
)? TRUE
: db_option(db_tok_string
, 'u');
682 task_bpt
= db_option(db_tok_string
, 'T');
683 thd_bpt
= db_option(db_tok_string
, 't');
684 if (task_bpt
&& user_global
)
685 db_error("Cannot specify both 'T' and 'U' option\n");
690 db_printf("Delete ALL breakpoints\n");
691 db_delete_all_breakpoints( (task_t
)task_bpt
);
696 db_thread_breakpoint_t tbp
;
697 db_breakpoint_t bkpt
;
699 if (db_read_token() != tNUMBER
) {
700 db_printf("Bad break point number #%s\n", db_tok_string
);
703 if ((tbp
= db_find_breakpoint_number(db_tok_number
, &bkpt
)) == 0) {
704 db_printf("No such break point #%d\n", db_tok_number
);
707 db_delete_breakpoint(bkpt
->task
, bkpt
->address
, tbp
->tb_task_thd
);
711 if (!db_expression(&addr
)) {
713 * We attempt to pick up the user_space indication from db_dot,
714 * so that a plain "d" always works.
716 addr
= (db_expr_t
)db_dot
;
717 if (!user_space
&& !DB_VALID_ADDRESS(addr
, FALSE
))
720 if (!DB_VALID_ADDRESS(addr
, user_space
)) {
721 db_printf("Address %#llX is not in %s space\n", (unsigned long long)addr
,
722 (user_space
)? "user": "kernel");
725 if (thd_bpt
|| task_bpt
) {
726 for (n
= 0; db_get_next_act(&thr_act
, n
); n
++) {
727 if (thr_act
== THREAD_NULL
)
728 db_error("No active thr_act\n");
730 if (thr_act
->task
== TASK_NULL
)
731 db_error("No task\n");
732 task_thd
= (vm_offset_t
) (thr_act
->task
);
734 task_thd
= (user_global
)? 0: (vm_offset_t
) thr_act
;
735 db_delete_breakpoint(db_target_space(thr_act
, user_space
),
736 (db_addr_t
)addr
, task_thd
);
739 db_delete_breakpoint(db_target_space(THREAD_NULL
, user_space
),
744 /* Set breakpoint with skip count */
745 #include <mach/machine/vm_param.h>
756 boolean_t user_global
= db_option(modif
, 'U');
757 boolean_t task_bpt
= db_option(modif
, 'T');
758 boolean_t user_space
;
763 if (!task_bpt
&& db_option(modif
,'t'))
767 if (task_bpt
&& user_global
)
768 db_error("Cannot specify both 'T' and 'U'\n");
769 user_space
= (user_global
)? TRUE
: db_option(modif
, 'u');
770 if (user_space
&& db_access_level
< DB_ACCESS_CURRENT
)
771 db_error("User space break point is not supported\n");
772 if ((!task_bpt
|| !user_space
) &&
773 !DB_VALID_ADDRESS(addr
, user_space
)) {
774 /* if the user has explicitly specified user space,
775 do not insert a breakpoint into the kernel */
777 db_error("Invalid user space address\n");
779 db_printf("%#llX is in user space\n", (unsigned long long)addr
);
781 db_printf("kernel is from %#X to %#x\n", VM_MIN_KERNEL_ADDRESS
, vm_last_addr
);
783 db_printf("kernel is from %#X to %#x\n", VM_MIN_KERNEL_ADDRESS
, VM_MAX_KERNEL_ADDRESS
);
786 if (db_option(modif
, 't') || task_bpt
) {
787 for (n
= 0; db_get_next_act(&thr_act
, n
); n
++) {
788 if (thr_act
== THREAD_NULL
)
789 db_error("No active thr_act\n");
790 if (task_bpt
&& thr_act
->task
== TASK_NULL
)
791 db_error("No task\n");
792 if (db_access_level
<= DB_ACCESS_CURRENT
&& user_space
793 && thr_act
->task
!= db_current_space())
794 db_error("Cannot set break point in inactive user space\n");
795 db_set_breakpoint(db_target_space(thr_act
, user_space
),
796 (db_addr_t
)addr
, count
,
797 (user_global
)? THREAD_NULL
: thr_act
,
801 db_set_breakpoint(db_target_space(THREAD_NULL
, user_space
),
803 count
, THREAD_NULL
, FALSE
);
807 /* list breakpoints */
809 db_listbreak_cmd(void)
811 db_list_breakpoints();