1 /* 2 * Mach Operating System 3 * Copyright (c) 1991,1990 Carnegie Mellon University 4 * All Rights Reserved. 5 * 6 * Permission to use, copy, modify and distribute this software and its 7 * documentation is hereby granted, provided that both the copyright 8 * notice and this permission notice appear in all copies of the 9 * software, derivative works or modified versions, and any portions 10 * thereof, and that both notices appear in supporting documentation. 11 * 12 * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS 13 * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR 14 * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. 15 * 16 * Carnegie Mellon requests users of this software to return to 17 * 18 * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU 19 * School of Computer Science 20 * Carnegie Mellon University 21 * Pittsburgh PA 15213-3890 22 * 23 * any improvements or extensions that they make and grant Carnegie the 24 * rights to redistribute these changes. 25 * 26 * $FreeBSD: src/sys/ddb/db_break.c,v 1.18 1999/08/28 00:41:05 peter Exp $ 27 * $DragonFly: src/sys/ddb/db_break.c,v 1.4 2003/08/27 10:47:13 rob Exp $ 28 */ 29 30 /* 31 * Author: David B. Golub, Carnegie Mellon University 32 * Date: 7/90 33 */ 34 /* 35 * Breakpoints. 36 */ 37 #include <sys/param.h> 38 39 #include <vm/vm.h> 40 #include <vm/vm_kern.h> 41 42 #include <ddb/ddb.h> 43 #include <ddb/db_break.h> 44 #include <ddb/db_access.h> 45 #include <ddb/db_sym.h> 46 47 #define NBREAKPOINTS 100 48 static struct db_breakpoint db_break_table[NBREAKPOINTS]; 49 static db_breakpoint_t db_next_free_breakpoint = &db_break_table[0]; 50 static db_breakpoint_t db_free_breakpoints = 0; 51 static db_breakpoint_t db_breakpoint_list = 0; 52 53 static db_breakpoint_t db_breakpoint_alloc (void); 54 static void db_breakpoint_free (db_breakpoint_t bkpt); 55 static void db_delete_breakpoint (vm_map_t map, db_addr_t addr); 56 static db_breakpoint_t db_find_breakpoint (vm_map_t map, db_addr_t addr); 57 static void db_list_breakpoints (void); 58 static void db_set_breakpoint (vm_map_t map, db_addr_t addr, 59 int count); 60 61 static db_breakpoint_t 62 db_breakpoint_alloc() 63 { 64 db_breakpoint_t bkpt; 65 66 if ((bkpt = db_free_breakpoints) != 0) { 67 db_free_breakpoints = bkpt->link; 68 return (bkpt); 69 } 70 if (db_next_free_breakpoint == &db_break_table[NBREAKPOINTS]) { 71 db_printf("All breakpoints used.\n"); 72 return (0); 73 } 74 bkpt = db_next_free_breakpoint; 75 db_next_free_breakpoint++; 76 77 return (bkpt); 78 } 79 80 static void 81 db_breakpoint_free(bkpt) 82 db_breakpoint_t bkpt; 83 { 84 bkpt->link = db_free_breakpoints; 85 db_free_breakpoints = bkpt; 86 } 87 88 static void 89 db_set_breakpoint(map, addr, count) 90 vm_map_t map; 91 db_addr_t addr; 92 int count; 93 { 94 db_breakpoint_t bkpt; 95 96 if (db_find_breakpoint(map, addr)) { 97 db_printf("Already set.\n"); 98 return; 99 } 100 101 bkpt = db_breakpoint_alloc(); 102 if (bkpt == 0) { 103 db_printf("Too many breakpoints.\n"); 104 return; 105 } 106 107 bkpt->map = map; 108 bkpt->address = addr; 109 bkpt->flags = 0; 110 bkpt->init_count = count; 111 bkpt->count = count; 112 113 bkpt->link = db_breakpoint_list; 114 db_breakpoint_list = bkpt; 115 } 116 117 static void 118 db_delete_breakpoint(map, addr) 119 vm_map_t map; 120 db_addr_t addr; 121 { 122 db_breakpoint_t bkpt; 123 db_breakpoint_t *prev; 124 125 for (prev = &db_breakpoint_list; 126 (bkpt = *prev) != 0; 127 prev = &bkpt->link) { 128 if (db_map_equal(bkpt->map, map) && 129 (bkpt->address == addr)) { 130 *prev = bkpt->link; 131 break; 132 } 133 } 134 if (bkpt == 0) { 135 db_printf("Not set.\n"); 136 return; 137 } 138 139 db_breakpoint_free(bkpt); 140 } 141 142 static db_breakpoint_t 143 db_find_breakpoint(map, addr) 144 vm_map_t map; 145 db_addr_t addr; 146 { 147 db_breakpoint_t bkpt; 148 149 for (bkpt = db_breakpoint_list; 150 bkpt != 0; 151 bkpt = bkpt->link) 152 { 153 if (db_map_equal(bkpt->map, map) && 154 (bkpt->address == addr)) 155 return (bkpt); 156 } 157 return (0); 158 } 159 160 db_breakpoint_t 161 db_find_breakpoint_here(addr) 162 db_addr_t addr; 163 { 164 return db_find_breakpoint(db_map_addr(addr), addr); 165 } 166 167 static boolean_t db_breakpoints_inserted = TRUE; 168 169 void 170 db_set_breakpoints() 171 { 172 db_breakpoint_t bkpt; 173 174 if (!db_breakpoints_inserted) { 175 176 for (bkpt = db_breakpoint_list; 177 bkpt != 0; 178 bkpt = bkpt->link) 179 if (db_map_current(bkpt->map)) { 180 bkpt->bkpt_inst = db_get_value(bkpt->address, 181 BKPT_SIZE, 182 FALSE); 183 db_put_value(bkpt->address, 184 BKPT_SIZE, 185 BKPT_SET(bkpt->bkpt_inst)); 186 } 187 db_breakpoints_inserted = TRUE; 188 } 189 } 190 191 void 192 db_clear_breakpoints() 193 { 194 db_breakpoint_t bkpt; 195 196 if (db_breakpoints_inserted) { 197 198 for (bkpt = db_breakpoint_list; 199 bkpt != 0; 200 bkpt = bkpt->link) 201 if (db_map_current(bkpt->map)) { 202 db_put_value(bkpt->address, BKPT_SIZE, bkpt->bkpt_inst); 203 } 204 db_breakpoints_inserted = FALSE; 205 } 206 } 207 208 #ifdef SOFTWARE_SSTEP 209 /* 210 * Set a temporary breakpoint. 211 * The instruction is changed immediately, 212 * so the breakpoint does not have to be on the breakpoint list. 213 */ 214 db_breakpoint_t 215 db_set_temp_breakpoint(addr) 216 db_addr_t addr; 217 { 218 db_breakpoint_t bkpt; 219 220 bkpt = db_breakpoint_alloc(); 221 if (bkpt == 0) { 222 db_printf("Too many breakpoints.\n"); 223 return 0; 224 } 225 226 bkpt->map = NULL; 227 bkpt->address = addr; 228 bkpt->flags = BKPT_TEMP; 229 bkpt->init_count = 1; 230 bkpt->count = 1; 231 232 bkpt->bkpt_inst = db_get_value(bkpt->address, BKPT_SIZE, FALSE); 233 db_put_value(bkpt->address, BKPT_SIZE, BKPT_SET(bkpt->bkpt_inst)); 234 return bkpt; 235 } 236 237 void 238 db_delete_temp_breakpoint(bkpt) 239 db_breakpoint_t bkpt; 240 { 241 db_put_value(bkpt->address, BKPT_SIZE, bkpt->bkpt_inst); 242 db_breakpoint_free(bkpt); 243 } 244 #endif /* SOFTWARE_SSTEP */ 245 246 /* 247 * List breakpoints. 248 */ 249 static void 250 db_list_breakpoints() 251 { 252 db_breakpoint_t bkpt; 253 254 if (db_breakpoint_list == 0) { 255 db_printf("No breakpoints set\n"); 256 return; 257 } 258 259 db_printf(" Map Count Address\n"); 260 for (bkpt = db_breakpoint_list; 261 bkpt != 0; 262 bkpt = bkpt->link) { 263 db_printf("%s%8p %5d ", 264 db_map_current(bkpt->map) ? "*" : " ", 265 (void *)bkpt->map, bkpt->init_count); 266 db_printsym(bkpt->address, DB_STGY_PROC); 267 db_printf("\n"); 268 } 269 } 270 271 /* Delete breakpoint */ 272 /*ARGSUSED*/ 273 void 274 db_delete_cmd(addr, have_addr, count, modif) 275 db_expr_t addr; 276 boolean_t have_addr; 277 db_expr_t count; 278 char * modif; 279 { 280 db_delete_breakpoint(db_map_addr(addr), (db_addr_t)addr); 281 } 282 283 /* Set breakpoint with skip count */ 284 /*ARGSUSED*/ 285 void 286 db_breakpoint_cmd(addr, have_addr, count, modif) 287 db_expr_t addr; 288 boolean_t have_addr; 289 db_expr_t count; 290 char * modif; 291 { 292 if (count == -1) 293 count = 1; 294 295 db_set_breakpoint(db_map_addr(addr), (db_addr_t)addr, count); 296 } 297 298 /* list breakpoints */ 299 void 300 db_listbreak_cmd(dummy1, dummy2, dummy3, dummy4) 301 db_expr_t dummy1; 302 boolean_t dummy2; 303 db_expr_t dummy3; 304 char * dummy4; 305 { 306 db_list_breakpoints(); 307 } 308 309 /* 310 * We want ddb to be usable before most of the kernel has been 311 * initialized. In particular, current_thread() or kernel_map 312 * (or both) may be null. 313 */ 314 315 boolean_t 316 db_map_equal(map1, map2) 317 vm_map_t map1, map2; 318 { 319 return ((map1 == map2) || 320 ((map1 == NULL) && (map2 == kernel_map)) || 321 ((map1 == kernel_map) && (map2 == NULL))); 322 } 323 324 boolean_t 325 db_map_current(map) 326 vm_map_t map; 327 { 328 #if 0 329 thread_t thread; 330 331 return ((map == NULL) || 332 (map == kernel_map) || 333 (((thread = current_thread()) != NULL) && 334 (map == thread->task->map))); 335 #else 336 return (1); 337 #endif 338 } 339 340 vm_map_t 341 db_map_addr(addr) 342 vm_offset_t addr; 343 { 344 #if 0 345 thread_t thread; 346 347 /* 348 * We want to return kernel_map for all 349 * non-user addresses, even when debugging 350 * kernel tasks with their own maps. 351 */ 352 353 if ((VM_MIN_ADDRESS <= addr) && 354 (addr < VM_MAX_ADDRESS) && 355 ((thread = current_thread()) != NULL)) 356 return thread->task->map; 357 else 358 #endif 359 return kernel_map; 360 } 361