db_watch.c revision 164359
159874Speter/*- 228861Skato * Mach Operating System 328861Skato * Copyright (c) 1991,1990 Carnegie Mellon University 428861Skato * All Rights Reserved. 528861Skato * 628861Skato * Permission to use, copy, modify and distribute this software and its 728861Skato * documentation is hereby granted, provided that both the copyright 828861Skato * notice and this permission notice appear in all copies of the 928861Skato * software, derivative works or modified versions, and any portions 1028861Skato * thereof, and that both notices appear in supporting documentation. 1128861Skato * 1228861Skato * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS 1328861Skato * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR 1428861Skato * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. 1528861Skato * 1628861Skato * Carnegie Mellon requests users of this software to return to 1728861Skato * 1828861Skato * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU 1928861Skato * School of Computer Science 2028861Skato * Carnegie Mellon University 2128861Skato * Pittsburgh PA 15213-3890 2228861Skato * 2328861Skato * any improvements or extensions that they make and grant Carnegie the 2428861Skato * rights to redistribute these changes. 2528861Skato */ 2628861Skato/* 2728861Skato * Author: Richard P. Draves, Carnegie Mellon University 2828861Skato * Date: 10/90 2928861Skato */ 3028861Skato 3128861Skato#include <sys/cdefs.h> 3228861Skato__FBSDID("$FreeBSD: head/sys/ddb/db_watch.c 164359 2006-11-17 16:41:56Z jhb $"); 3328861Skato 3428861Skato#include <sys/param.h> 3528861Skato#include <sys/kernel.h> 3628861Skato#include <sys/lock.h> 3728861Skato#include <sys/proc.h> 3828861Skato 3928861Skato#include <vm/vm.h> 4028861Skato#include <vm/pmap.h> 4128861Skato#include <vm/vm_map.h> 4228861Skato 4328861Skato#include <ddb/ddb.h> 4428861Skato#include <ddb/db_watch.h> 4528861Skato 4628861Skato/* 4728861Skato * Watchpoints. 4828861Skato */ 4928861Skato 5028861Skatostatic boolean_t db_watchpoints_inserted = TRUE; 5128861Skato 5228861Skato#define NWATCHPOINTS 100 5392761Salfredstatic struct db_watchpoint db_watch_table[NWATCHPOINTS]; 5428861Skatostatic db_watchpoint_t db_next_free_watchpoint = &db_watch_table[0]; 5533044Sbdestatic db_watchpoint_t db_free_watchpoints = 0; 5628861Skatostatic db_watchpoint_t db_watchpoint_list = 0; 5728861Skato 5828861Skatostatic db_watchpoint_t db_watchpoint_alloc(void); 5928861Skatostatic void db_watchpoint_free(db_watchpoint_t watch); 6028861Skatostatic void db_delete_watchpoint(vm_map_t map, db_addr_t addr); 6133044Sbde#ifdef notused 6228861Skatostatic boolean_t db_find_watchpoint(vm_map_t map, db_addr_t addr, 6328861Skato db_regs_t *regs); 6428861Skato#endif 6528861Skatostatic void db_list_watchpoints(void); 66static void db_set_watchpoint(vm_map_t map, db_addr_t addr, 67 vm_size_t size); 68 69static db_watchpoint_t 70db_watchpoint_alloc() 71{ 72 register db_watchpoint_t watch; 73 74 if ((watch = db_free_watchpoints) != 0) { 75 db_free_watchpoints = watch->link; 76 return (watch); 77 } 78 if (db_next_free_watchpoint == &db_watch_table[NWATCHPOINTS]) { 79 db_printf("All watchpoints used.\n"); 80 return (0); 81 } 82 watch = db_next_free_watchpoint; 83 db_next_free_watchpoint++; 84 85 return (watch); 86} 87 88static void 89db_watchpoint_free(watch) 90 register db_watchpoint_t watch; 91{ 92 watch->link = db_free_watchpoints; 93 db_free_watchpoints = watch; 94} 95 96static void 97db_set_watchpoint(map, addr, size) 98 vm_map_t map; 99 db_addr_t addr; 100 vm_size_t size; 101{ 102 register db_watchpoint_t watch; 103 104 if (map == NULL) { 105 db_printf("No map.\n"); 106 return; 107 } 108 109 /* 110 * Should we do anything fancy with overlapping regions? 111 */ 112 113 for (watch = db_watchpoint_list; 114 watch != 0; 115 watch = watch->link) 116 if (db_map_equal(watch->map, map) && 117 (watch->loaddr == addr) && 118 (watch->hiaddr == addr+size)) { 119 db_printf("Already set.\n"); 120 return; 121 } 122 123 watch = db_watchpoint_alloc(); 124 if (watch == 0) { 125 db_printf("Too many watchpoints.\n"); 126 return; 127 } 128 129 watch->map = map; 130 watch->loaddr = addr; 131 watch->hiaddr = addr+size; 132 133 watch->link = db_watchpoint_list; 134 db_watchpoint_list = watch; 135 136 db_watchpoints_inserted = FALSE; 137} 138 139static void 140db_delete_watchpoint(map, addr) 141 vm_map_t map; 142 db_addr_t addr; 143{ 144 register db_watchpoint_t watch; 145 register db_watchpoint_t *prev; 146 147 for (prev = &db_watchpoint_list; 148 (watch = *prev) != 0; 149 prev = &watch->link) 150 if (db_map_equal(watch->map, map) && 151 (watch->loaddr <= addr) && 152 (addr < watch->hiaddr)) { 153 *prev = watch->link; 154 db_watchpoint_free(watch); 155 return; 156 } 157 158 db_printf("Not set.\n"); 159} 160 161static void 162db_list_watchpoints() 163{ 164 register db_watchpoint_t watch; 165 166 if (db_watchpoint_list == 0) { 167 db_printf("No watchpoints set\n"); 168 return; 169 } 170 171#ifdef __LP64__ 172 db_printf(" Map Address Size\n"); 173#else 174 db_printf(" Map Address Size\n"); 175#endif 176 for (watch = db_watchpoint_list; 177 watch != 0; 178 watch = watch->link) 179#ifdef __LP64__ 180 db_printf("%s%16p %16lx %lx\n", 181#else 182 db_printf("%s%8p %8lx %lx\n", 183#endif 184 db_map_current(watch->map) ? "*" : " ", 185 (void *)watch->map, (long)watch->loaddr, 186 (long)watch->hiaddr - (long)watch->loaddr); 187} 188 189/* Delete watchpoint */ 190/*ARGSUSED*/ 191void 192db_deletewatch_cmd(addr, have_addr, count, modif) 193 db_expr_t addr; 194 boolean_t have_addr; 195 db_expr_t count; 196 char * modif; 197{ 198 db_delete_watchpoint(db_map_addr(addr), addr); 199} 200 201/* Set watchpoint */ 202/*ARGSUSED*/ 203void 204db_watchpoint_cmd(addr, have_addr, count, modif) 205 db_expr_t addr; 206 boolean_t have_addr; 207 db_expr_t count; 208 char * modif; 209{ 210 vm_size_t size; 211 db_expr_t value; 212 213 if (db_expression(&value)) 214 size = (vm_size_t) value; 215 else 216 size = 4; 217 db_skip_to_eol(); 218 219 db_set_watchpoint(db_map_addr(addr), addr, size); 220} 221 222/* 223 * At least one non-optional show-command must be implemented using 224 * DB_SHOW_COMMAND() so that db_show_cmd_set gets created. Here is one. 225 */ 226DB_SHOW_COMMAND(watches, db_listwatch_cmd) 227{ 228 db_list_watchpoints(); 229 db_md_list_watchpoints(); 230} 231 232void 233db_set_watchpoints() 234{ 235 register db_watchpoint_t watch; 236 237 if (!db_watchpoints_inserted) { 238 for (watch = db_watchpoint_list; 239 watch != 0; 240 watch = watch->link) 241 pmap_protect(watch->map->pmap, 242 trunc_page(watch->loaddr), 243 round_page(watch->hiaddr), 244 VM_PROT_READ); 245 246 db_watchpoints_inserted = TRUE; 247 } 248} 249 250void 251db_clear_watchpoints() 252{ 253 db_watchpoints_inserted = FALSE; 254} 255 256#ifdef notused 257static boolean_t 258db_find_watchpoint(map, addr, regs) 259 vm_map_t map; 260 db_addr_t addr; 261 db_regs_t *regs; 262{ 263 register db_watchpoint_t watch; 264 db_watchpoint_t found = 0; 265 266 for (watch = db_watchpoint_list; 267 watch != 0; 268 watch = watch->link) 269 if (db_map_equal(watch->map, map)) { 270 if ((watch->loaddr <= addr) && 271 (addr < watch->hiaddr)) 272 return (TRUE); 273 else if ((trunc_page(watch->loaddr) <= addr) && 274 (addr < round_page(watch->hiaddr))) 275 found = watch; 276 } 277 278 /* 279 * We didn't hit exactly on a watchpoint, but we are 280 * in a protected region. We want to single-step 281 * and then re-protect. 282 */ 283 284 if (found) { 285 db_watchpoints_inserted = FALSE; 286 db_single_step(regs); 287 } 288 289 return (FALSE); 290} 291#endif 292 293 294 295/* Delete hardware watchpoint */ 296/*ARGSUSED*/ 297void 298db_deletehwatch_cmd(addr, have_addr, count, modif) 299 db_expr_t addr; 300 boolean_t have_addr; 301 db_expr_t count; 302 char * modif; 303{ 304 int rc; 305 306 if (count < 0) 307 count = 4; 308 309 rc = db_md_clr_watchpoint(addr, count); 310 if (rc < 0) 311 db_printf("hardware watchpoint could not be deleted\n"); 312} 313 314/* Set hardware watchpoint */ 315/*ARGSUSED*/ 316void 317db_hwatchpoint_cmd(addr, have_addr, count, modif) 318 db_expr_t addr; 319 boolean_t have_addr; 320 db_expr_t count; 321 char * modif; 322{ 323 int rc; 324 325 if (count < 0) 326 count = 4; 327 328 rc = db_md_set_watchpoint(addr, count); 329 if (rc < 0) 330 db_printf("hardware watchpoint could not be set\n"); 331} 332