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