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$ 27 */ 28 29 /* 30 * Author: David B. Golub, Carnegie Mellon University 31 * Date: 7/90 32 */ 33 /* 34 * Breakpoints. 35 */ 36 #include <sys/param.h> 37 38 #include <vm/vm.h> 39 #include <vm/vm_kern.h> 40 41 #include <ddb/ddb.h> 42 #include <ddb/db_break.h> 43 #include <ddb/db_access.h> 44 #include <ddb/db_sym.h> 45 46 #define NBREAKPOINTS 100 47 static struct db_breakpoint db_break_table[NBREAKPOINTS]; 48 static db_breakpoint_t db_next_free_breakpoint = &db_break_table[0]; 49 static db_breakpoint_t db_free_breakpoints = 0; 50 static db_breakpoint_t db_breakpoint_list = 0; 51 52 static db_breakpoint_t db_breakpoint_alloc(void); 53 static void db_breakpoint_free(db_breakpoint_t bkpt); 54 static void db_delete_breakpoint(vm_map_t map, db_addr_t addr); 55 static db_breakpoint_t db_find_breakpoint(vm_map_t map, db_addr_t addr); 56 static void db_list_breakpoints(void); 57 static void db_set_breakpoint(vm_map_t map, db_addr_t addr, int count); 58 59 static db_breakpoint_t 60 db_breakpoint_alloc() 61 { 62 register db_breakpoint_t bkpt; 63 64 if ((bkpt = db_free_breakpoints) != 0) { 65 db_free_breakpoints = bkpt->link; 66 return (bkpt); 67 } 68 if (db_next_free_breakpoint == &db_break_table[NBREAKPOINTS]) { 69 db_printf("All breakpoints used.\n"); 70 return (0); 71 } 72 bkpt = db_next_free_breakpoint; 73 db_next_free_breakpoint++; 74 75 return (bkpt); 76 } 77 78 static void 79 db_breakpoint_free(bkpt) 80 register db_breakpoint_t bkpt; 81 { 82 bkpt->link = db_free_breakpoints; 83 db_free_breakpoints = bkpt; 84 } 85 86 static void 87 db_set_breakpoint(map, addr, count) 88 vm_map_t map; 89 db_addr_t addr; 90 int count; 91 { 92 register db_breakpoint_t bkpt; 93 94 if (db_find_breakpoint(map, addr)) { 95 db_printf("Already set.\n"); 96 return; 97 } 98 99 bkpt = db_breakpoint_alloc(); 100 if (bkpt == 0) { 101 db_printf("Too many breakpoints.\n"); 102 return; 103 } 104 105 bkpt->map = map; 106 bkpt->address = addr; 107 bkpt->flags = 0; 108 bkpt->init_count = count; 109 bkpt->count = count; 110 111 bkpt->link = db_breakpoint_list; 112 db_breakpoint_list = bkpt; 113 } 114 115 static void 116 db_delete_breakpoint(map, addr) 117 vm_map_t map; 118 db_addr_t addr; 119 { 120 register db_breakpoint_t bkpt; 121 register db_breakpoint_t *prev; 122 123 for (prev = &db_breakpoint_list; 124 (bkpt = *prev) != 0; 125 prev = &bkpt->link) { 126 if (db_map_equal(bkpt->map, map) && 127 (bkpt->address == addr)) { 128 *prev = bkpt->link; 129 break; 130 } 131 } 132 if (bkpt == 0) { 133 db_printf("Not set.\n"); 134 return; 135 } 136 137 db_breakpoint_free(bkpt); 138 } 139 140 static db_breakpoint_t 141 db_find_breakpoint(map, addr) 142 vm_map_t map; 143 db_addr_t addr; 144 { 145 register db_breakpoint_t bkpt; 146 147 for (bkpt = db_breakpoint_list; 148 bkpt != 0; 149 bkpt = bkpt->link) 150 { 151 if (db_map_equal(bkpt->map, map) && 152 (bkpt->address == addr)) 153 return (bkpt); 154 } 155 return (0); 156 } 157 158 db_breakpoint_t 159 db_find_breakpoint_here(addr) 160 db_addr_t addr; 161 { 162 return db_find_breakpoint(db_map_addr(addr), addr); 163 } 164 165 static boolean_t db_breakpoints_inserted = TRUE; 166 167 #ifndef BKPT_WRITE 168 #define BKPT_WRITE(addr, storage) \ 169 do { \ 170 *storage = db_get_value(addr, BKPT_SIZE, FALSE); \ 171 db_put_value(addr, BKPT_SIZE, BKPT_SET(*storage)); \ 172 } while (0) 173 #endif 174 175 #ifndef BKPT_CLEAR 176 #define BKPT_CLEAR(addr, storage) \ 177 db_put_value(addr, BKPT_SIZE, *storage) 178 #endif 179 180 void 181 db_set_breakpoints() 182 { 183 register db_breakpoint_t bkpt; 184 185 if (!db_breakpoints_inserted) { 186 187 for (bkpt = db_breakpoint_list; 188 bkpt != 0; 189 bkpt = bkpt->link) 190 if (db_map_current(bkpt->map)) { 191 BKPT_WRITE(bkpt->address, &bkpt->bkpt_inst); 192 } 193 db_breakpoints_inserted = TRUE; 194 } 195 } 196 197 void 198 db_clear_breakpoints() 199 { 200 register db_breakpoint_t bkpt; 201 202 if (db_breakpoints_inserted) { 203 204 for (bkpt = db_breakpoint_list; 205 bkpt != 0; 206 bkpt = bkpt->link) 207 if (db_map_current(bkpt->map)) { 208 BKPT_CLEAR(bkpt->address, &bkpt->bkpt_inst); 209 } 210 db_breakpoints_inserted = FALSE; 211 } 212 } 213 214 #ifdef SOFTWARE_SSTEP 215 /* 216 * Set a temporary breakpoint. 217 * The instruction is changed immediately, 218 * so the breakpoint does not have to be on the breakpoint list. 219 */ 220 db_breakpoint_t 221 db_set_temp_breakpoint(addr) 222 db_addr_t addr; 223 { 224 register db_breakpoint_t bkpt; 225 226 bkpt = db_breakpoint_alloc(); 227 if (bkpt == 0) { 228 db_printf("Too many breakpoints.\n"); 229 return 0; 230 } 231 232 bkpt->map = NULL; 233 bkpt->address = addr; 234 bkpt->flags = BKPT_TEMP; 235 bkpt->init_count = 1; 236 bkpt->count = 1; 237 238 BKPT_WRITE(bkpt->address, &bkpt->bkpt_inst); 239 return bkpt; 240 } 241 242 void 243 db_delete_temp_breakpoint(bkpt) 244 db_breakpoint_t bkpt; 245 { 246 BKPT_CLEAR(bkpt->address, &bkpt->bkpt_inst); 247 db_breakpoint_free(bkpt); 248 } 249 #endif /* SOFTWARE_SSTEP */ 250 251 /* 252 * List breakpoints. 253 */ 254 static void 255 db_list_breakpoints() 256 { 257 register db_breakpoint_t bkpt; 258 259 if (db_breakpoint_list == 0) { 260 db_printf("No breakpoints set\n"); 261 return; 262 } 263 264 db_printf(" Map Count Address\n"); 265 for (bkpt = db_breakpoint_list; 266 bkpt != 0; 267 bkpt = bkpt->link) { 268 db_printf("%s%8p %5d ", 269 db_map_current(bkpt->map) ? "*" : " ", 270 (void *)bkpt->map, bkpt->init_count); 271 db_printsym(bkpt->address, DB_STGY_PROC); 272 db_printf("\n"); 273 } 274 } 275 276 /* Delete breakpoint */ 277 /*ARGSUSED*/ 278 void 279 db_delete_cmd(addr, have_addr, count, modif) 280 db_expr_t addr; 281 boolean_t have_addr; 282 db_expr_t count; 283 char * modif; 284 { 285 db_delete_breakpoint(db_map_addr(addr), (db_addr_t)addr); 286 } 287 288 /* Set breakpoint with skip count */ 289 /*ARGSUSED*/ 290 void 291 db_breakpoint_cmd(addr, have_addr, count, modif) 292 db_expr_t addr; 293 boolean_t have_addr; 294 db_expr_t count; 295 char * modif; 296 { 297 if (count == -1) 298 count = 1; 299 300 db_set_breakpoint(db_map_addr(addr), (db_addr_t)addr, count); 301 } 302 303 /* list breakpoints */ 304 void 305 db_listbreak_cmd(dummy1, dummy2, dummy3, dummy4) 306 db_expr_t dummy1; 307 boolean_t dummy2; 308 db_expr_t dummy3; 309 char * dummy4; 310 { 311 db_list_breakpoints(); 312 } 313 314 /* 315 * We want ddb to be usable before most of the kernel has been 316 * initialized. In particular, current_thread() or kernel_map 317 * (or both) may be null. 318 */ 319 320 boolean_t 321 db_map_equal(map1, map2) 322 vm_map_t map1, map2; 323 { 324 return ((map1 == map2) || 325 ((map1 == NULL) && (map2 == kernel_map)) || 326 ((map1 == kernel_map) && (map2 == NULL))); 327 } 328 329 boolean_t 330 db_map_current(map) 331 vm_map_t map; 332 { 333 #if 0 334 thread_t thread; 335 336 return ((map == NULL) || 337 (map == kernel_map) || 338 (((thread = current_thread()) != NULL) && 339 (map == thread->task->map))); 340 #else 341 return (1); 342 #endif 343 } 344 345 vm_map_t 346 db_map_addr(addr) 347 vm_offset_t addr; 348 { 349 #if 0 350 thread_t thread; 351 352 /* 353 * We want to return kernel_map for all 354 * non-user addresses, even when debugging 355 * kernel tasks with their own maps. 356 */ 357 358 if ((VM_MIN_ADDRESS <= addr) && 359 (addr < VM_MAX_ADDRESS) && 360 ((thread = current_thread()) != NULL)) 361 return thread->task->map; 362 else 363 #endif 364 return kernel_map; 365 } 366