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.10 1995/12/10 13:32:33 phk 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 #include <sys/systm.h> 38 39 #include <vm/vm.h> 40 #include <vm/vm_kern.h> 41 42 #include <ddb/ddb.h> 43 #include <ddb/db_lex.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 __P((void)); 55 static void db_breakpoint_free __P((db_breakpoint_t bkpt)); 56 static void db_delete_breakpoint __P((vm_map_t map, db_addr_t addr)); 57 static db_breakpoint_t db_find_breakpoint __P((vm_map_t map, db_addr_t addr)); 58 static void db_list_breakpoints __P((void)); 59 static void db_set_breakpoint __P((vm_map_t map, db_addr_t addr, 60 int count)); 61 #ifdef notused 62 static db_breakpoint_t db_set_temp_breakpoint __P((db_addr_t addr)); 63 static void db_delete_temp_breakpoint __P((db_breakpoint_t bkpt)); 64 #endif 65 66 static db_breakpoint_t 67 db_breakpoint_alloc() 68 { 69 register db_breakpoint_t bkpt; 70 71 if ((bkpt = db_free_breakpoints) != 0) { 72 db_free_breakpoints = bkpt->link; 73 return (bkpt); 74 } 75 if (db_next_free_breakpoint == &db_break_table[NBREAKPOINTS]) { 76 db_printf("All breakpoints used.\n"); 77 return (0); 78 } 79 bkpt = db_next_free_breakpoint; 80 db_next_free_breakpoint++; 81 82 return (bkpt); 83 } 84 85 static void 86 db_breakpoint_free(bkpt) 87 register db_breakpoint_t bkpt; 88 { 89 bkpt->link = db_free_breakpoints; 90 db_free_breakpoints = bkpt; 91 } 92 93 static void 94 db_set_breakpoint(map, addr, count) 95 vm_map_t map; 96 db_addr_t addr; 97 int count; 98 { 99 register db_breakpoint_t bkpt; 100 101 if (db_find_breakpoint(map, addr)) { 102 db_printf("Already set.\n"); 103 return; 104 } 105 106 bkpt = db_breakpoint_alloc(); 107 if (bkpt == 0) { 108 db_printf("Too many breakpoints.\n"); 109 return; 110 } 111 112 bkpt->map = map; 113 bkpt->address = addr; 114 bkpt->flags = 0; 115 bkpt->init_count = count; 116 bkpt->count = count; 117 118 bkpt->link = db_breakpoint_list; 119 db_breakpoint_list = bkpt; 120 } 121 122 static void 123 db_delete_breakpoint(map, addr) 124 vm_map_t map; 125 db_addr_t addr; 126 { 127 register db_breakpoint_t bkpt; 128 register db_breakpoint_t *prev; 129 130 for (prev = &db_breakpoint_list; 131 (bkpt = *prev) != 0; 132 prev = &bkpt->link) { 133 if (db_map_equal(bkpt->map, map) && 134 (bkpt->address == addr)) { 135 *prev = bkpt->link; 136 break; 137 } 138 } 139 if (bkpt == 0) { 140 db_printf("Not set.\n"); 141 return; 142 } 143 144 db_breakpoint_free(bkpt); 145 } 146 147 static db_breakpoint_t 148 db_find_breakpoint(map, addr) 149 vm_map_t map; 150 db_addr_t addr; 151 { 152 register db_breakpoint_t bkpt; 153 154 for (bkpt = db_breakpoint_list; 155 bkpt != 0; 156 bkpt = bkpt->link) 157 { 158 if (db_map_equal(bkpt->map, map) && 159 (bkpt->address == addr)) 160 return (bkpt); 161 } 162 return (0); 163 } 164 165 db_breakpoint_t 166 db_find_breakpoint_here(addr) 167 db_addr_t addr; 168 { 169 return db_find_breakpoint(db_map_addr(addr), addr); 170 } 171 172 static boolean_t db_breakpoints_inserted = TRUE; 173 174 void 175 db_set_breakpoints() 176 { 177 register db_breakpoint_t bkpt; 178 179 if (!db_breakpoints_inserted) { 180 181 for (bkpt = db_breakpoint_list; 182 bkpt != 0; 183 bkpt = bkpt->link) 184 if (db_map_current(bkpt->map)) { 185 bkpt->bkpt_inst = db_get_value(bkpt->address, 186 BKPT_SIZE, 187 FALSE); 188 db_put_value(bkpt->address, 189 BKPT_SIZE, 190 BKPT_SET(bkpt->bkpt_inst)); 191 } 192 db_breakpoints_inserted = TRUE; 193 } 194 } 195 196 void 197 db_clear_breakpoints() 198 { 199 register db_breakpoint_t bkpt; 200 201 if (db_breakpoints_inserted) { 202 203 for (bkpt = db_breakpoint_list; 204 bkpt != 0; 205 bkpt = bkpt->link) 206 if (db_map_current(bkpt->map)) { 207 db_put_value(bkpt->address, BKPT_SIZE, bkpt->bkpt_inst); 208 } 209 db_breakpoints_inserted = FALSE; 210 } 211 } 212 213 #ifdef notused 214 /* 215 * Set a temporary breakpoint. 216 * The instruction is changed immediately, 217 * so the breakpoint does not have to be on the breakpoint list. 218 */ 219 static db_breakpoint_t 220 db_set_temp_breakpoint(addr) 221 db_addr_t addr; 222 { 223 register db_breakpoint_t bkpt; 224 225 bkpt = db_breakpoint_alloc(); 226 if (bkpt == 0) { 227 db_printf("Too many breakpoints.\n"); 228 return 0; 229 } 230 231 bkpt->map = NULL; 232 bkpt->address = addr; 233 bkpt->flags = BKPT_TEMP; 234 bkpt->init_count = 1; 235 bkpt->count = 1; 236 237 bkpt->bkpt_inst = db_get_value(bkpt->address, BKPT_SIZE, FALSE); 238 db_put_value(bkpt->address, BKPT_SIZE, BKPT_SET(bkpt->bkpt_inst)); 239 return bkpt; 240 } 241 242 static void 243 db_delete_temp_breakpoint(bkpt) 244 db_breakpoint_t bkpt; 245 { 246 db_put_value(bkpt->address, BKPT_SIZE, bkpt->bkpt_inst); 247 db_breakpoint_free(bkpt); 248 } 249 #endif 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 { 269 db_printf("%s%8x %5d ", 270 db_map_current(bkpt->map) ? "*" : " ", 271 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