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