1/*
2 * Copyright (c) 2000-2005 Apple Computer, Inc. All rights reserved.
3 *
4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
5 *
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. The rights granted to you under the License
10 * may not be used to create, or enable the creation or redistribution of,
11 * unlawful or unlicensed copies of an Apple operating system, or to
12 * circumvent, violate, or enable the circumvention or violation of, any
13 * terms of an Apple operating system software license agreement.
14 *
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
17 *
18 * The Original Code and all software distributed under the License are
19 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
22 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
23 * Please see the License for the specific language governing rights and
24 * limitations under the License.
25 *
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
27 */
28/*
29 * @OSF_COPYRIGHT@
30 */
31/*
32 */
33/*
34 *	Author: David B. Golub, Carnegie Mellon University
35 *	Date:	7/90
36 */
37
38/*
39 * Breakpoints.
40 */
41#include <mach/boolean.h>
42#include <machine/db_machdep.h>
43#include <ddb/db_lex.h>
44#include <ddb/db_break.h>
45#include <ddb/db_access.h>
46#include <ddb/db_sym.h>
47#include <ddb/db_variables.h>
48#include <ddb/db_command.h>
49#include <ddb/db_cond.h>
50#include <ddb/db_expr.h>
51#include <ddb/db_output.h>		/* For db_printf() */
52#include <ddb/db_task_thread.h>
53#include <kern/thread.h>
54
55#define	NBREAKPOINTS	100
56#define NTHREAD_LIST	(NBREAKPOINTS*3)
57
58struct db_breakpoint	db_break_table[NBREAKPOINTS];
59db_breakpoint_t		db_next_free_breakpoint = &db_break_table[0];
60db_breakpoint_t		db_free_breakpoints = 0;
61db_breakpoint_t		db_breakpoint_list = 0;
62
63static struct db_thread_breakpoint	db_thread_break_list[NTHREAD_LIST];
64static db_thread_breakpoint_t		db_free_thread_break_list = 0;
65static boolean_t			db_thread_break_init = FALSE;
66static int				db_breakpoint_number = 0;
67
68/* Prototypes for functions local to this file.  XXX -- should be static!
69 */
70static int db_add_thread_breakpoint(
71	register db_breakpoint_t	bkpt,
72	vm_offset_t			task_thd,
73	int				count,
74	boolean_t			task_bpt);
75
76static int db_delete_thread_breakpoint(
77	register db_breakpoint_t	bkpt,
78	vm_offset_t			task_thd);
79
80static db_thread_breakpoint_t db_find_thread_breakpoint(
81	db_breakpoint_t	bkpt,
82	thread_t	thr_act);
83
84static void db_force_delete_breakpoint(
85	db_breakpoint_t	bkpt,
86	vm_offset_t	task_thd,
87	boolean_t	is_task);
88
89db_breakpoint_t db_breakpoint_alloc(void);
90
91void db_breakpoint_free(register db_breakpoint_t bkpt);
92
93void db_delete_breakpoint(
94	task_t		task,
95	db_addr_t	addr,
96	vm_offset_t	task_thd);
97
98void
99db_delete_all_breakpoints(
100	task_t		task);
101
102void db_list_breakpoints(void);
103
104
105
106db_breakpoint_t
107db_breakpoint_alloc(void)
108{
109	register db_breakpoint_t	bkpt;
110
111	if ((bkpt = db_free_breakpoints) != 0) {
112	    db_free_breakpoints = bkpt->link;
113	    return (bkpt);
114	}
115	if (db_next_free_breakpoint == &db_break_table[NBREAKPOINTS]) {
116	    db_printf("All breakpoints used.\n");
117	    return (0);
118	}
119	bkpt = db_next_free_breakpoint;
120	db_next_free_breakpoint++;
121
122	return (bkpt);
123}
124
125void
126db_breakpoint_free(register db_breakpoint_t bkpt)
127{
128	bkpt->link = db_free_breakpoints;
129	db_free_breakpoints = bkpt;
130}
131
132static int
133db_add_thread_breakpoint(
134	register db_breakpoint_t	bkpt,
135	vm_offset_t			task_thd,
136	int				count,
137	boolean_t			task_bpt)
138{
139	register db_thread_breakpoint_t tp;
140
141	if (db_thread_break_init == FALSE) {
142	    for (tp = db_thread_break_list;
143		tp < &db_thread_break_list[NTHREAD_LIST-1]; tp++)
144		tp->tb_next = tp+1;
145	    tp->tb_next = 0;
146	    db_free_thread_break_list = db_thread_break_list;
147	    db_thread_break_init = TRUE;
148	}
149	if (db_free_thread_break_list == 0)
150	    return (-1);
151	tp = db_free_thread_break_list;
152	db_free_thread_break_list = tp->tb_next;
153	tp->tb_is_task = task_bpt;
154	tp->tb_task_thd = task_thd;
155	tp->tb_count = count;
156	tp->tb_init_count = count;
157	tp->tb_cond = 0;
158	tp->tb_number = ++db_breakpoint_number;
159	tp->tb_next = bkpt->threads;
160	bkpt->threads = tp;
161	return(0);
162}
163
164static int
165db_delete_thread_breakpoint(
166	register db_breakpoint_t	bkpt,
167	vm_offset_t			task_thd)
168{
169	register db_thread_breakpoint_t tp;
170	register db_thread_breakpoint_t *tpp;
171
172	if (task_thd == 0) {
173	    /* delete all the thread-breakpoints */
174
175	    for (tpp = &bkpt->threads; (tp = *tpp) != 0; tpp = &tp->tb_next)
176		db_cond_free(tp);
177
178	    *tpp = db_free_thread_break_list;
179	    db_free_thread_break_list = bkpt->threads;
180	    bkpt->threads = 0;
181	    return 0;
182	} else {
183	    /* delete the specified thread-breakpoint */
184
185	    for (tpp = &bkpt->threads; (tp = *tpp) != 0; tpp = &tp->tb_next)
186		if (tp->tb_task_thd == task_thd) {
187		    db_cond_free(tp);
188		    *tpp = tp->tb_next;
189		    tp->tb_next = db_free_thread_break_list;
190		    db_free_thread_break_list = tp;
191		    return 0;
192		}
193
194	    return -1;	/* not found */
195	}
196}
197
198static db_thread_breakpoint_t
199db_find_thread_breakpoint(
200	db_breakpoint_t	bkpt,
201	thread_t	thr_act)
202{
203	register db_thread_breakpoint_t tp;
204	register task_t task =
205			(thr_act == THREAD_NULL)
206					? TASK_NULL : thr_act->task;
207
208	for (tp = bkpt->threads; tp; tp = tp->tb_next) {
209	    if (tp->tb_is_task) {
210		if (tp->tb_task_thd == (vm_offset_t)task)
211		    break;
212		continue;
213	    }
214	    if (tp->tb_task_thd == (vm_offset_t)thr_act || tp->tb_task_thd == 0)
215		break;
216	}
217	return(tp);
218}
219
220db_thread_breakpoint_t
221db_find_thread_breakpoint_here(
222	task_t		task,
223	db_addr_t	addr)
224{
225	db_breakpoint_t bkpt;
226
227	bkpt = db_find_breakpoint(task, (db_addr_t)addr);
228	if (bkpt == 0)
229	    return(0);
230	return(db_find_thread_breakpoint(bkpt, current_thread()));
231}
232
233db_thread_breakpoint_t
234db_find_breakpoint_number(
235	int		num,
236	db_breakpoint_t *bkptp)
237{
238	register db_thread_breakpoint_t tp;
239	register db_breakpoint_t bkpt;
240
241	for (bkpt = db_breakpoint_list; bkpt != 0; bkpt = bkpt->link) {
242	    for (tp = bkpt->threads; tp; tp = tp->tb_next) {
243		if (tp->tb_number == num) {
244		    if (bkptp)
245			*bkptp = bkpt;
246		    return(tp);
247		}
248	    }
249	}
250	return(0);
251}
252
253static void
254db_force_delete_breakpoint(
255	db_breakpoint_t	bkpt,
256	vm_offset_t	task_thd,
257	boolean_t	is_task)
258{
259	db_printf("deleted a stale breakpoint at ");
260	if (bkpt->task == TASK_NULL || db_lookup_task(bkpt->task) >= 0)
261	   db_task_printsym(bkpt->address, DB_STGY_PROC, bkpt->task);
262	else
263	   db_printf("%#X", bkpt->address);
264	if (bkpt->task)
265	   db_printf(" in task %X", bkpt->task);
266	if (task_thd)
267	   db_printf(" for %s %X", (is_task)? "task": "thr_act", task_thd);
268	db_printf("\n");
269	db_delete_thread_breakpoint(bkpt, task_thd);
270}
271
272void
273db_check_breakpoint_valid(void)
274{
275	register db_thread_breakpoint_t tbp, tbp_next;
276	register db_breakpoint_t bkpt, *bkptp;
277
278	bkptp = &db_breakpoint_list;
279	for (bkpt = *bkptp; bkpt; bkpt = *bkptp) {
280	    if (bkpt->task != TASK_NULL) {
281		if (db_lookup_task(bkpt->task) < 0) {
282		    db_force_delete_breakpoint(bkpt, 0, FALSE);
283		    *bkptp = bkpt->link;
284		    db_breakpoint_free(bkpt);
285		    continue;
286		}
287	    } else {
288		for (tbp = bkpt->threads; tbp; tbp = tbp_next) {
289		    tbp_next = tbp->tb_next;
290		    if (tbp->tb_task_thd == 0)
291			continue;
292		    if ((tbp->tb_is_task &&
293			 db_lookup_task((task_t)(tbp->tb_task_thd)) < 0) ||
294			(!tbp->tb_is_task &&
295			 db_lookup_act((thread_t)(tbp->tb_task_thd)) < 0)) {
296			db_force_delete_breakpoint(bkpt,
297					tbp->tb_task_thd, tbp->tb_is_task);
298		    }
299		}
300		if (bkpt->threads == 0) {
301		    db_put_task_value(bkpt->address, BKPT_SIZE,
302				 bkpt->bkpt_inst, bkpt->task);
303		    *bkptp = bkpt->link;
304		    db_breakpoint_free(bkpt);
305		    continue;
306		}
307	    }
308	    bkptp = &bkpt->link;
309	}
310}
311
312void
313db_set_breakpoint(
314	task_t		task,
315	db_addr_t	addr,
316	int		count,
317	thread_t	thr_act,
318	boolean_t	task_bpt)
319{
320	register db_breakpoint_t bkpt;
321	db_breakpoint_t alloc_bkpt = 0;
322	vm_offset_t task_thd;
323
324	bkpt = db_find_breakpoint(task, addr);
325	if (bkpt) {
326	    if (thr_act == THREAD_NULL
327		|| db_find_thread_breakpoint(bkpt, thr_act)) {
328		db_printf("Already set.\n");
329		return;
330	    }
331	} else {
332	    if (!DB_CHECK_ACCESS(addr, BKPT_SIZE, task)) {
333		if (task) {
334		    db_printf("Warning: non-resident page for breakpoint at %llX",
335			      (unsigned long long)addr);
336		    db_printf(" in task %lX.\n", task);
337		} else {
338		    db_printf("Cannot set breakpoint at %llX in kernel space.\n",
339			      (unsigned long long)addr);
340		    return;
341		}
342	    }
343	    alloc_bkpt = bkpt = db_breakpoint_alloc();
344	    if (bkpt == 0) {
345		db_printf("Too many breakpoints.\n");
346		return;
347	    }
348	    bkpt->task = task;
349	    bkpt->flags = (task && thr_act == THREAD_NULL)?
350				(BKPT_USR_GLOBAL|BKPT_1ST_SET): 0;
351	    bkpt->address = addr;
352	    bkpt->threads = 0;
353	}
354	if (db_breakpoint_list == 0)
355	    db_breakpoint_number = 0;
356	task_thd = (task_bpt)	? (vm_offset_t)(thr_act->task)
357				: (vm_offset_t)thr_act;
358	if (db_add_thread_breakpoint(bkpt, task_thd, count, task_bpt) < 0) {
359	    if (alloc_bkpt)
360		db_breakpoint_free(alloc_bkpt);
361	    db_printf("Too many thread_breakpoints.\n");
362	} else {
363	    db_printf("set breakpoint #%x\n", db_breakpoint_number);
364	    if (alloc_bkpt) {
365		bkpt->link = db_breakpoint_list;
366		db_breakpoint_list = bkpt;
367	    }
368	}
369}
370
371void
372db_delete_breakpoint(
373	task_t		task,
374	db_addr_t	addr,
375	vm_offset_t	task_thd)
376{
377	register db_breakpoint_t	bkpt;
378	register db_breakpoint_t	*prev;
379
380	for (prev = &db_breakpoint_list; (bkpt = *prev) != 0;
381					     prev = &bkpt->link) {
382	    if ((bkpt->task == task
383		   || (task != TASK_NULL && (bkpt->flags & BKPT_USR_GLOBAL)))
384		&& bkpt->address == addr)
385		break;
386	}
387	if (bkpt && (bkpt->flags & BKPT_SET_IN_MEM)) {
388	    db_printf("cannot delete it now.\n");
389	    return;
390	}
391	if (bkpt == 0
392	    || db_delete_thread_breakpoint(bkpt, task_thd) < 0) {
393	    db_printf("Not set.\n");
394	    return;
395	}
396	if (bkpt->threads == 0) {
397	    *prev = bkpt->link;
398	    db_breakpoint_free(bkpt);
399	}
400}
401
402db_breakpoint_t
403db_find_breakpoint(
404	task_t		task,
405	db_addr_t	addr)
406{
407	register db_breakpoint_t	bkpt;
408
409	for (bkpt = db_breakpoint_list; bkpt != 0; bkpt = bkpt->link) {
410	    if ((bkpt->task == task
411		  || (task != TASK_NULL && (bkpt->flags & BKPT_USR_GLOBAL)))
412		&& bkpt->address == addr)
413		return (bkpt);
414	}
415	return (0);
416}
417
418boolean_t
419db_find_breakpoint_here(
420	task_t		task,
421	db_addr_t	addr)
422{
423	register db_breakpoint_t	bkpt;
424
425	for (bkpt = db_breakpoint_list; bkpt != 0; bkpt = bkpt->link) {
426	    if ((bkpt->task == task
427		   || (task != TASK_NULL && (bkpt->flags & BKPT_USR_GLOBAL)))
428                && bkpt->address == addr)
429		return(TRUE);
430	    if ((bkpt->flags & BKPT_USR_GLOBAL) == 0 &&
431		  DB_PHYS_EQ(task, addr, bkpt->task, bkpt->address))
432		return (TRUE);
433	}
434	return(FALSE);
435}
436
437boolean_t	db_breakpoints_inserted = TRUE;
438
439void
440db_set_breakpoints(void)
441{
442	register db_breakpoint_t bkpt;
443	register task_t	task;
444	db_expr_t	inst;
445	thread_t	cur_act = current_thread();
446	task_t		cur_task =
447				(cur_act) ?
448					cur_act->task : TASK_NULL;
449	boolean_t	inserted = TRUE;
450
451	if (!db_breakpoints_inserted) {
452	    for (bkpt = db_breakpoint_list; bkpt != 0; bkpt = bkpt->link) {
453		if (bkpt->flags & BKPT_SET_IN_MEM)
454		    continue;
455		task = bkpt->task;
456		if (bkpt->flags & BKPT_USR_GLOBAL) {
457		    if ((bkpt->flags & BKPT_1ST_SET) == 0) {
458		        if (cur_task == TASK_NULL)
459			    continue;
460		        task = cur_task;
461		    } else
462			bkpt->flags &= ~BKPT_1ST_SET;
463		}
464		if (DB_CHECK_ACCESS(bkpt->address, BKPT_SIZE, task)) {
465		    inst = db_get_task_value(bkpt->address, BKPT_SIZE, FALSE,
466								task);
467		    if (inst == BKPT_SET(inst))
468			continue;
469		    bkpt->bkpt_inst = inst;
470		    db_put_task_value(bkpt->address,
471				BKPT_SIZE,
472				BKPT_SET(bkpt->bkpt_inst), task);
473		    bkpt->flags |= BKPT_SET_IN_MEM;
474		} else {
475		    inserted = FALSE;
476		}
477	    }
478	    db_breakpoints_inserted = inserted;
479	}
480}
481
482void
483db_clear_breakpoints(void)
484{
485	register db_breakpoint_t bkpt, *bkptp;
486	register task_t	 task;
487	db_expr_t inst;
488	thread_t	 cur_act = current_thread();
489	task_t	 cur_task = (cur_act) ?
490			cur_act->task: TASK_NULL;
491
492	if (db_breakpoints_inserted) {
493	    bkptp = &db_breakpoint_list;
494	    for (bkpt = *bkptp; bkpt; bkpt = *bkptp) {
495		task = bkpt->task;
496		if (bkpt->flags & BKPT_USR_GLOBAL) {
497		    if (cur_task == TASK_NULL) {
498			bkptp = &bkpt->link;
499			continue;
500		    }
501		    task = cur_task;
502		}
503		if ((bkpt->flags & BKPT_SET_IN_MEM)
504		    && DB_CHECK_ACCESS(bkpt->address, BKPT_SIZE, task)) {
505		    inst = db_get_task_value(bkpt->address, BKPT_SIZE, FALSE,
506								task);
507		    if (inst != BKPT_SET(inst)) {
508			if (bkpt->flags & BKPT_USR_GLOBAL) {
509			    bkptp = &bkpt->link;
510			    continue;
511			}
512			db_force_delete_breakpoint(bkpt, 0, FALSE);
513			*bkptp = bkpt->link;
514		        db_breakpoint_free(bkpt);
515			continue;
516		    }
517		    db_put_task_value(bkpt->address, BKPT_SIZE,
518				 bkpt->bkpt_inst, task);
519		    bkpt->flags &= ~BKPT_SET_IN_MEM;
520		}
521		bkptp = &bkpt->link;
522	    }
523	    db_breakpoints_inserted = FALSE;
524	}
525}
526
527/*
528 * Set a temporary breakpoint.
529 * The instruction is changed immediately,
530 * so the breakpoint does not have to be on the breakpoint list.
531 */
532db_breakpoint_t
533db_set_temp_breakpoint(
534	task_t		task,
535	db_addr_t	addr)
536{
537	register db_breakpoint_t	bkpt;
538
539	bkpt = db_breakpoint_alloc();
540	if (bkpt == 0) {
541	    db_printf("Too many breakpoints.\n");
542	    return 0;
543	}
544	bkpt->task = task;
545	bkpt->address = addr;
546	bkpt->flags = BKPT_TEMP;
547	bkpt->threads = 0;
548	if (db_add_thread_breakpoint(bkpt, 0, 1, FALSE) < 0) {
549	    if (bkpt)
550		db_breakpoint_free(bkpt);
551	    db_printf("Too many thread_breakpoints.\n");
552	    return 0;
553	}
554	bkpt->bkpt_inst = db_get_task_value(bkpt->address, BKPT_SIZE,
555						FALSE, task);
556	db_put_task_value(bkpt->address, BKPT_SIZE,
557				BKPT_SET(bkpt->bkpt_inst), task);
558	return bkpt;
559}
560
561void
562db_delete_temp_breakpoint(
563	task_t		task,
564	db_breakpoint_t	bkpt)
565{
566	db_put_task_value(bkpt->address, BKPT_SIZE, bkpt->bkpt_inst, task);
567	db_delete_thread_breakpoint(bkpt, 0);
568	db_breakpoint_free(bkpt);
569}
570
571/*
572 * List breakpoints.
573 */
574void
575db_list_breakpoints(void)
576{
577	register db_breakpoint_t	bkpt;
578
579	if (db_breakpoint_list == 0) {
580	    db_printf("No breakpoints set\n");
581	    return;
582	}
583
584	db_printf(" No  Space    Task.Act    Cnt  Address(Cond)\n");
585	for (bkpt = db_breakpoint_list;
586	     bkpt != 0;
587	     bkpt = bkpt->link)
588	{
589	    register 	db_thread_breakpoint_t tp;
590	    int		task_id;
591	    int		act_id;
592
593	    if (bkpt->threads) {
594		for (tp = bkpt->threads; tp; tp = tp->tb_next) {
595		    db_printf("%3d  ", tp->tb_number);
596		    if (bkpt->flags & BKPT_USR_GLOBAL)
597			db_printf("user     ");
598		    else if (bkpt->task == TASK_NULL)
599			db_printf("kernel   ");
600		    else if ((task_id = db_lookup_task(bkpt->task)) < 0)
601			db_printf("%0*X ", 2*sizeof(vm_offset_t), bkpt->task);
602		    else
603			db_printf("task%-3d  ", task_id);
604		    if (tp->tb_task_thd == 0) {
605			db_printf("all         ");
606		    } else {
607			if (tp->tb_is_task) {
608			    task_id = db_lookup_task((task_t)(tp->tb_task_thd));
609			    if (task_id < 0)
610				db_printf("%0*X    ", 2*sizeof(vm_offset_t),
611					   tp->tb_task_thd);
612			    else
613				db_printf("task%03d     ", task_id);
614			} else {
615			    thread_t thd = (thread_t)(tp->tb_task_thd);
616			    task_id = db_lookup_task(thd->task);
617			    act_id = db_lookup_task_act(thd->task, thd);
618			    if (task_id < 0 || act_id < 0)
619				db_printf("%0*X    ", 2*sizeof(vm_offset_t),
620						tp->tb_task_thd);
621			    else
622				db_printf("task%03d.%-3d ", task_id, act_id);
623			}
624		    }
625	    	    db_printf("%3d  ", tp->tb_init_count);
626		    db_task_printsym(bkpt->address, DB_STGY_PROC, bkpt->task);
627		    if (tp->tb_cond > 0) {
628			db_printf("(");
629			db_cond_print(tp);
630			db_printf(")");
631		    }
632		    db_printf("\n");
633		}
634	    } else {
635		if (bkpt->task == TASK_NULL)
636		    db_printf("  ?  kernel   ");
637		else
638		    db_printf("%*X ", 2*sizeof(vm_offset_t), bkpt->task);
639		db_printf("(?)              ");
640		db_task_printsym(bkpt->address, DB_STGY_PROC, bkpt->task);
641		db_printf("\n");
642	    }
643	}
644}
645
646void
647db_delete_all_breakpoints(
648	task_t		task)
649{
650	register db_breakpoint_t	bkpt;
651
652	bkpt = db_breakpoint_list;
653	while ( bkpt != 0 ) {
654		if (bkpt->task == task ||
655		    (task != TASK_NULL && (bkpt->flags & BKPT_USR_GLOBAL))) {
656			db_delete_breakpoint(task, bkpt->address, 0);
657			bkpt = db_breakpoint_list;
658		}
659		else
660			bkpt = bkpt->link;
661
662	}
663}
664
665/* Delete breakpoint */
666void
667db_delete_cmd(void)
668{
669	register int n;
670	thread_t 	 thr_act;
671	vm_offset_t task_thd;
672	boolean_t user_global = FALSE;
673	boolean_t task_bpt = FALSE;
674	boolean_t user_space = FALSE;
675	boolean_t thd_bpt = FALSE;
676	db_expr_t addr;
677	int t;
678
679	t = db_read_token();
680	if (t == tSLASH) {
681	    t = db_read_token();
682	    if (t != tIDENT) {
683		db_printf("Bad modifier \"%s\"\n", db_tok_string);
684		db_error(0);
685	    }
686	    user_global = db_option(db_tok_string, 'U');
687	    user_space = (user_global)? TRUE: db_option(db_tok_string, 'u');
688	    task_bpt = db_option(db_tok_string, 'T');
689	    thd_bpt = db_option(db_tok_string, 't');
690	    if (task_bpt && user_global)
691		db_error("Cannot specify both 'T' and 'U' option\n");
692	    t = db_read_token();
693	}
694
695	if ( t == tSTAR ) {
696		db_printf("Delete ALL breakpoints\n");
697    		db_delete_all_breakpoints( (task_t)task_bpt );
698    		return;
699	}
700
701	if (t == tHASH) {
702	    db_thread_breakpoint_t tbp;
703	    db_breakpoint_t bkpt = 0;
704
705	    if (db_read_token() != tNUMBER) {
706		db_printf("Bad break point number #%s\n", db_tok_string);
707		db_error(0);
708	    }
709	    if ((tbp = db_find_breakpoint_number(db_tok_number, &bkpt)) == 0) {
710	        db_printf("No such break point #%d\n", db_tok_number);
711	        db_error(0);
712	    }
713	    db_delete_breakpoint(bkpt->task, bkpt->address, tbp->tb_task_thd);
714	    return;
715	}
716	db_unread_token(t);
717	if (!db_expression(&addr)) {
718	    /*
719	     *	We attempt to pick up the user_space indication from db_dot,
720	     *	so that a plain "d" always works.
721	     */
722	    addr = (db_expr_t)db_dot;
723	    if (!user_space && !DB_VALID_ADDRESS(addr, FALSE))
724		user_space = TRUE;
725	}
726	if (!DB_VALID_ADDRESS(addr, user_space)) {
727	    db_printf("Address %#llX is not in %s space\n", (unsigned long long)addr,
728			(user_space)? "user": "kernel");
729	    db_error(0);
730	}
731	if (thd_bpt || task_bpt) {
732	    for (n = 0; db_get_next_act(&thr_act, n); n++) {
733		if (thr_act == THREAD_NULL)
734		    db_error("No active thr_act\n");
735		if (task_bpt) {
736		    if (thr_act->task == TASK_NULL)
737			db_error("No task\n");
738		    task_thd = (vm_offset_t) (thr_act->task);
739		} else
740		    task_thd = (user_global)? 0: (vm_offset_t) thr_act;
741		db_delete_breakpoint(db_target_space(thr_act, user_space),
742					(db_addr_t)addr, task_thd);
743	    }
744	} else {
745	    db_delete_breakpoint(db_target_space(THREAD_NULL, user_space),
746					 (db_addr_t)addr, 0);
747	}
748}
749
750/* Set breakpoint with skip count */
751#include <mach/machine/vm_param.h>
752
753void
754db_breakpoint_cmd(db_expr_t addr, __unused boolean_t have_addr, db_expr_t count,
755		  char *modif)
756{
757	register int n;
758	thread_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;
762
763	if (count == (uint64_t)-1)
764	    count = 1;
765#if 0 /* CHECKME */
766	if (!task_bpt && db_option(modif,'t'))
767	  task_bpt = TRUE;
768#endif
769
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 */
779	    if (user_space)
780	      db_error("Invalid user space address\n");
781	    user_space = TRUE;
782	    db_printf("%#llX is in user space\n", (unsigned long long)addr);
783#ifdef ppc
784	    db_printf("kernel is from %#X to %#x\n", VM_MIN_KERNEL_ADDRESS, vm_last_addr);
785#else
786	    db_printf("kernel is from %#X to %#x\n", VM_MIN_KERNEL_ADDRESS, VM_MAX_KERNEL_ADDRESS);
787#endif
788	}
789	if (db_option(modif, 't') || task_bpt) {
790	    for (n = 0; db_get_next_act(&thr_act, n); n++) {
791		if (thr_act == THREAD_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)? THREAD_NULL: thr_act,
801					task_bpt);
802	    }
803	} else {
804	    db_set_breakpoint(db_target_space(THREAD_NULL, user_space),
805				 (db_addr_t)addr,
806				 count, THREAD_NULL, FALSE);
807	}
808}
809
810/* list breakpoints */
811void
812db_listbreak_cmd(__unused db_expr_t addr, __unused boolean_t have_addr,
813		 __unused db_expr_t count, __unused char *modif)
814{
815	db_list_breakpoints();
816}
817