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