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 */ 27 /* 28 * Author: David B. Golub, Carnegie Mellon University 29 * Date: 7/90 30 */ 31 /* 32 * Breakpoints. 33 */ 34 35 #include <sys/cdefs.h> 36 __FBSDID("$FreeBSD$"); 37 38 #include <sys/param.h> 39 40 #include <vm/vm.h> 41 #include <vm/vm_kern.h> 42 43 #include <ddb/ddb.h> 44 #include <ddb/db_break.h> 45 #include <ddb/db_access.h> 46 #include <ddb/db_sym.h> 47 48 #define NBREAKPOINTS 100 49 static struct db_breakpoint db_break_table[NBREAKPOINTS]; 50 static db_breakpoint_t db_next_free_breakpoint = &db_break_table[0]; 51 static db_breakpoint_t db_free_breakpoints = 0; 52 static db_breakpoint_t db_breakpoint_list = 0; 53 54 static db_breakpoint_t db_breakpoint_alloc(void); 55 static void db_breakpoint_free(db_breakpoint_t bkpt); 56 static void db_delete_breakpoint(vm_map_t map, db_addr_t addr); 57 static db_breakpoint_t db_find_breakpoint(vm_map_t map, db_addr_t addr); 58 static void db_list_breakpoints(void); 59 static void db_set_breakpoint(vm_map_t map, db_addr_t addr, int count); 60 61 static db_breakpoint_t 62 db_breakpoint_alloc() 63 { 64 register 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 register 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 register 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 register db_breakpoint_t bkpt; 123 register 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 register 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 #ifndef BKPT_WRITE 170 #define BKPT_WRITE(addr, storage) \ 171 do { \ 172 *storage = db_get_value(addr, BKPT_SIZE, FALSE); \ 173 db_put_value(addr, BKPT_SIZE, BKPT_SET(*storage)); \ 174 } while (0) 175 #endif 176 177 #ifndef BKPT_CLEAR 178 #define BKPT_CLEAR(addr, storage) \ 179 db_put_value(addr, BKPT_SIZE, *storage) 180 #endif 181 182 void 183 db_set_breakpoints() 184 { 185 register db_breakpoint_t bkpt; 186 187 if (!db_breakpoints_inserted) { 188 189 for (bkpt = db_breakpoint_list; 190 bkpt != 0; 191 bkpt = bkpt->link) 192 if (db_map_current(bkpt->map)) { 193 BKPT_WRITE(bkpt->address, &bkpt->bkpt_inst); 194 } 195 db_breakpoints_inserted = TRUE; 196 } 197 } 198 199 void 200 db_clear_breakpoints() 201 { 202 register db_breakpoint_t bkpt; 203 204 if (db_breakpoints_inserted) { 205 206 for (bkpt = db_breakpoint_list; 207 bkpt != 0; 208 bkpt = bkpt->link) 209 if (db_map_current(bkpt->map)) { 210 BKPT_CLEAR(bkpt->address, &bkpt->bkpt_inst); 211 } 212 db_breakpoints_inserted = FALSE; 213 } 214 } 215 216 #ifdef SOFTWARE_SSTEP 217 /* 218 * Set a temporary breakpoint. 219 * The instruction is changed immediately, 220 * so the breakpoint does not have to be on the breakpoint list. 221 */ 222 db_breakpoint_t 223 db_set_temp_breakpoint(addr) 224 db_addr_t addr; 225 { 226 register db_breakpoint_t bkpt; 227 228 bkpt = db_breakpoint_alloc(); 229 if (bkpt == 0) { 230 db_printf("Too many breakpoints.\n"); 231 return 0; 232 } 233 234 bkpt->map = NULL; 235 bkpt->address = addr; 236 bkpt->flags = BKPT_TEMP; 237 bkpt->init_count = 1; 238 bkpt->count = 1; 239 240 BKPT_WRITE(bkpt->address, &bkpt->bkpt_inst); 241 return bkpt; 242 } 243 244 void 245 db_delete_temp_breakpoint(bkpt) 246 db_breakpoint_t bkpt; 247 { 248 BKPT_CLEAR(bkpt->address, &bkpt->bkpt_inst); 249 db_breakpoint_free(bkpt); 250 } 251 #endif /* SOFTWARE_SSTEP */ 252 253 /* 254 * List breakpoints. 255 */ 256 static void 257 db_list_breakpoints() 258 { 259 register db_breakpoint_t bkpt; 260 261 if (db_breakpoint_list == 0) { 262 db_printf("No breakpoints set\n"); 263 return; 264 } 265 266 db_printf(" Map Count Address\n"); 267 for (bkpt = db_breakpoint_list; 268 bkpt != 0; 269 bkpt = bkpt->link) { 270 db_printf("%s%8p %5d ", 271 db_map_current(bkpt->map) ? "*" : " ", 272 (void *)bkpt->map, bkpt->init_count); 273 db_printsym(bkpt->address, DB_STGY_PROC); 274 db_printf("\n"); 275 } 276 } 277 278 /* Delete breakpoint */ 279 /*ARGSUSED*/ 280 void 281 db_delete_cmd(addr, have_addr, count, modif) 282 db_expr_t addr; 283 boolean_t have_addr; 284 db_expr_t count; 285 char * modif; 286 { 287 db_delete_breakpoint(db_map_addr(addr), (db_addr_t)addr); 288 } 289 290 /* Set breakpoint with skip count */ 291 /*ARGSUSED*/ 292 void 293 db_breakpoint_cmd(addr, have_addr, count, modif) 294 db_expr_t addr; 295 boolean_t have_addr; 296 db_expr_t count; 297 char * modif; 298 { 299 if (count == -1) 300 count = 1; 301 302 db_set_breakpoint(db_map_addr(addr), (db_addr_t)addr, count); 303 } 304 305 /* list breakpoints */ 306 void 307 db_listbreak_cmd(dummy1, dummy2, dummy3, dummy4) 308 db_expr_t dummy1; 309 boolean_t dummy2; 310 db_expr_t dummy3; 311 char * dummy4; 312 { 313 db_list_breakpoints(); 314 } 315 316 /* 317 * We want ddb to be usable before most of the kernel has been 318 * initialized. In particular, current_thread() or kernel_map 319 * (or both) may be null. 320 */ 321 322 boolean_t 323 db_map_equal(map1, map2) 324 vm_map_t map1, map2; 325 { 326 return ((map1 == map2) || 327 ((map1 == NULL) && (map2 == kernel_map)) || 328 ((map1 == kernel_map) && (map2 == NULL))); 329 } 330 331 boolean_t 332 db_map_current(map) 333 vm_map_t map; 334 { 335 #if 0 336 thread_t thread; 337 338 return ((map == NULL) || 339 (map == kernel_map) || 340 (((thread = current_thread()) != NULL) && 341 (map == thread->task->map))); 342 #else 343 return (1); 344 #endif 345 } 346 347 vm_map_t 348 db_map_addr(addr) 349 vm_offset_t addr; 350 { 351 #if 0 352 thread_t thread; 353 354 /* 355 * We want to return kernel_map for all 356 * non-user addresses, even when debugging 357 * kernel tasks with their own maps. 358 */ 359 360 if ((VM_MIN_ADDRESS <= addr) && 361 (addr < VM_MAX_ADDRESS) && 362 ((thread = current_thread()) != NULL)) 363 return thread->task->map; 364 else 365 #endif 366 return kernel_map; 367 } 368