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