Home | History | Annotate | Line # | Download | only in ddb
db_watch.c revision 1.18
      1 /*	$NetBSD: db_watch.c,v 1.18 2001/11/12 22:54:07 lukem Exp $	*/
      2 
      3 /*
      4  * Mach Operating System
      5  * Copyright (c) 1991,1990 Carnegie Mellon University
      6  * All Rights Reserved.
      7  *
      8  * Permission to use, copy, modify and distribute this software and its
      9  * documentation is hereby granted, provided that both the copyright
     10  * notice and this permission notice appear in all copies of the
     11  * software, derivative works or modified versions, and any portions
     12  * thereof, and that both notices appear in supporting documentation.
     13  *
     14  * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
     15  * CONDITION.  CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
     16  * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
     17  *
     18  * Carnegie Mellon requests users of this software to return to
     19  *
     20  *  Software Distribution Coordinator  or  Software.Distribution (at) CS.CMU.EDU
     21  *  School of Computer Science
     22  *  Carnegie Mellon University
     23  *  Pittsburgh PA 15213-3890
     24  *
     25  * any improvements or extensions that they make and grant Carnegie the
     26  * rights to redistribute these changes.
     27  *
     28  * 	Author: Richard P. Draves, Carnegie Mellon University
     29  *	Date:	10/90
     30  */
     31 
     32 #include <sys/cdefs.h>
     33 __KERNEL_RCSID(0, "$NetBSD: db_watch.c,v 1.18 2001/11/12 22:54:07 lukem Exp $");
     34 
     35 #include <sys/param.h>
     36 #include <sys/proc.h>
     37 
     38 #include <machine/db_machdep.h>
     39 
     40 #include <ddb/db_break.h>
     41 #include <ddb/db_watch.h>
     42 #include <ddb/db_lex.h>
     43 #include <ddb/db_access.h>
     44 #include <ddb/db_run.h>
     45 #include <ddb/db_sym.h>
     46 #include <ddb/db_output.h>
     47 #include <ddb/db_command.h>
     48 #include <ddb/db_extern.h>
     49 
     50 /*
     51  * Watchpoints.
     52  */
     53 
     54 boolean_t	db_watchpoints_inserted = TRUE;
     55 
     56 #define	NWATCHPOINTS	100
     57 struct db_watchpoint	db_watch_table[NWATCHPOINTS];
     58 db_watchpoint_t		db_next_free_watchpoint = &db_watch_table[0];
     59 db_watchpoint_t		db_free_watchpoints = 0;
     60 db_watchpoint_t		db_watchpoint_list = 0;
     61 
     62 db_watchpoint_t
     63 db_watchpoint_alloc()
     64 {
     65 	db_watchpoint_t	watch;
     66 
     67 	if ((watch = db_free_watchpoints) != 0) {
     68 	    db_free_watchpoints = watch->link;
     69 	    return (watch);
     70 	}
     71 	if (db_next_free_watchpoint == &db_watch_table[NWATCHPOINTS]) {
     72 	    db_printf("All watchpoints used.\n");
     73 	    return (0);
     74 	}
     75 	watch = db_next_free_watchpoint;
     76 	db_next_free_watchpoint++;
     77 
     78 	return (watch);
     79 }
     80 
     81 void
     82 db_watchpoint_free(watch)
     83 	db_watchpoint_t	watch;
     84 {
     85 	watch->link = db_free_watchpoints;
     86 	db_free_watchpoints = watch;
     87 }
     88 
     89 void
     90 db_set_watchpoint(map, addr, size)
     91 	struct vm_map	*map;
     92 	db_addr_t	addr;
     93 	vsize_t	size;
     94 {
     95 	db_watchpoint_t	watch;
     96 
     97 	if (map == NULL) {
     98 	    db_printf("No map.\n");
     99 	    return;
    100 	}
    101 
    102 	/*
    103 	 *	Should we do anything fancy with overlapping regions?
    104 	 */
    105 
    106 	for (watch = db_watchpoint_list;
    107 	     watch != 0;
    108 	     watch = watch->link)
    109 	    if (db_map_equal(watch->map, map) &&
    110 		(watch->loaddr == addr) &&
    111 		(watch->hiaddr == addr+size)) {
    112 		db_printf("Already set.\n");
    113 		return;
    114 	    }
    115 
    116 	watch = db_watchpoint_alloc();
    117 	if (watch == 0) {
    118 	    db_printf("Too many watchpoints.\n");
    119 	    return;
    120 	}
    121 
    122 	watch->map = map;
    123 	watch->loaddr = addr;
    124 	watch->hiaddr = addr+size;
    125 
    126 	watch->link = db_watchpoint_list;
    127 	db_watchpoint_list = watch;
    128 
    129 	db_watchpoints_inserted = FALSE;
    130 }
    131 
    132 void
    133 db_delete_watchpoint(map, addr)
    134 	struct vm_map	*map;
    135 	db_addr_t	addr;
    136 {
    137 	db_watchpoint_t	watch;
    138 	db_watchpoint_t	*prev;
    139 
    140 	for (prev = &db_watchpoint_list;
    141 	     (watch = *prev) != 0;
    142 	     prev = &watch->link)
    143 	    if (db_map_equal(watch->map, map) &&
    144 		(watch->loaddr <= addr) &&
    145 		(addr < watch->hiaddr)) {
    146 		*prev = watch->link;
    147 		db_watchpoint_free(watch);
    148 		return;
    149 	    }
    150 
    151 	db_printf("Not set.\n");
    152 }
    153 
    154 void
    155 db_list_watchpoints()
    156 {
    157 	db_watchpoint_t	watch;
    158 
    159 	if (db_watchpoint_list == 0) {
    160 	    db_printf("No watchpoints set\n");
    161 	    return;
    162 	}
    163 
    164 	db_printf(" Map        Address  Size\n");
    165 	for (watch = db_watchpoint_list;
    166 	     watch != 0;
    167 	     watch = watch->link)
    168 	    db_printf("%s%p  %8lx  %lx\n",
    169 		      db_map_current(watch->map) ? "*" : " ",
    170 		      watch->map, watch->loaddr,
    171 		      watch->hiaddr - watch->loaddr);
    172 }
    173 
    174 /* Delete watchpoint */
    175 /*ARGSUSED*/
    176 void
    177 db_deletewatch_cmd(addr, have_addr, count, modif)
    178 	db_expr_t	addr;
    179 	int		have_addr;
    180 	db_expr_t	count;
    181 	char *		modif;
    182 {
    183 	db_delete_watchpoint(db_map_addr(addr), addr);
    184 }
    185 
    186 /* Set watchpoint */
    187 /*ARGSUSED*/
    188 void
    189 db_watchpoint_cmd(addr, have_addr, count, modif)
    190 	db_expr_t	addr;
    191 	int		have_addr;
    192 	db_expr_t	count;
    193 	char *		modif;
    194 {
    195 	vsize_t	size;
    196 	db_expr_t	value;
    197 
    198 	if (db_expression(&value))
    199 	    size = (vsize_t) value;
    200 	else
    201 	    size = 4;
    202 	db_skip_to_eol();
    203 
    204 	db_set_watchpoint(db_map_addr(addr), addr, size);
    205 }
    206 
    207 /* list watchpoints */
    208 /*ARGSUSED*/
    209 void
    210 db_listwatch_cmd(addr, have_addr, count, modif)
    211 	db_expr_t	addr;
    212 	int		have_addr;
    213 	db_expr_t	count;
    214 	char *		modif;
    215 {
    216 	db_list_watchpoints();
    217 }
    218 
    219 void
    220 db_set_watchpoints()
    221 {
    222 	db_watchpoint_t	watch;
    223 
    224 	if (!db_watchpoints_inserted) {
    225 	    for (watch = db_watchpoint_list;
    226 	         watch != 0;
    227 	         watch = watch->link) {
    228 		pmap_protect(watch->map->pmap,
    229 			     trunc_page(watch->loaddr),
    230 			     round_page(watch->hiaddr),
    231 			     VM_PROT_READ);
    232 		pmap_update(watch->map->pmap);
    233 	    }
    234 
    235 	    db_watchpoints_inserted = TRUE;
    236 	}
    237 }
    238 
    239 void
    240 db_clear_watchpoints()
    241 {
    242 	db_watchpoints_inserted = FALSE;
    243 }
    244 
    245 boolean_t
    246 db_find_watchpoint(map, addr, regs)
    247 	struct vm_map	*map;
    248 	db_addr_t	addr;
    249 	db_regs_t	*regs;
    250 {
    251 	db_watchpoint_t watch;
    252 	db_watchpoint_t found = 0;
    253 
    254 	for (watch = db_watchpoint_list;
    255 	     watch != 0;
    256 	     watch = watch->link)
    257 	    if (db_map_equal(watch->map, map)) {
    258 		if ((watch->loaddr <= addr) &&
    259 		    (addr < watch->hiaddr))
    260 		    return (TRUE);
    261 		else if ((trunc_page(watch->loaddr) <= addr) &&
    262 			 (addr < round_page(watch->hiaddr)))
    263 		    found = watch;
    264 	    }
    265 
    266 	/*
    267 	 *	We didn't hit exactly on a watchpoint, but we are
    268 	 *	in a protected region.  We want to single-step
    269 	 *	and then re-protect.
    270 	 */
    271 
    272 	if (found) {
    273 	    db_watchpoints_inserted = FALSE;
    274 	    db_single_step(regs);
    275 	}
    276 
    277 	return (FALSE);
    278 }
    279