1 /* $NetBSD: db_break.c,v 1.7 1996/03/30 22:30:03 christos Exp $ */ 2 3 /* 4 * Mach Operating System 5 * Copyright (c) 1991,1990 Carnegie Mellon University 6 * All Rights Reserved. 7 * 8 * Permission to use, copy, modify and distribute this software and its 9 * documentation is hereby granted, provided that both the copyright 10 * notice and this permission notice appear in all copies of the 11 * software, derivative works or modified versions, and any portions 12 * thereof, and that both notices appear in supporting documentation. 13 * 14 * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS 15 * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR 16 * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. 17 * 18 * Carnegie Mellon requests users of this software to return to 19 * 20 * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU 21 * School of Computer Science 22 * Carnegie Mellon University 23 * Pittsburgh PA 15213-3890 24 * 25 * any improvements or extensions that they make and grant Carnegie the 26 * rights to redistribute these changes. 27 * 28 * Author: David B. Golub, Carnegie Mellon University 29 * Date: 7/90 30 */ 31 32 /* 33 * Breakpoints. 34 */ 35 #include <sys/param.h> 36 #include <sys/proc.h> 37 38 #include <machine/db_machdep.h> /* type definitions */ 39 40 #include <ddb/db_lex.h> 41 #include <ddb/db_access.h> 42 #include <ddb/db_sym.h> 43 #include <ddb/db_break.h> 44 #include <ddb/db_output.h> 45 46 #define NBREAKPOINTS 100 47 struct db_breakpoint db_break_table[NBREAKPOINTS]; 48 db_breakpoint_t db_next_free_breakpoint = &db_break_table[0]; 49 db_breakpoint_t db_free_breakpoints = 0; 50 db_breakpoint_t db_breakpoint_list = 0; 51 52 db_breakpoint_t 53 db_breakpoint_alloc() 54 { 55 register db_breakpoint_t bkpt; 56 57 if ((bkpt = db_free_breakpoints) != 0) { 58 db_free_breakpoints = bkpt->link; 59 return (bkpt); 60 } 61 if (db_next_free_breakpoint == &db_break_table[NBREAKPOINTS]) { 62 db_printf("All breakpoints used.\n"); 63 return (0); 64 } 65 bkpt = db_next_free_breakpoint; 66 db_next_free_breakpoint++; 67 68 return (bkpt); 69 } 70 71 void 72 db_breakpoint_free(bkpt) 73 register db_breakpoint_t bkpt; 74 { 75 bkpt->link = db_free_breakpoints; 76 db_free_breakpoints = bkpt; 77 } 78 79 void 80 db_set_breakpoint(map, addr, count) 81 vm_map_t map; 82 db_addr_t addr; 83 int count; 84 { 85 register db_breakpoint_t bkpt; 86 87 if (db_find_breakpoint(map, addr)) { 88 db_printf("Already set.\n"); 89 return; 90 } 91 92 bkpt = db_breakpoint_alloc(); 93 if (bkpt == 0) { 94 db_printf("Too many breakpoints.\n"); 95 return; 96 } 97 98 bkpt->map = map; 99 bkpt->address = addr; 100 bkpt->flags = 0; 101 bkpt->init_count = count; 102 bkpt->count = count; 103 104 bkpt->link = db_breakpoint_list; 105 db_breakpoint_list = bkpt; 106 } 107 108 void 109 db_delete_breakpoint(map, addr) 110 vm_map_t map; 111 db_addr_t addr; 112 { 113 register db_breakpoint_t bkpt; 114 register db_breakpoint_t *prev; 115 116 for (prev = &db_breakpoint_list; 117 (bkpt = *prev) != 0; 118 prev = &bkpt->link) { 119 if (db_map_equal(bkpt->map, map) && 120 (bkpt->address == addr)) { 121 *prev = bkpt->link; 122 break; 123 } 124 } 125 if (bkpt == 0) { 126 db_printf("Not set.\n"); 127 return; 128 } 129 130 db_breakpoint_free(bkpt); 131 } 132 133 db_breakpoint_t 134 db_find_breakpoint(map, addr) 135 vm_map_t map; 136 db_addr_t addr; 137 { 138 register db_breakpoint_t bkpt; 139 140 for (bkpt = db_breakpoint_list; 141 bkpt != 0; 142 bkpt = bkpt->link) 143 { 144 if (db_map_equal(bkpt->map, map) && 145 (bkpt->address == addr)) 146 return (bkpt); 147 } 148 return (0); 149 } 150 151 db_breakpoint_t 152 db_find_breakpoint_here(addr) 153 db_addr_t addr; 154 { 155 return db_find_breakpoint(db_map_addr(addr), addr); 156 } 157 158 boolean_t db_breakpoints_inserted = TRUE; 159 160 void 161 db_set_breakpoints() 162 { 163 register db_breakpoint_t bkpt; 164 165 if (!db_breakpoints_inserted) { 166 167 for (bkpt = db_breakpoint_list; 168 bkpt != 0; 169 bkpt = bkpt->link) 170 if (db_map_current(bkpt->map)) { 171 bkpt->bkpt_inst = db_get_value(bkpt->address, 172 BKPT_SIZE, 173 FALSE); 174 db_put_value(bkpt->address, 175 BKPT_SIZE, 176 BKPT_SET(bkpt->bkpt_inst)); 177 } 178 db_breakpoints_inserted = TRUE; 179 } 180 } 181 182 void 183 db_clear_breakpoints() 184 { 185 register db_breakpoint_t bkpt; 186 187 if (db_breakpoints_inserted) { 188 189 for (bkpt = db_breakpoint_list; 190 bkpt != 0; 191 bkpt = bkpt->link) 192 if (db_map_current(bkpt->map)) { 193 db_put_value(bkpt->address, BKPT_SIZE, bkpt->bkpt_inst); 194 } 195 db_breakpoints_inserted = FALSE; 196 } 197 } 198 199 /* 200 * Set a temporary breakpoint. 201 * The instruction is changed immediately, 202 * so the breakpoint does not have to be on the breakpoint list. 203 */ 204 db_breakpoint_t 205 db_set_temp_breakpoint(addr) 206 db_addr_t addr; 207 { 208 register db_breakpoint_t bkpt; 209 210 bkpt = db_breakpoint_alloc(); 211 if (bkpt == 0) { 212 db_printf("Too many breakpoints.\n"); 213 return 0; 214 } 215 216 bkpt->map = NULL; 217 bkpt->address = addr; 218 bkpt->flags = BKPT_TEMP; 219 bkpt->init_count = 1; 220 bkpt->count = 1; 221 222 bkpt->bkpt_inst = db_get_value(bkpt->address, BKPT_SIZE, FALSE); 223 db_put_value(bkpt->address, BKPT_SIZE, BKPT_SET(bkpt->bkpt_inst)); 224 return bkpt; 225 } 226 227 void 228 db_delete_temp_breakpoint(bkpt) 229 db_breakpoint_t bkpt; 230 { 231 db_put_value(bkpt->address, BKPT_SIZE, bkpt->bkpt_inst); 232 db_breakpoint_free(bkpt); 233 } 234 235 /* 236 * List breakpoints. 237 */ 238 void 239 db_list_breakpoints() 240 { 241 register db_breakpoint_t bkpt; 242 243 if (db_breakpoint_list == 0) { 244 db_printf("No breakpoints set\n"); 245 return; 246 } 247 248 db_printf(" Map Count Address\n"); 249 for (bkpt = db_breakpoint_list; 250 bkpt != 0; 251 bkpt = bkpt->link) 252 { 253 db_printf("%s%p %5d ", 254 db_map_current(bkpt->map) ? "*" : " ", 255 bkpt->map, bkpt->init_count); 256 db_printsym(bkpt->address, DB_STGY_PROC); 257 db_printf("\n"); 258 } 259 } 260 261 /* Delete breakpoint */ 262 /*ARGSUSED*/ 263 void 264 db_delete_cmd(addr, have_addr, count, modif) 265 db_expr_t addr; 266 int have_addr; 267 db_expr_t count; 268 char * modif; 269 { 270 db_delete_breakpoint(db_map_addr(addr), (db_addr_t)addr); 271 } 272 273 /* Set breakpoint with skip count */ 274 /*ARGSUSED*/ 275 void 276 db_breakpoint_cmd(addr, have_addr, count, modif) 277 db_expr_t addr; 278 int have_addr; 279 db_expr_t count; 280 char * modif; 281 { 282 if (count == -1) 283 count = 1; 284 285 db_set_breakpoint(db_map_addr(addr), (db_addr_t)addr, count); 286 } 287 288 /* list breakpoints */ 289 /*ARGSUSED*/ 290 void 291 db_listbreak_cmd(addr, have_addr, count, modif) 292 db_expr_t addr; 293 int have_addr; 294 db_expr_t count; 295 char * modif; 296 { 297 db_list_breakpoints(); 298 } 299 300 #include <vm/vm_kern.h> 301 302 /* 303 * We want ddb to be usable before most of the kernel has been 304 * initialized. In particular, current_thread() or kernel_map 305 * (or both) may be null. 306 */ 307 308 boolean_t 309 db_map_equal(map1, map2) 310 vm_map_t map1, map2; 311 { 312 return ((map1 == map2) || 313 ((map1 == NULL) && (map2 == kernel_map)) || 314 ((map1 == kernel_map) && (map2 == NULL))); 315 } 316 317 boolean_t 318 db_map_current(map) 319 vm_map_t map; 320 { 321 #if 0 322 thread_t thread; 323 324 return ((map == NULL) || 325 (map == kernel_map) || 326 (((thread = current_thread()) != NULL) && 327 (map == thread->task->map))); 328 #else 329 return (1); 330 #endif 331 } 332 333 vm_map_t 334 db_map_addr(addr) 335 vm_offset_t addr; 336 { 337 #if 0 338 thread_t thread; 339 340 /* 341 * We want to return kernel_map for all 342 * non-user addresses, even when debugging 343 * kernel tasks with their own maps. 344 */ 345 346 if ((VM_MIN_ADDRESS <= addr) && 347 (addr < VM_MAX_ADDRESS) && 348 ((thread = current_thread()) != NULL)) 349 return thread->task->map; 350 else 351 #endif 352 return kernel_map; 353 } 354