]> git.saurik.com Git - apple/xnu.git/blob - osfmk/ddb/db_break.c
aa96f95bf564cd50e8a6be4d947032ebdf94934d
[apple/xnu.git] / osfmk / ddb / db_break.c
1 /*
2 * Copyright (c) 2000 Apple Computer, Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * Copyright (c) 1999-2003 Apple Computer, Inc. All Rights Reserved.
7 *
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
13 * file.
14 *
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.
22 *
23 * @APPLE_LICENSE_HEADER_END@
24 */
25 /*
26 * @OSF_COPYRIGHT@
27 */
28 /*
29 * HISTORY
30 *
31 * Revision 1.1.1.1 1998/09/22 21:05:47 wsanchez
32 * Import of Mac OS X kernel (~semeria)
33 *
34 * Revision 1.2 1998/04/29 17:35:26 mburg
35 * MK7.3 merger
36 *
37 * Revision 1.2.47.1 1998/02/03 09:23:57 gdt
38 * Merge up to MK7.3
39 * [1998/02/03 09:10:14 gdt]
40 *
41 * Revision 1.2.45.1 1997/03/27 18:46:16 barbou
42 * ri-osc CR1557: re-enable thread-specific breakpoints.
43 * [1995/09/20 15:23:46 bolinger]
44 * [97/02/25 barbou]
45 *
46 * Revision 1.2.21.6 1996/01/09 19:15:21 devrcs
47 * Changed declarations of 'register foo' to 'register int foo'
48 * Fixed printfs which print addresses.
49 * [1995/12/01 21:41:51 jfraser]
50 *
51 * Merged '64-bit safe' changes from DEC alpha port.
52 * [1995/11/21 18:02:40 jfraser]
53 *
54 * Revision 1.2.21.5 1995/04/07 18:52:54 barbou
55 * Allow breakpoints on non-resident pages. The breakpoint will
56 * actually be set when the page is paged in.
57 * [93/09/23 barbou]
58 * [95/03/08 barbou]
59 *
60 * Revision 1.2.21.4 1995/02/23 21:43:19 alanl
61 * Merged with DIPC2_SHARED.
62 * [1995/01/04 20:15:04 alanl]
63 *
64 * Revision 1.2.28.1 1994/11/04 09:52:15 dwm
65 * mk6 CR668 - 1.3b26 merge
66 * * Revision 1.2.4.5 1994/05/06 18:38:52 tmt
67 * Merged osc1.3dec/shared with osc1.3b19
68 * Moved struct db_breakpoint from here to db_break.h.
69 * Merge Alpha changes into osc1.312b source code.
70 * 64bit cleanup.
71 * * End1.3merge
72 * [1994/11/04 08:49:10 dwm]
73 *
74 * Revision 1.2.21.2 1994/09/23 01:17:57 ezf
75 * change marker to not FREE
76 * [1994/09/22 21:09:19 ezf]
77 *
78 * Revision 1.2.21.1 1994/06/11 21:11:24 bolinger
79 * Merge up to NMK17.2.
80 * [1994/06/11 20:01:06 bolinger]
81 *
82 * Revision 1.2.25.2 1994/10/28 18:56:21 rwd
83 * Delint.
84 * [94/10/28 rwd]
85 *
86 * Revision 1.2.25.1 1994/08/04 01:42:15 mmp
87 * 23-Jun-94 Stan Smith (stans@ssd.intel.com)
88 * Let d * delete all breakpoints.
89 * [1994/06/28 13:54:00 sjs]
90 *
91 * Revision 1.2.19.2 1994/04/11 09:34:22 bernadat
92 * Moved db_breakpoint struct declaration to db_break.h
93 * [94/03/16 bernadat]
94 *
95 * Revision 1.2.19.1 1994/02/08 10:57:22 bernadat
96 * When setting a breakpoint, force user_space if breakpoint is
97 * outside kernel_space (like in the case of an emulator).
98 * [93/09/27 paire]
99 *
100 * Changed silly decimal display to hex (to match input conventions).
101 * Change from NORMA_MK14.6 [93/01/09 sjs]
102 * [93/07/16 bernadat]
103 * [94/02/07 bernadat]
104 *
105 * Revision 1.2.4.3 1993/07/27 18:26:48 elliston
106 * Add ANSI prototypes. CR #9523.
107 * [1993/07/27 18:10:54 elliston]
108 *
109 * Revision 1.2.4.2 1993/06/09 02:19:39 gm
110 * Added to OSF/1 R1.3 from NMK15.0.
111 * [1993/06/02 20:55:42 jeffc]
112 *
113 * Revision 1.2 1993/04/19 16:01:31 devrcs
114 * Changes from MK78:
115 * Removed unused variable from db_delete_cmd().
116 * Added declaration for arg 'count' of db_add_thread_breakpoint().
117 * [92/05/18 jfriedl]
118 * Fixed b/tu to b/Tu work if the specified address is valid in the
119 * target address space but not the current user space. Explicit
120 * user space breakpoints (b/u, b/Tu, etc) will no longer get
121 * inserted into the kernel if the specified address is invalid.
122 * [92/04/18 danner]
123 * [92/12/18 bruel]
124 *
125 * Revision 1.1 1992/09/30 02:00:52 robert
126 * Initial revision
127 *
128 * $EndLog$
129 */
130 /* CMU_HIST */
131 /*
132 * Revision 2.11.3.1 92/03/03 16:13:20 jeffreyh
133 * Pick up changes from TRUNK
134 * [92/02/26 10:58:37 jeffreyh]
135 *
136 * Revision 2.12 92/02/19 16:46:24 elf
137 * Removed one of the many user-unfriendlinesses.
138 * [92/02/10 17:48:25 af]
139 *
140 * Revision 2.11 91/11/12 11:50:24 rvb
141 * Fixed db_delete_cmd so that just "d" works in user space.
142 * [91/10/31 rpd]
143 * Fixed db_delete_thread_breakpoint for zero task_thd.
144 * [91/10/30 rpd]
145 *
146 * Revision 2.10 91/10/09 15:57:41 af
147 * Supported thread-oriented break points.
148 * [91/08/29 tak]
149 *
150 * Revision 2.9 91/07/09 23:15:39 danner
151 * Conditionalized db_map_addr to work right on the luna. Used a
152 * ifdef luna88k. This is evil, and needs to be fixed.
153 * [91/07/08 danner]
154 *
155 * Revision 2.2 91/04/10 22:54:50 mbj
156 * Grabbed 3.0 copyright/disclaimer since ddb comes from 3.0.
157 * [91/04/09 rvb]
158 *
159 * Revision 2.7 91/02/05 17:06:00 mrt
160 * Changed to new Mach copyright
161 * [91/01/31 16:17:01 mrt]
162 *
163 * Revision 2.6 91/01/08 15:09:03 rpd
164 * Added db_map_equal, db_map_current, db_map_addr.
165 * [90/11/10 rpd]
166 *
167 * Revision 2.5 90/11/05 14:26:32 rpd
168 * Initialize db_breakpoints_inserted to TRUE.
169 * [90/11/04 rpd]
170 *
171 * Revision 2.4 90/10/25 14:43:33 rwd
172 * Added map field to breakpoints.
173 * Added map argument to db_set_breakpoint, db_delete_breakpoint,
174 * db_find_breakpoint. Added db_find_breakpoint_here.
175 * [90/10/18 rpd]
176 *
177 * Revision 2.3 90/09/28 16:57:07 jsb
178 * Fixed db_breakpoint_free.
179 * [90/09/18 rpd]
180 *
181 * Revision 2.2 90/08/27 21:49:53 dbg
182 * Reflected changes in db_printsym()'s calling seq.
183 * [90/08/20 af]
184 * Clear breakpoints only if inserted.
185 * Reduce lint.
186 * [90/08/07 dbg]
187 * Created.
188 * [90/07/25 dbg]
189 *
190 */
191 /* CMU_ENDHIST */
192 /*
193 * Mach Operating System
194 * Copyright (c) 1991,1990 Carnegie Mellon University
195 * All Rights Reserved.
196 *
197 * Permission to use, copy, modify and distribute this software and its
198 * documentation is hereby granted, provided that both the copyright
199 * notice and this permission notice appear in all copies of the
200 * software, derivative works or modified versions, and any portions
201 * thereof, and that both notices appear in supporting documentation.
202 *
203 * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
204 * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
205 * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
206 *
207 * Carnegie Mellon requests users of this software to return to
208 *
209 * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
210 * School of Computer Science
211 * Carnegie Mellon University
212 * Pittsburgh PA 15213-3890
213 *
214 * any improvements or extensions that they make and grant Carnegie Mellon
215 * the rights to redistribute these changes.
216 */
217 /*
218 */
219 /*
220 * Author: David B. Golub, Carnegie Mellon University
221 * Date: 7/90
222 */
223
224 /*
225 * Breakpoints.
226 */
227 #include <mach/boolean.h>
228 #include <machine/db_machdep.h>
229 #include <ddb/db_lex.h>
230 #include <ddb/db_break.h>
231 #include <ddb/db_access.h>
232 #include <ddb/db_sym.h>
233 #include <ddb/db_variables.h>
234 #include <ddb/db_command.h>
235 #include <ddb/db_cond.h>
236 #include <ddb/db_expr.h>
237 #include <ddb/db_output.h> /* For db_printf() */
238 #include <ddb/db_task_thread.h>
239
240
241 #define NBREAKPOINTS 100
242 #define NTHREAD_LIST (NBREAKPOINTS*3)
243
244 struct db_breakpoint db_break_table[NBREAKPOINTS];
245 db_breakpoint_t db_next_free_breakpoint = &db_break_table[0];
246 db_breakpoint_t db_free_breakpoints = 0;
247 db_breakpoint_t db_breakpoint_list = 0;
248
249 static struct db_thread_breakpoint db_thread_break_list[NTHREAD_LIST];
250 static db_thread_breakpoint_t db_free_thread_break_list = 0;
251 static boolean_t db_thread_break_init = FALSE;
252 static int db_breakpoint_number = 0;
253
254 /* Prototypes for functions local to this file. XXX -- should be static!
255 */
256 static int db_add_thread_breakpoint(
257 register db_breakpoint_t bkpt,
258 vm_offset_t task_thd,
259 int count,
260 boolean_t task_bpt);
261
262 static int db_delete_thread_breakpoint(
263 register db_breakpoint_t bkpt,
264 vm_offset_t task_thd);
265
266 static db_thread_breakpoint_t db_find_thread_breakpoint(
267 db_breakpoint_t bkpt,
268 thread_act_t thr_act);
269
270 static void db_force_delete_breakpoint(
271 db_breakpoint_t bkpt,
272 vm_offset_t task_thd,
273 boolean_t is_task);
274
275 db_breakpoint_t db_breakpoint_alloc(void);
276
277 void db_breakpoint_free(register db_breakpoint_t bkpt);
278
279 void db_delete_breakpoint(
280 task_t task,
281 db_addr_t addr,
282 vm_offset_t task_thd);
283
284 void
285 db_delete_all_breakpoints(
286 task_t task);
287
288 void db_list_breakpoints(void);
289
290
291
292 db_breakpoint_t
293 db_breakpoint_alloc(void)
294 {
295 register db_breakpoint_t bkpt;
296
297 if ((bkpt = db_free_breakpoints) != 0) {
298 db_free_breakpoints = bkpt->link;
299 return (bkpt);
300 }
301 if (db_next_free_breakpoint == &db_break_table[NBREAKPOINTS]) {
302 db_printf("All breakpoints used.\n");
303 return (0);
304 }
305 bkpt = db_next_free_breakpoint;
306 db_next_free_breakpoint++;
307
308 return (bkpt);
309 }
310
311 void
312 db_breakpoint_free(register db_breakpoint_t bkpt)
313 {
314 bkpt->link = db_free_breakpoints;
315 db_free_breakpoints = bkpt;
316 }
317
318 static int
319 db_add_thread_breakpoint(
320 register db_breakpoint_t bkpt,
321 vm_offset_t task_thd,
322 int count,
323 boolean_t task_bpt)
324 {
325 register db_thread_breakpoint_t tp;
326
327 if (db_thread_break_init == FALSE) {
328 for (tp = db_thread_break_list;
329 tp < &db_thread_break_list[NTHREAD_LIST-1]; tp++)
330 tp->tb_next = tp+1;
331 tp->tb_next = 0;
332 db_free_thread_break_list = db_thread_break_list;
333 db_thread_break_init = TRUE;
334 }
335 if (db_free_thread_break_list == 0)
336 return (-1);
337 tp = db_free_thread_break_list;
338 db_free_thread_break_list = tp->tb_next;
339 tp->tb_is_task = task_bpt;
340 tp->tb_task_thd = task_thd;
341 tp->tb_count = count;
342 tp->tb_init_count = count;
343 tp->tb_cond = 0;
344 tp->tb_number = ++db_breakpoint_number;
345 tp->tb_next = bkpt->threads;
346 bkpt->threads = tp;
347 return(0);
348 }
349
350 static int
351 db_delete_thread_breakpoint(
352 register db_breakpoint_t bkpt,
353 vm_offset_t task_thd)
354 {
355 register db_thread_breakpoint_t tp;
356 register db_thread_breakpoint_t *tpp;
357
358 if (task_thd == 0) {
359 /* delete all the thread-breakpoints */
360
361 for (tpp = &bkpt->threads; (tp = *tpp) != 0; tpp = &tp->tb_next)
362 db_cond_free(tp);
363
364 *tpp = db_free_thread_break_list;
365 db_free_thread_break_list = bkpt->threads;
366 bkpt->threads = 0;
367 return 0;
368 } else {
369 /* delete the specified thread-breakpoint */
370
371 for (tpp = &bkpt->threads; (tp = *tpp) != 0; tpp = &tp->tb_next)
372 if (tp->tb_task_thd == task_thd) {
373 db_cond_free(tp);
374 *tpp = tp->tb_next;
375 tp->tb_next = db_free_thread_break_list;
376 db_free_thread_break_list = tp;
377 return 0;
378 }
379
380 return -1; /* not found */
381 }
382 }
383
384 static db_thread_breakpoint_t
385 db_find_thread_breakpoint(
386 db_breakpoint_t bkpt,
387 thread_act_t thr_act)
388 {
389 register db_thread_breakpoint_t tp;
390 register task_t task =
391 (thr_act == THR_ACT_NULL || thr_act->kernel_loaded)
392 ? TASK_NULL : thr_act->task;
393
394 for (tp = bkpt->threads; tp; tp = tp->tb_next) {
395 if (tp->tb_is_task) {
396 if (tp->tb_task_thd == (vm_offset_t)task)
397 break;
398 continue;
399 }
400 if (tp->tb_task_thd == (vm_offset_t)thr_act || tp->tb_task_thd == 0)
401 break;
402 }
403 return(tp);
404 }
405
406 db_thread_breakpoint_t
407 db_find_thread_breakpoint_here(
408 task_t task,
409 db_addr_t addr)
410 {
411 db_breakpoint_t bkpt;
412
413 bkpt = db_find_breakpoint(task, (db_addr_t)addr);
414 if (bkpt == 0)
415 return(0);
416 return(db_find_thread_breakpoint(bkpt, current_act()));
417 }
418
419 db_thread_breakpoint_t
420 db_find_breakpoint_number(
421 int num,
422 db_breakpoint_t *bkptp)
423 {
424 register db_thread_breakpoint_t tp;
425 register db_breakpoint_t bkpt;
426
427 for (bkpt = db_breakpoint_list; bkpt != 0; bkpt = bkpt->link) {
428 for (tp = bkpt->threads; tp; tp = tp->tb_next) {
429 if (tp->tb_number == num) {
430 if (bkptp)
431 *bkptp = bkpt;
432 return(tp);
433 }
434 }
435 }
436 return(0);
437 }
438
439 static void
440 db_force_delete_breakpoint(
441 db_breakpoint_t bkpt,
442 vm_offset_t task_thd,
443 boolean_t is_task)
444 {
445 db_printf("deleted a stale breakpoint at ");
446 if (bkpt->task == TASK_NULL || db_lookup_task(bkpt->task) >= 0)
447 db_task_printsym(bkpt->address, DB_STGY_PROC, bkpt->task);
448 else
449 db_printf("%#X", bkpt->address);
450 if (bkpt->task)
451 db_printf(" in task %X", bkpt->task);
452 if (task_thd)
453 db_printf(" for %s %X", (is_task)? "task": "thr_act", task_thd);
454 db_printf("\n");
455 db_delete_thread_breakpoint(bkpt, task_thd);
456 }
457
458 void
459 db_check_breakpoint_valid(void)
460 {
461 register db_thread_breakpoint_t tbp, tbp_next;
462 register db_breakpoint_t bkpt, *bkptp;
463
464 bkptp = &db_breakpoint_list;
465 for (bkpt = *bkptp; bkpt; bkpt = *bkptp) {
466 if (bkpt->task != TASK_NULL) {
467 if (db_lookup_task(bkpt->task) < 0) {
468 db_force_delete_breakpoint(bkpt, 0, FALSE);
469 *bkptp = bkpt->link;
470 db_breakpoint_free(bkpt);
471 continue;
472 }
473 } else {
474 for (tbp = bkpt->threads; tbp; tbp = tbp_next) {
475 tbp_next = tbp->tb_next;
476 if (tbp->tb_task_thd == 0)
477 continue;
478 if ((tbp->tb_is_task &&
479 db_lookup_task((task_t)(tbp->tb_task_thd)) < 0) ||
480 (!tbp->tb_is_task &&
481 db_lookup_act((thread_act_t)(tbp->tb_task_thd)) < 0)) {
482 db_force_delete_breakpoint(bkpt,
483 tbp->tb_task_thd, tbp->tb_is_task);
484 }
485 }
486 if (bkpt->threads == 0) {
487 db_put_task_value(bkpt->address, BKPT_SIZE,
488 bkpt->bkpt_inst, bkpt->task);
489 *bkptp = bkpt->link;
490 db_breakpoint_free(bkpt);
491 continue;
492 }
493 }
494 bkptp = &bkpt->link;
495 }
496 }
497
498 void
499 db_set_breakpoint(
500 task_t task,
501 db_addr_t addr,
502 int count,
503 thread_act_t thr_act,
504 boolean_t task_bpt)
505 {
506 register db_breakpoint_t bkpt;
507 db_breakpoint_t alloc_bkpt = 0;
508 vm_offset_t task_thd;
509
510 bkpt = db_find_breakpoint(task, addr);
511 if (bkpt) {
512 if (thr_act == THR_ACT_NULL
513 || db_find_thread_breakpoint(bkpt, thr_act)) {
514 db_printf("Already set.\n");
515 return;
516 }
517 } else {
518 if (!DB_CHECK_ACCESS(addr, BKPT_SIZE, task)) {
519 if (task) {
520 db_printf("Warning: non-resident page for breakpoint at %lX",
521 addr);
522 db_printf(" in task %lX.\n", task);
523 } else {
524 db_printf("Cannot set breakpoint at %lX in kernel space.\n",
525 addr);
526 return;
527 }
528 }
529 alloc_bkpt = bkpt = db_breakpoint_alloc();
530 if (bkpt == 0) {
531 db_printf("Too many breakpoints.\n");
532 return;
533 }
534 bkpt->task = task;
535 bkpt->flags = (task && thr_act == THR_ACT_NULL)?
536 (BKPT_USR_GLOBAL|BKPT_1ST_SET): 0;
537 bkpt->address = addr;
538 bkpt->threads = 0;
539 }
540 if (db_breakpoint_list == 0)
541 db_breakpoint_number = 0;
542 task_thd = (task_bpt) ? (vm_offset_t)(thr_act->task)
543 : (vm_offset_t)thr_act;
544 if (db_add_thread_breakpoint(bkpt, task_thd, count, task_bpt) < 0) {
545 if (alloc_bkpt)
546 db_breakpoint_free(alloc_bkpt);
547 db_printf("Too many thread_breakpoints.\n");
548 } else {
549 db_printf("set breakpoint #%x\n", db_breakpoint_number);
550 if (alloc_bkpt) {
551 bkpt->link = db_breakpoint_list;
552 db_breakpoint_list = bkpt;
553 }
554 }
555 }
556
557 void
558 db_delete_breakpoint(
559 task_t task,
560 db_addr_t addr,
561 vm_offset_t task_thd)
562 {
563 register db_breakpoint_t bkpt;
564 register db_breakpoint_t *prev;
565
566 for (prev = &db_breakpoint_list; (bkpt = *prev) != 0;
567 prev = &bkpt->link) {
568 if ((bkpt->task == task
569 || (task != TASK_NULL && (bkpt->flags & BKPT_USR_GLOBAL)))
570 && bkpt->address == addr)
571 break;
572 }
573 if (bkpt && (bkpt->flags & BKPT_SET_IN_MEM)) {
574 db_printf("cannot delete it now.\n");
575 return;
576 }
577 if (bkpt == 0
578 || db_delete_thread_breakpoint(bkpt, task_thd) < 0) {
579 db_printf("Not set.\n");
580 return;
581 }
582 if (bkpt->threads == 0) {
583 *prev = bkpt->link;
584 db_breakpoint_free(bkpt);
585 }
586 }
587
588 db_breakpoint_t
589 db_find_breakpoint(
590 task_t task,
591 db_addr_t addr)
592 {
593 register db_breakpoint_t bkpt;
594
595 for (bkpt = db_breakpoint_list; bkpt != 0; bkpt = bkpt->link) {
596 if ((bkpt->task == task
597 || (task != TASK_NULL && (bkpt->flags & BKPT_USR_GLOBAL)))
598 && bkpt->address == addr)
599 return (bkpt);
600 }
601 return (0);
602 }
603
604 boolean_t
605 db_find_breakpoint_here(
606 task_t task,
607 db_addr_t addr)
608 {
609 register db_breakpoint_t bkpt;
610
611 for (bkpt = db_breakpoint_list; bkpt != 0; bkpt = bkpt->link) {
612 if ((bkpt->task == task
613 || (task != TASK_NULL && (bkpt->flags & BKPT_USR_GLOBAL)))
614 && bkpt->address == addr)
615 return(TRUE);
616 if ((bkpt->flags & BKPT_USR_GLOBAL) == 0 &&
617 DB_PHYS_EQ(task, addr, bkpt->task, bkpt->address))
618 return (TRUE);
619 }
620 return(FALSE);
621 }
622
623 boolean_t db_breakpoints_inserted = TRUE;
624
625 void
626 db_set_breakpoints(void)
627 {
628 register db_breakpoint_t bkpt;
629 register task_t task;
630 db_expr_t inst;
631 thread_act_t cur_act = current_act();
632 task_t cur_task =
633 (cur_act && !cur_act->kernel_loaded) ?
634 cur_act->task : TASK_NULL;
635 boolean_t inserted = TRUE;
636
637 if (!db_breakpoints_inserted) {
638 for (bkpt = db_breakpoint_list; bkpt != 0; bkpt = bkpt->link) {
639 if (bkpt->flags & BKPT_SET_IN_MEM)
640 continue;
641 task = bkpt->task;
642 if (bkpt->flags & BKPT_USR_GLOBAL) {
643 if ((bkpt->flags & BKPT_1ST_SET) == 0) {
644 if (cur_task == TASK_NULL)
645 continue;
646 task = cur_task;
647 } else
648 bkpt->flags &= ~BKPT_1ST_SET;
649 }
650 if (DB_CHECK_ACCESS(bkpt->address, BKPT_SIZE, task)) {
651 inst = db_get_task_value(bkpt->address, BKPT_SIZE, FALSE,
652 task);
653 if (inst == BKPT_SET(inst))
654 continue;
655 bkpt->bkpt_inst = inst;
656 db_put_task_value(bkpt->address,
657 BKPT_SIZE,
658 BKPT_SET(bkpt->bkpt_inst), task);
659 bkpt->flags |= BKPT_SET_IN_MEM;
660 } else {
661 inserted = FALSE;
662 }
663 }
664 db_breakpoints_inserted = inserted;
665 }
666 }
667
668 void
669 db_clear_breakpoints(void)
670 {
671 register db_breakpoint_t bkpt, *bkptp;
672 register task_t task;
673 db_expr_t inst;
674 thread_act_t cur_act = current_act();
675 task_t cur_task = (cur_act && !cur_act->kernel_loaded) ?
676 cur_act->task: TASK_NULL;
677
678 if (db_breakpoints_inserted) {
679 bkptp = &db_breakpoint_list;
680 for (bkpt = *bkptp; bkpt; bkpt = *bkptp) {
681 task = bkpt->task;
682 if (bkpt->flags & BKPT_USR_GLOBAL) {
683 if (cur_task == TASK_NULL) {
684 bkptp = &bkpt->link;
685 continue;
686 }
687 task = cur_task;
688 }
689 if ((bkpt->flags & BKPT_SET_IN_MEM)
690 && DB_CHECK_ACCESS(bkpt->address, BKPT_SIZE, task)) {
691 inst = db_get_task_value(bkpt->address, BKPT_SIZE, FALSE,
692 task);
693 if (inst != BKPT_SET(inst)) {
694 if (bkpt->flags & BKPT_USR_GLOBAL) {
695 bkptp = &bkpt->link;
696 continue;
697 }
698 db_force_delete_breakpoint(bkpt, 0, FALSE);
699 *bkptp = bkpt->link;
700 db_breakpoint_free(bkpt);
701 continue;
702 }
703 db_put_task_value(bkpt->address, BKPT_SIZE,
704 bkpt->bkpt_inst, task);
705 bkpt->flags &= ~BKPT_SET_IN_MEM;
706 }
707 bkptp = &bkpt->link;
708 }
709 db_breakpoints_inserted = FALSE;
710 }
711 }
712
713 /*
714 * Set a temporary breakpoint.
715 * The instruction is changed immediately,
716 * so the breakpoint does not have to be on the breakpoint list.
717 */
718 db_breakpoint_t
719 db_set_temp_breakpoint(
720 task_t task,
721 db_addr_t addr)
722 {
723 register db_breakpoint_t bkpt;
724
725 bkpt = db_breakpoint_alloc();
726 if (bkpt == 0) {
727 db_printf("Too many breakpoints.\n");
728 return 0;
729 }
730 bkpt->task = task;
731 bkpt->address = addr;
732 bkpt->flags = BKPT_TEMP;
733 bkpt->threads = 0;
734 if (db_add_thread_breakpoint(bkpt, 0, 1, FALSE) < 0) {
735 if (bkpt)
736 db_breakpoint_free(bkpt);
737 db_printf("Too many thread_breakpoints.\n");
738 return 0;
739 }
740 bkpt->bkpt_inst = db_get_task_value(bkpt->address, BKPT_SIZE,
741 FALSE, task);
742 db_put_task_value(bkpt->address, BKPT_SIZE,
743 BKPT_SET(bkpt->bkpt_inst), task);
744 return bkpt;
745 }
746
747 void
748 db_delete_temp_breakpoint(
749 task_t task,
750 db_breakpoint_t bkpt)
751 {
752 db_put_task_value(bkpt->address, BKPT_SIZE, bkpt->bkpt_inst, task);
753 db_delete_thread_breakpoint(bkpt, 0);
754 db_breakpoint_free(bkpt);
755 }
756
757 /*
758 * List breakpoints.
759 */
760 void
761 db_list_breakpoints(void)
762 {
763 register db_breakpoint_t bkpt;
764
765 if (db_breakpoint_list == 0) {
766 db_printf("No breakpoints set\n");
767 return;
768 }
769
770 db_printf(" No Space Task.Act Cnt Address(Cond)\n");
771 for (bkpt = db_breakpoint_list;
772 bkpt != 0;
773 bkpt = bkpt->link)
774 {
775 register db_thread_breakpoint_t tp;
776 int task_id;
777 int act_id;
778
779 if (bkpt->threads) {
780 for (tp = bkpt->threads; tp; tp = tp->tb_next) {
781 db_printf("%3d ", tp->tb_number);
782 if (bkpt->flags & BKPT_USR_GLOBAL)
783 db_printf("user ");
784 else if (bkpt->task == TASK_NULL)
785 db_printf("kernel ");
786 else if ((task_id = db_lookup_task(bkpt->task)) < 0)
787 db_printf("%0*X ", 2*sizeof(vm_offset_t), bkpt->task);
788 else
789 db_printf("task%-3d ", task_id);
790 if (tp->tb_task_thd == 0) {
791 db_printf("all ");
792 } else {
793 if (tp->tb_is_task) {
794 task_id = db_lookup_task((task_t)(tp->tb_task_thd));
795 if (task_id < 0)
796 db_printf("%0*X ", 2*sizeof(vm_offset_t),
797 tp->tb_task_thd);
798 else
799 db_printf("task%03d ", task_id);
800 } else {
801 thread_act_t thd = (thread_act_t)(tp->tb_task_thd);
802 task_id = db_lookup_task(thd->task);
803 act_id = db_lookup_task_act(thd->task, thd);
804 if (task_id < 0 || act_id < 0)
805 db_printf("%0*X ", 2*sizeof(vm_offset_t),
806 tp->tb_task_thd);
807 else
808 db_printf("task%03d.%-3d ", task_id, act_id);
809 }
810 }
811 db_printf("%3d ", tp->tb_init_count);
812 db_task_printsym(bkpt->address, DB_STGY_PROC, bkpt->task);
813 if (tp->tb_cond > 0) {
814 db_printf("(");
815 db_cond_print(tp);
816 db_printf(")");
817 }
818 db_printf("\n");
819 }
820 } else {
821 if (bkpt->task == TASK_NULL)
822 db_printf(" ? kernel ");
823 else
824 db_printf("%*X ", 2*sizeof(vm_offset_t), bkpt->task);
825 db_printf("(?) ");
826 db_task_printsym(bkpt->address, DB_STGY_PROC, bkpt->task);
827 db_printf("\n");
828 }
829 }
830 }
831
832 void
833 db_delete_all_breakpoints(
834 task_t task)
835 {
836 register db_breakpoint_t bkpt;
837
838 bkpt = db_breakpoint_list;
839 while ( bkpt != 0 ) {
840 if (bkpt->task == task ||
841 (task != TASK_NULL && (bkpt->flags & BKPT_USR_GLOBAL))) {
842 db_delete_breakpoint(task, bkpt->address, 0);
843 bkpt = db_breakpoint_list;
844 }
845 else
846 bkpt = bkpt->link;
847
848 }
849 }
850
851 /* Delete breakpoint */
852 void
853 db_delete_cmd(void)
854 {
855 register int n;
856 thread_act_t thr_act;
857 vm_offset_t task_thd;
858 boolean_t user_global = FALSE;
859 boolean_t task_bpt = FALSE;
860 boolean_t user_space = FALSE;
861 boolean_t thd_bpt = FALSE;
862 db_expr_t addr;
863 int t;
864
865 t = db_read_token();
866 if (t == tSLASH) {
867 t = db_read_token();
868 if (t != tIDENT) {
869 db_printf("Bad modifier \"%s\"\n", db_tok_string);
870 db_error(0);
871 }
872 user_global = db_option(db_tok_string, 'U');
873 user_space = (user_global)? TRUE: db_option(db_tok_string, 'u');
874 task_bpt = db_option(db_tok_string, 'T');
875 thd_bpt = db_option(db_tok_string, 't');
876 if (task_bpt && user_global)
877 db_error("Cannot specify both 'T' and 'U' option\n");
878 t = db_read_token();
879 }
880
881 if ( t == tSTAR ) {
882 db_printf("Delete ALL breakpoints\n");
883 db_delete_all_breakpoints( (task_t)task_bpt );
884 return;
885 }
886
887 if (t == tHASH) {
888 db_thread_breakpoint_t tbp;
889 db_breakpoint_t bkpt;
890
891 if (db_read_token() != tNUMBER) {
892 db_printf("Bad break point number #%s\n", db_tok_string);
893 db_error(0);
894 }
895 if ((tbp = db_find_breakpoint_number(db_tok_number, &bkpt)) == 0) {
896 db_printf("No such break point #%d\n", db_tok_number);
897 db_error(0);
898 }
899 db_delete_breakpoint(bkpt->task, bkpt->address, tbp->tb_task_thd);
900 return;
901 }
902 db_unread_token(t);
903 if (!db_expression(&addr)) {
904 /*
905 * We attempt to pick up the user_space indication from db_dot,
906 * so that a plain "d" always works.
907 */
908 addr = (db_expr_t)db_dot;
909 if (!user_space && !DB_VALID_ADDRESS(addr, FALSE))
910 user_space = TRUE;
911 }
912 if (!DB_VALID_ADDRESS(addr, user_space)) {
913 db_printf("Address %#X is not in %s space\n", addr,
914 (user_space)? "user": "kernel");
915 db_error(0);
916 }
917 if (thd_bpt || task_bpt) {
918 for (n = 0; db_get_next_act(&thr_act, n); n++) {
919 if (thr_act == THR_ACT_NULL)
920 db_error("No active thr_act\n");
921 if (task_bpt) {
922 if (thr_act->task == TASK_NULL)
923 db_error("No task\n");
924 task_thd = (vm_offset_t) (thr_act->task);
925 } else
926 task_thd = (user_global)? 0: (vm_offset_t) thr_act;
927 db_delete_breakpoint(db_target_space(thr_act, user_space),
928 (db_addr_t)addr, task_thd);
929 }
930 } else {
931 db_delete_breakpoint(db_target_space(THR_ACT_NULL, user_space),
932 (db_addr_t)addr, 0);
933 }
934 }
935
936 /* Set breakpoint with skip count */
937 #include <mach/machine/vm_param.h>
938
939 void
940 db_breakpoint_cmd(
941 db_expr_t addr,
942 int have_addr,
943 db_expr_t count,
944 char * modif)
945 {
946 register int n;
947 thread_act_t thr_act;
948 boolean_t user_global = db_option(modif, 'U');
949 boolean_t task_bpt = db_option(modif, 'T');
950 boolean_t user_space;
951
952 if (count == -1)
953 count = 1;
954 #if 0 /* CHECKME */
955 if (!task_bpt && db_option(modif,'t'))
956 task_bpt = TRUE;
957 #endif
958
959 if (task_bpt && user_global)
960 db_error("Cannot specify both 'T' and 'U'\n");
961 user_space = (user_global)? TRUE: db_option(modif, 'u');
962 if (user_space && db_access_level < DB_ACCESS_CURRENT)
963 db_error("User space break point is not supported\n");
964 if ((!task_bpt || !user_space) &&
965 !DB_VALID_ADDRESS(addr, user_space)) {
966 /* if the user has explicitly specified user space,
967 do not insert a breakpoint into the kernel */
968 if (user_space)
969 db_error("Invalid user space address\n");
970 user_space = TRUE;
971 db_printf("%#X is in user space\n", addr);
972 db_printf("kernel is from %#X to %#x\n", VM_MIN_KERNEL_ADDRESS, VM_MAX_KERNEL_ADDRESS);
973 }
974 if (db_option(modif, 't') || task_bpt) {
975 for (n = 0; db_get_next_act(&thr_act, n); n++) {
976 if (thr_act == THR_ACT_NULL)
977 db_error("No active thr_act\n");
978 if (task_bpt && thr_act->task == TASK_NULL)
979 db_error("No task\n");
980 if (db_access_level <= DB_ACCESS_CURRENT && user_space
981 && thr_act->task != db_current_space())
982 db_error("Cannot set break point in inactive user space\n");
983 db_set_breakpoint(db_target_space(thr_act, user_space),
984 (db_addr_t)addr, count,
985 (user_global)? THR_ACT_NULL: thr_act,
986 task_bpt);
987 }
988 } else {
989 db_set_breakpoint(db_target_space(THR_ACT_NULL, user_space),
990 (db_addr_t)addr,
991 count, THR_ACT_NULL, FALSE);
992 }
993 }
994
995 /* list breakpoints */
996 void
997 db_listbreak_cmd(void)
998 {
999 db_list_breakpoints();
1000 }