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