Source to osfmk/ddb/db_break.c
/*
* Copyright (c) 2000 Apple Computer, Inc. All rights reserved.
*
* @APPLE_LICENSE_HEADER_START@
*
* The contents of this file constitute Original Code as defined in and
* are subject to the Apple Public Source License Version 1.1 (the
* "License"). You may not use this file except in compliance with the
* License. Please obtain a copy of the License at
* http://www.apple.com/publicsource and read it before using this file.
*
* This Original Code and all software distributed under the License are
* distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER
* EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
* INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the
* License for the specific language governing rights and limitations
* under the License.
*
* @APPLE_LICENSE_HEADER_END@
*/
/*
* @OSF_COPYRIGHT@
*/
/*
* HISTORY
*
* Revision 1.1.1.1 1998/09/22 21:05:47 wsanchez
* Import of Mac OS X kernel (~semeria)
*
* Revision 1.2 1998/04/29 17:35:26 mburg
* MK7.3 merger
*
* Revision 1.2.47.1 1998/02/03 09:23:57 gdt
* Merge up to MK7.3
* [1998/02/03 09:10:14 gdt]
*
* Revision 1.2.45.1 1997/03/27 18:46:16 barbou
* ri-osc CR1557: re-enable thread-specific breakpoints.
* [1995/09/20 15:23:46 bolinger]
* [97/02/25 barbou]
*
* Revision 1.2.21.6 1996/01/09 19:15:21 devrcs
* Changed declarations of 'register foo' to 'register int foo'
* Fixed printfs which print addresses.
* [1995/12/01 21:41:51 jfraser]
*
* Merged '64-bit safe' changes from DEC alpha port.
* [1995/11/21 18:02:40 jfraser]
*
* Revision 1.2.21.5 1995/04/07 18:52:54 barbou
* Allow breakpoints on non-resident pages. The breakpoint will
* actually be set when the page is paged in.
* [93/09/23 barbou]
* [95/03/08 barbou]
*
* Revision 1.2.21.4 1995/02/23 21:43:19 alanl
* Merged with DIPC2_SHARED.
* [1995/01/04 20:15:04 alanl]
*
* Revision 1.2.28.1 1994/11/04 09:52:15 dwm
* mk6 CR668 - 1.3b26 merge
* * Revision 1.2.4.5 1994/05/06 18:38:52 tmt
* Merged osc1.3dec/shared with osc1.3b19
* Moved struct db_breakpoint from here to db_break.h.
* Merge Alpha changes into osc1.312b source code.
* 64bit cleanup.
* * End1.3merge
* [1994/11/04 08:49:10 dwm]
*
* Revision 1.2.21.2 1994/09/23 01:17:57 ezf
* change marker to not FREE
* [1994/09/22 21:09:19 ezf]
*
* Revision 1.2.21.1 1994/06/11 21:11:24 bolinger
* Merge up to NMK17.2.
* [1994/06/11 20:01:06 bolinger]
*
* Revision 1.2.25.2 1994/10/28 18:56:21 rwd
* Delint.
* [94/10/28 rwd]
*
* Revision 1.2.25.1 1994/08/04 01:42:15 mmp
* 23-Jun-94 Stan Smith ([email protected])
* Let d * delete all breakpoints.
* [1994/06/28 13:54:00 sjs]
*
* Revision 1.2.19.2 1994/04/11 09:34:22 bernadat
* Moved db_breakpoint struct declaration to db_break.h
* [94/03/16 bernadat]
*
* Revision 1.2.19.1 1994/02/08 10:57:22 bernadat
* When setting a breakpoint, force user_space if breakpoint is
* outside kernel_space (like in the case of an emulator).
* [93/09/27 paire]
*
* Changed silly decimal display to hex (to match input conventions).
* Change from NORMA_MK14.6 [93/01/09 sjs]
* [93/07/16 bernadat]
* [94/02/07 bernadat]
*
* Revision 1.2.4.3 1993/07/27 18:26:48 elliston
* Add ANSI prototypes. CR #9523.
* [1993/07/27 18:10:54 elliston]
*
* Revision 1.2.4.2 1993/06/09 02:19:39 gm
* Added to OSF/1 R1.3 from NMK15.0.
* [1993/06/02 20:55:42 jeffc]
*
* Revision 1.2 1993/04/19 16:01:31 devrcs
* Changes from MK78:
* Removed unused variable from db_delete_cmd().
* Added declaration for arg 'count' of db_add_thread_breakpoint().
* [92/05/18 jfriedl]
* Fixed b/tu to b/Tu work if the specified address is valid in the
* target address space but not the current user space. Explicit
* user space breakpoints (b/u, b/Tu, etc) will no longer get
* inserted into the kernel if the specified address is invalid.
* [92/04/18 danner]
* [92/12/18 bruel]
*
* Revision 1.1 1992/09/30 02:00:52 robert
* Initial revision
*
* $EndLog$
*/
/* CMU_HIST */
/*
* Revision 2.11.3.1 92/03/03 16:13:20 jeffreyh
* Pick up changes from TRUNK
* [92/02/26 10:58:37 jeffreyh]
*
* Revision 2.12 92/02/19 16:46:24 elf
* Removed one of the many user-unfriendlinesses.
* [92/02/10 17:48:25 af]
*
* Revision 2.11 91/11/12 11:50:24 rvb
* Fixed db_delete_cmd so that just "d" works in user space.
* [91/10/31 rpd]
* Fixed db_delete_thread_breakpoint for zero task_thd.
* [91/10/30 rpd]
*
* Revision 2.10 91/10/09 15:57:41 af
* Supported thread-oriented break points.
* [91/08/29 tak]
*
* Revision 2.9 91/07/09 23:15:39 danner
* Conditionalized db_map_addr to work right on the luna. Used a
* ifdef luna88k. This is evil, and needs to be fixed.
* [91/07/08 danner]
*
* Revision 2.2 91/04/10 22:54:50 mbj
* Grabbed 3.0 copyright/disclaimer since ddb comes from 3.0.
* [91/04/09 rvb]
*
* Revision 2.7 91/02/05 17:06:00 mrt
* Changed to new Mach copyright
* [91/01/31 16:17:01 mrt]
*
* Revision 2.6 91/01/08 15:09:03 rpd
* Added db_map_equal, db_map_current, db_map_addr.
* [90/11/10 rpd]
*
* Revision 2.5 90/11/05 14:26:32 rpd
* Initialize db_breakpoints_inserted to TRUE.
* [90/11/04 rpd]
*
* Revision 2.4 90/10/25 14:43:33 rwd
* Added map field to breakpoints.
* Added map argument to db_set_breakpoint, db_delete_breakpoint,
* db_find_breakpoint. Added db_find_breakpoint_here.
* [90/10/18 rpd]
*
* Revision 2.3 90/09/28 16:57:07 jsb
* Fixed db_breakpoint_free.
* [90/09/18 rpd]
*
* Revision 2.2 90/08/27 21:49:53 dbg
* Reflected changes in db_printsym()'s calling seq.
* [90/08/20 af]
* Clear breakpoints only if inserted.
* Reduce lint.
* [90/08/07 dbg]
* Created.
* [90/07/25 dbg]
*
*/
/* CMU_ENDHIST */
/*
* Mach Operating System
* Copyright (c) 1991,1990 Carnegie Mellon University
* All Rights Reserved.
*
* Permission to use, copy, modify and distribute this software and its
* documentation is hereby granted, provided that both the copyright
* notice and this permission notice appear in all copies of the
* software, derivative works or modified versions, and any portions
* thereof, and that both notices appear in supporting documentation.
*
* CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
* CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
* ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
*
* Carnegie Mellon requests users of this software to return to
*
* Software Distribution Coordinator or [email protected]
* School of Computer Science
* Carnegie Mellon University
* Pittsburgh PA 15213-3890
*
* any improvements or extensions that they make and grant Carnegie Mellon
* the rights to redistribute these changes.
*/
/*
*/
/*
* Author: David B. Golub, Carnegie Mellon University
* Date: 7/90
*/
/*
* Breakpoints.
*/
#include <mach/boolean.h>
#include <machine/db_machdep.h>
#include <ddb/db_lex.h>
#include <ddb/db_break.h>
#include <ddb/db_access.h>
#include <ddb/db_sym.h>
#include <ddb/db_variables.h>
#include <ddb/db_command.h>
#include <ddb/db_cond.h>
#include <ddb/db_expr.h>
#include <ddb/db_output.h> /* For db_printf() */
#include <ddb/db_task_thread.h>
#define NBREAKPOINTS 100
#define NTHREAD_LIST (NBREAKPOINTS*3)
struct db_breakpoint db_break_table[NBREAKPOINTS];
db_breakpoint_t db_next_free_breakpoint = &db_break_table[0];
db_breakpoint_t db_free_breakpoints = 0;
db_breakpoint_t db_breakpoint_list = 0;
static struct db_thread_breakpoint db_thread_break_list[NTHREAD_LIST];
static db_thread_breakpoint_t db_free_thread_break_list = 0;
static boolean_t db_thread_break_init = FALSE;
static int db_breakpoint_number = 0;
/* Prototypes for functions local to this file. XXX -- should be static!
*/
static int db_add_thread_breakpoint(
register db_breakpoint_t bkpt,
vm_offset_t task_thd,
int count,
boolean_t task_bpt);
static int db_delete_thread_breakpoint(
register db_breakpoint_t bkpt,
vm_offset_t task_thd);
static db_thread_breakpoint_t db_find_thread_breakpoint(
db_breakpoint_t bkpt,
thread_act_t thr_act);
static void db_force_delete_breakpoint(
db_breakpoint_t bkpt,
vm_offset_t task_thd,
boolean_t is_task);
db_breakpoint_t db_breakpoint_alloc(void);
void db_breakpoint_free(register db_breakpoint_t bkpt);
void db_delete_breakpoint(
task_t task,
db_addr_t addr,
vm_offset_t task_thd);
void
db_delete_all_breakpoints(
task_t task);
void db_list_breakpoints(void);
db_breakpoint_t
db_breakpoint_alloc(void)
{
register db_breakpoint_t bkpt;
if ((bkpt = db_free_breakpoints) != 0) {
db_free_breakpoints = bkpt->link;
return (bkpt);
}
if (db_next_free_breakpoint == &db_break_table[NBREAKPOINTS]) {
db_printf("All breakpoints used.\n");
return (0);
}
bkpt = db_next_free_breakpoint;
db_next_free_breakpoint++;
return (bkpt);
}
void
db_breakpoint_free(register db_breakpoint_t bkpt)
{
bkpt->link = db_free_breakpoints;
db_free_breakpoints = bkpt;
}
static int
db_add_thread_breakpoint(
register db_breakpoint_t bkpt,
vm_offset_t task_thd,
int count,
boolean_t task_bpt)
{
register db_thread_breakpoint_t tp;
if (db_thread_break_init == FALSE) {
for (tp = db_thread_break_list;
tp < &db_thread_break_list[NTHREAD_LIST-1]; tp++)
tp->tb_next = tp+1;
tp->tb_next = 0;
db_free_thread_break_list = db_thread_break_list;
db_thread_break_init = TRUE;
}
if (db_free_thread_break_list == 0)
return (-1);
tp = db_free_thread_break_list;
db_free_thread_break_list = tp->tb_next;
tp->tb_is_task = task_bpt;
tp->tb_task_thd = task_thd;
tp->tb_count = count;
tp->tb_init_count = count;
tp->tb_cond = 0;
tp->tb_number = ++db_breakpoint_number;
tp->tb_next = bkpt->threads;
bkpt->threads = tp;
return(0);
}
static int
db_delete_thread_breakpoint(
register db_breakpoint_t bkpt,
vm_offset_t task_thd)
{
register db_thread_breakpoint_t tp;
register db_thread_breakpoint_t *tpp;
if (task_thd == 0) {
/* delete all the thread-breakpoints */
for (tpp = &bkpt->threads; (tp = *tpp) != 0; tpp = &tp->tb_next)
db_cond_free(tp);
*tpp = db_free_thread_break_list;
db_free_thread_break_list = bkpt->threads;
bkpt->threads = 0;
return 0;
} else {
/* delete the specified thread-breakpoint */
for (tpp = &bkpt->threads; (tp = *tpp) != 0; tpp = &tp->tb_next)
if (tp->tb_task_thd == task_thd) {
db_cond_free(tp);
*tpp = tp->tb_next;
tp->tb_next = db_free_thread_break_list;
db_free_thread_break_list = tp;
return 0;
}
return -1; /* not found */
}
}
static db_thread_breakpoint_t
db_find_thread_breakpoint(
db_breakpoint_t bkpt,
thread_act_t thr_act)
{
register db_thread_breakpoint_t tp;
register task_t task =
(thr_act == THR_ACT_NULL || thr_act->kernel_loaded)
? TASK_NULL : thr_act->task;
for (tp = bkpt->threads; tp; tp = tp->tb_next) {
if (tp->tb_is_task) {
if (tp->tb_task_thd == (vm_offset_t)task)
break;
continue;
}
if (tp->tb_task_thd == (vm_offset_t)thr_act || tp->tb_task_thd == 0)
break;
}
return(tp);
}
db_thread_breakpoint_t
db_find_thread_breakpoint_here(
task_t task,
db_addr_t addr)
{
db_breakpoint_t bkpt;
bkpt = db_find_breakpoint(task, (db_addr_t)addr);
if (bkpt == 0)
return(0);
return(db_find_thread_breakpoint(bkpt, current_act()));
}
db_thread_breakpoint_t
db_find_breakpoint_number(
int num,
db_breakpoint_t *bkptp)
{
register db_thread_breakpoint_t tp;
register db_breakpoint_t bkpt;
for (bkpt = db_breakpoint_list; bkpt != 0; bkpt = bkpt->link) {
for (tp = bkpt->threads; tp; tp = tp->tb_next) {
if (tp->tb_number == num) {
if (bkptp)
*bkptp = bkpt;
return(tp);
}
}
}
return(0);
}
static void
db_force_delete_breakpoint(
db_breakpoint_t bkpt,
vm_offset_t task_thd,
boolean_t is_task)
{
db_printf("deleted a stale breakpoint at ");
if (bkpt->task == TASK_NULL || db_lookup_task(bkpt->task) >= 0)
db_task_printsym(bkpt->address, DB_STGY_PROC, bkpt->task);
else
db_printf("%#X", bkpt->address);
if (bkpt->task)
db_printf(" in task %X", bkpt->task);
if (task_thd)
db_printf(" for %s %X", (is_task)? "task": "thr_act", task_thd);
db_printf("\n");
db_delete_thread_breakpoint(bkpt, task_thd);
}
void
db_check_breakpoint_valid(void)
{
register db_thread_breakpoint_t tbp, tbp_next;
register db_breakpoint_t bkpt, *bkptp;
bkptp = &db_breakpoint_list;
for (bkpt = *bkptp; bkpt; bkpt = *bkptp) {
if (bkpt->task != TASK_NULL) {
if (db_lookup_task(bkpt->task) < 0) {
db_force_delete_breakpoint(bkpt, 0, FALSE);
*bkptp = bkpt->link;
db_breakpoint_free(bkpt);
continue;
}
} else {
for (tbp = bkpt->threads; tbp; tbp = tbp_next) {
tbp_next = tbp->tb_next;
if (tbp->tb_task_thd == 0)
continue;
if ((tbp->tb_is_task &&
db_lookup_task((task_t)(tbp->tb_task_thd)) < 0) ||
(!tbp->tb_is_task &&
db_lookup_act((thread_act_t)(tbp->tb_task_thd)) < 0)) {
db_force_delete_breakpoint(bkpt,
tbp->tb_task_thd, tbp->tb_is_task);
}
}
if (bkpt->threads == 0) {
db_put_task_value(bkpt->address, BKPT_SIZE,
bkpt->bkpt_inst, bkpt->task);
*bkptp = bkpt->link;
db_breakpoint_free(bkpt);
continue;
}
}
bkptp = &bkpt->link;
}
}
void
db_set_breakpoint(
task_t task,
db_addr_t addr,
int count,
thread_act_t thr_act,
boolean_t task_bpt)
{
register db_breakpoint_t bkpt;
db_breakpoint_t alloc_bkpt = 0;
vm_offset_t task_thd;
bkpt = db_find_breakpoint(task, addr);
if (bkpt) {
if (thr_act == THR_ACT_NULL
|| db_find_thread_breakpoint(bkpt, thr_act)) {
db_printf("Already set.\n");
return;
}
} else {
if (!DB_CHECK_ACCESS(addr, BKPT_SIZE, task)) {
if (task) {
db_printf("Warning: non-resident page for breakpoint at %lX",
addr);
db_printf(" in task %lX.\n", task);
} else {
db_printf("Cannot set breakpoint at %lX in kernel space.\n",
addr);
return;
}
}
alloc_bkpt = bkpt = db_breakpoint_alloc();
if (bkpt == 0) {
db_printf("Too many breakpoints.\n");
return;
}
bkpt->task = task;
bkpt->flags = (task && thr_act == THR_ACT_NULL)?
(BKPT_USR_GLOBAL|BKPT_1ST_SET): 0;
bkpt->address = addr;
bkpt->threads = 0;
}
if (db_breakpoint_list == 0)
db_breakpoint_number = 0;
task_thd = (task_bpt) ? (vm_offset_t)(thr_act->task)
: (vm_offset_t)thr_act;
if (db_add_thread_breakpoint(bkpt, task_thd, count, task_bpt) < 0) {
if (alloc_bkpt)
db_breakpoint_free(alloc_bkpt);
db_printf("Too many thread_breakpoints.\n");
} else {
db_printf("set breakpoint #%x\n", db_breakpoint_number);
if (alloc_bkpt) {
bkpt->link = db_breakpoint_list;
db_breakpoint_list = bkpt;
}
}
}
void
db_delete_breakpoint(
task_t task,
db_addr_t addr,
vm_offset_t task_thd)
{
register db_breakpoint_t bkpt;
register db_breakpoint_t *prev;
for (prev = &db_breakpoint_list; (bkpt = *prev) != 0;
prev = &bkpt->link) {
if ((bkpt->task == task
|| (task != TASK_NULL && (bkpt->flags & BKPT_USR_GLOBAL)))
&& bkpt->address == addr)
break;
}
if (bkpt && (bkpt->flags & BKPT_SET_IN_MEM)) {
db_printf("cannot delete it now.\n");
return;
}
if (bkpt == 0
|| db_delete_thread_breakpoint(bkpt, task_thd) < 0) {
db_printf("Not set.\n");
return;
}
if (bkpt->threads == 0) {
*prev = bkpt->link;
db_breakpoint_free(bkpt);
}
}
db_breakpoint_t
db_find_breakpoint(
task_t task,
db_addr_t addr)
{
register db_breakpoint_t bkpt;
for (bkpt = db_breakpoint_list; bkpt != 0; bkpt = bkpt->link) {
if ((bkpt->task == task
|| (task != TASK_NULL && (bkpt->flags & BKPT_USR_GLOBAL)))
&& bkpt->address == addr)
return (bkpt);
}
return (0);
}
boolean_t
db_find_breakpoint_here(
task_t task,
db_addr_t addr)
{
register db_breakpoint_t bkpt;
for (bkpt = db_breakpoint_list; bkpt != 0; bkpt = bkpt->link) {
if ((bkpt->task == task
|| (task != TASK_NULL && (bkpt->flags & BKPT_USR_GLOBAL)))
&& bkpt->address == addr)
return(TRUE);
if ((bkpt->flags & BKPT_USR_GLOBAL) == 0 &&
DB_PHYS_EQ(task, addr, bkpt->task, bkpt->address))
return (TRUE);
}
return(FALSE);
}
boolean_t db_breakpoints_inserted = TRUE;
void
db_set_breakpoints(void)
{
register db_breakpoint_t bkpt;
register task_t task;
db_expr_t inst;
thread_act_t cur_act = current_act();
task_t cur_task =
(cur_act && !cur_act->kernel_loaded) ?
cur_act->task : TASK_NULL;
boolean_t inserted = TRUE;
if (!db_breakpoints_inserted) {
for (bkpt = db_breakpoint_list; bkpt != 0; bkpt = bkpt->link) {
if (bkpt->flags & BKPT_SET_IN_MEM)
continue;
task = bkpt->task;
if (bkpt->flags & BKPT_USR_GLOBAL) {
if ((bkpt->flags & BKPT_1ST_SET) == 0) {
if (cur_task == TASK_NULL)
continue;
task = cur_task;
} else
bkpt->flags &= ~BKPT_1ST_SET;
}
if (DB_CHECK_ACCESS(bkpt->address, BKPT_SIZE, task)) {
inst = db_get_task_value(bkpt->address, BKPT_SIZE, FALSE,
task);
if (inst == BKPT_SET(inst))
continue;
bkpt->bkpt_inst = inst;
db_put_task_value(bkpt->address,
BKPT_SIZE,
BKPT_SET(bkpt->bkpt_inst), task);
bkpt->flags |= BKPT_SET_IN_MEM;
} else {
inserted = FALSE;
}
}
db_breakpoints_inserted = inserted;
}
}
void
db_clear_breakpoints(void)
{
register db_breakpoint_t bkpt, *bkptp;
register task_t task;
db_expr_t inst;
thread_act_t cur_act = current_act();
task_t cur_task = (cur_act && !cur_act->kernel_loaded) ?
cur_act->task: TASK_NULL;
if (db_breakpoints_inserted) {
bkptp = &db_breakpoint_list;
for (bkpt = *bkptp; bkpt; bkpt = *bkptp) {
task = bkpt->task;
if (bkpt->flags & BKPT_USR_GLOBAL) {
if (cur_task == TASK_NULL) {
bkptp = &bkpt->link;
continue;
}
task = cur_task;
}
if ((bkpt->flags & BKPT_SET_IN_MEM)
&& DB_CHECK_ACCESS(bkpt->address, BKPT_SIZE, task)) {
inst = db_get_task_value(bkpt->address, BKPT_SIZE, FALSE,
task);
if (inst != BKPT_SET(inst)) {
if (bkpt->flags & BKPT_USR_GLOBAL) {
bkptp = &bkpt->link;
continue;
}
db_force_delete_breakpoint(bkpt, 0, FALSE);
*bkptp = bkpt->link;
db_breakpoint_free(bkpt);
continue;
}
db_put_task_value(bkpt->address, BKPT_SIZE,
bkpt->bkpt_inst, task);
bkpt->flags &= ~BKPT_SET_IN_MEM;
}
bkptp = &bkpt->link;
}
db_breakpoints_inserted = FALSE;
}
}
/*
* Set a temporary breakpoint.
* The instruction is changed immediately,
* so the breakpoint does not have to be on the breakpoint list.
*/
db_breakpoint_t
db_set_temp_breakpoint(
task_t task,
db_addr_t addr)
{
register db_breakpoint_t bkpt;
bkpt = db_breakpoint_alloc();
if (bkpt == 0) {
db_printf("Too many breakpoints.\n");
return 0;
}
bkpt->task = task;
bkpt->address = addr;
bkpt->flags = BKPT_TEMP;
bkpt->threads = 0;
if (db_add_thread_breakpoint(bkpt, 0, 1, FALSE) < 0) {
if (bkpt)
db_breakpoint_free(bkpt);
db_printf("Too many thread_breakpoints.\n");
return 0;
}
bkpt->bkpt_inst = db_get_task_value(bkpt->address, BKPT_SIZE,
FALSE, task);
db_put_task_value(bkpt->address, BKPT_SIZE,
BKPT_SET(bkpt->bkpt_inst), task);
return bkpt;
}
void
db_delete_temp_breakpoint(
task_t task,
db_breakpoint_t bkpt)
{
db_put_task_value(bkpt->address, BKPT_SIZE, bkpt->bkpt_inst, task);
db_delete_thread_breakpoint(bkpt, 0);
db_breakpoint_free(bkpt);
}
/*
* List breakpoints.
*/
void
db_list_breakpoints(void)
{
register db_breakpoint_t bkpt;
if (db_breakpoint_list == 0) {
db_printf("No breakpoints set\n");
return;
}
db_printf(" No Space Task.Act Cnt Address(Cond)\n");
for (bkpt = db_breakpoint_list;
bkpt != 0;
bkpt = bkpt->link)
{
register db_thread_breakpoint_t tp;
int task_id;
int act_id;
if (bkpt->threads) {
for (tp = bkpt->threads; tp; tp = tp->tb_next) {
db_printf("%3d ", tp->tb_number);
if (bkpt->flags & BKPT_USR_GLOBAL)
db_printf("user ");
else if (bkpt->task == TASK_NULL)
db_printf("kernel ");
else if ((task_id = db_lookup_task(bkpt->task)) < 0)
db_printf("%0*X ", 2*sizeof(vm_offset_t), bkpt->task);
else
db_printf("task%-3d ", task_id);
if (tp->tb_task_thd == 0) {
db_printf("all ");
} else {
if (tp->tb_is_task) {
task_id = db_lookup_task((task_t)(tp->tb_task_thd));
if (task_id < 0)
db_printf("%0*X ", 2*sizeof(vm_offset_t),
tp->tb_task_thd);
else
db_printf("task%03d ", task_id);
} else {
thread_act_t thd = (thread_act_t)(tp->tb_task_thd);
task_id = db_lookup_task(thd->task);
act_id = db_lookup_task_act(thd->task, thd);
if (task_id < 0 || act_id < 0)
db_printf("%0*X ", 2*sizeof(vm_offset_t),
tp->tb_task_thd);
else
db_printf("task%03d.%-3d ", task_id, act_id);
}
}
db_printf("%3d ", tp->tb_init_count);
db_task_printsym(bkpt->address, DB_STGY_PROC, bkpt->task);
if (tp->tb_cond > 0) {
db_printf("(");
db_cond_print(tp);
db_printf(")");
}
db_printf("\n");
}
} else {
if (bkpt->task == TASK_NULL)
db_printf(" ? kernel ");
else
db_printf("%*X ", 2*sizeof(vm_offset_t), bkpt->task);
db_printf("(?) ");
db_task_printsym(bkpt->address, DB_STGY_PROC, bkpt->task);
db_printf("\n");
}
}
}
void
db_delete_all_breakpoints(
task_t task)
{
register db_breakpoint_t bkpt;
bkpt = db_breakpoint_list;
while ( bkpt != 0 ) {
if (bkpt->task == task ||
(task != TASK_NULL && (bkpt->flags & BKPT_USR_GLOBAL))) {
db_delete_breakpoint(task, bkpt->address, 0);
bkpt = db_breakpoint_list;
}
else
bkpt = bkpt->link;
}
}
/* Delete breakpoint */
void
db_delete_cmd(void)
{
register int n;
thread_act_t thr_act;
vm_offset_t task_thd;
boolean_t user_global = FALSE;
boolean_t task_bpt = FALSE;
boolean_t user_space = FALSE;
boolean_t thd_bpt = FALSE;
db_expr_t addr;
int t;
t = db_read_token();
if (t == tSLASH) {
t = db_read_token();
if (t != tIDENT) {
db_printf("Bad modifier \"%s\"\n", db_tok_string);
db_error(0);
}
user_global = db_option(db_tok_string, 'U');
user_space = (user_global)? TRUE: db_option(db_tok_string, 'u');
task_bpt = db_option(db_tok_string, 'T');
thd_bpt = db_option(db_tok_string, 't');
if (task_bpt && user_global)
db_error("Cannot specify both 'T' and 'U' option\n");
t = db_read_token();
}
if ( t == tSTAR ) {
db_printf("Delete ALL breakpoints\n");
db_delete_all_breakpoints( (task_t)task_bpt );
return;
}
if (t == tHASH) {
db_thread_breakpoint_t tbp;
db_breakpoint_t bkpt;
if (db_read_token() != tNUMBER) {
db_printf("Bad break point number #%s\n", db_tok_string);
db_error(0);
}
if ((tbp = db_find_breakpoint_number(db_tok_number, &bkpt)) == 0) {
db_printf("No such break point #%d\n", db_tok_number);
db_error(0);
}
db_delete_breakpoint(bkpt->task, bkpt->address, tbp->tb_task_thd);
return;
}
db_unread_token(t);
if (!db_expression(&addr)) {
/*
* We attempt to pick up the user_space indication from db_dot,
* so that a plain "d" always works.
*/
addr = (db_expr_t)db_dot;
if (!user_space && !DB_VALID_ADDRESS(addr, FALSE))
user_space = TRUE;
}
if (!DB_VALID_ADDRESS(addr, user_space)) {
db_printf("Address %#X is not in %s space\n", addr,
(user_space)? "user": "kernel");
db_error(0);
}
if (thd_bpt || task_bpt) {
for (n = 0; db_get_next_act(&thr_act, n); n++) {
if (thr_act == THR_ACT_NULL)
db_error("No active thr_act\n");
if (task_bpt) {
if (thr_act->task == TASK_NULL)
db_error("No task\n");
task_thd = (vm_offset_t) (thr_act->task);
} else
task_thd = (user_global)? 0: (vm_offset_t) thr_act;
db_delete_breakpoint(db_target_space(thr_act, user_space),
(db_addr_t)addr, task_thd);
}
} else {
db_delete_breakpoint(db_target_space(THR_ACT_NULL, user_space),
(db_addr_t)addr, 0);
}
}
/* Set breakpoint with skip count */
#include <mach/machine/vm_param.h>
void
db_breakpoint_cmd(
db_expr_t addr,
int have_addr,
db_expr_t count,
char * modif)
{
register int n;
thread_act_t thr_act;
boolean_t user_global = db_option(modif, 'U');
boolean_t task_bpt = db_option(modif, 'T');
boolean_t user_space;
if (count == -1)
count = 1;
#if 0 /* CHECKME */
if (!task_bpt && db_option(modif,'t'))
task_bpt = TRUE;
#endif
if (task_bpt && user_global)
db_error("Cannot specify both 'T' and 'U'\n");
user_space = (user_global)? TRUE: db_option(modif, 'u');
if (user_space && db_access_level < DB_ACCESS_CURRENT)
db_error("User space break point is not supported\n");
if ((!task_bpt || !user_space) &&
!DB_VALID_ADDRESS(addr, user_space)) {
/* if the user has explicitly specified user space,
do not insert a breakpoint into the kernel */
if (user_space)
db_error("Invalid user space address\n");
user_space = TRUE;
db_printf("%#X is in user space\n", addr);
db_printf("kernel is from %#X to %#x\n", VM_MIN_KERNEL_ADDRESS, VM_MAX_KERNEL_ADDRESS);
}
if (db_option(modif, 't') || task_bpt) {
for (n = 0; db_get_next_act(&thr_act, n); n++) {
if (thr_act == THR_ACT_NULL)
db_error("No active thr_act\n");
if (task_bpt && thr_act->task == TASK_NULL)
db_error("No task\n");
if (db_access_level <= DB_ACCESS_CURRENT && user_space
&& thr_act->task != db_current_space())
db_error("Cannot set break point in inactive user space\n");
db_set_breakpoint(db_target_space(thr_act, user_space),
(db_addr_t)addr, count,
(user_global)? THR_ACT_NULL: thr_act,
task_bpt);
}
} else {
db_set_breakpoint(db_target_space(THR_ACT_NULL, user_space),
(db_addr_t)addr,
count, THR_ACT_NULL, FALSE);
}
}
/* list breakpoints */
void
db_listbreak_cmd(void)
{
db_list_breakpoints();
}