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