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