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 * $FreeBSD: src/sys/ddb/db_break.c,v 1.18 1999/08/28 00:41:05 peter Exp $ 27 * $DragonFly: src/sys/ddb/db_break.c,v 1.7 2006/12/28 21:24:01 dillon Exp $ 28 */ 29 30 /* 31 * Author: David B. Golub, Carnegie Mellon University 32 * Date: 7/90 33 */ 34 /* 35 * Breakpoints. 36 */ 37 #include <sys/param.h> 38 39 #include <vm/vm.h> 40 #include <vm/vm_kern.h> 41 42 #include <ddb/ddb.h> 43 #include <ddb/db_break.h> 44 #include <ddb/db_access.h> 45 #include <ddb/db_sym.h> 46 47 #define NBREAKPOINTS 100 48 static struct db_breakpoint db_break_table[NBREAKPOINTS]; 49 static db_breakpoint_t db_next_free_breakpoint = &db_break_table[0]; 50 static db_breakpoint_t db_free_breakpoints = 0; 51 static db_breakpoint_t db_breakpoint_list = 0; 52 53 static db_breakpoint_t db_breakpoint_alloc (void); 54 static void db_breakpoint_free (db_breakpoint_t bkpt); 55 static void db_delete_breakpoint (vm_map_t map, db_addr_t addr); 56 static db_breakpoint_t db_find_breakpoint (vm_map_t map, db_addr_t addr); 57 static void db_list_breakpoints (void); 58 static void db_set_breakpoint (vm_map_t map, db_addr_t addr, 59 int count); 60 61 static db_breakpoint_t 62 db_breakpoint_alloc(void) 63 { 64 db_breakpoint_t bkpt; 65 66 if ((bkpt = db_free_breakpoints) != 0) { 67 db_free_breakpoints = bkpt->link; 68 return (bkpt); 69 } 70 if (db_next_free_breakpoint == &db_break_table[NBREAKPOINTS]) { 71 db_printf("All breakpoints used.\n"); 72 return (0); 73 } 74 bkpt = db_next_free_breakpoint; 75 db_next_free_breakpoint++; 76 77 return (bkpt); 78 } 79 80 static void 81 db_breakpoint_free(db_breakpoint_t bkpt) 82 { 83 bkpt->link = db_free_breakpoints; 84 db_free_breakpoints = bkpt; 85 } 86 87 static void 88 db_set_breakpoint(vm_map_t map, db_addr_t addr, int count) 89 { 90 db_breakpoint_t bkpt; 91 92 if (db_find_breakpoint(map, addr)) { 93 db_printf("Already set.\n"); 94 return; 95 } 96 97 bkpt = db_breakpoint_alloc(); 98 if (bkpt == 0) { 99 db_printf("Too many breakpoints.\n"); 100 return; 101 } 102 103 bkpt->map = map; 104 bkpt->address = addr; 105 bkpt->flags = 0; 106 bkpt->init_count = count; 107 bkpt->count = count; 108 109 bkpt->link = db_breakpoint_list; 110 db_breakpoint_list = bkpt; 111 } 112 113 static void 114 db_delete_breakpoint(vm_map_t map, db_addr_t addr) 115 { 116 db_breakpoint_t bkpt; 117 db_breakpoint_t *prev; 118 119 for (prev = &db_breakpoint_list; 120 (bkpt = *prev) != 0; 121 prev = &bkpt->link) { 122 if (db_map_equal(bkpt->map, map) && 123 (bkpt->address == addr)) { 124 *prev = bkpt->link; 125 break; 126 } 127 } 128 if (bkpt == 0) { 129 db_printf("Not set.\n"); 130 return; 131 } 132 133 db_breakpoint_free(bkpt); 134 } 135 136 static db_breakpoint_t 137 db_find_breakpoint(vm_map_t map, db_addr_t addr) 138 { 139 db_breakpoint_t bkpt; 140 141 for (bkpt = db_breakpoint_list; 142 bkpt != 0; 143 bkpt = bkpt->link) 144 { 145 if (db_map_equal(bkpt->map, map) && 146 (bkpt->address == addr)) 147 return (bkpt); 148 } 149 return (0); 150 } 151 152 db_breakpoint_t 153 db_find_breakpoint_here(db_addr_t addr) 154 { 155 return db_find_breakpoint(db_map_addr(addr), addr); 156 } 157 158 static boolean_t db_breakpoints_inserted = TRUE; 159 160 void 161 db_set_breakpoints(void) 162 { 163 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(void) 184 { 185 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 #ifdef SOFTWARE_SSTEP 200 /* 201 * Set a temporary breakpoint. 202 * The instruction is changed immediately, 203 * so the breakpoint does not have to be on the breakpoint list. 204 */ 205 db_breakpoint_t 206 db_set_temp_breakpoint(db_addr_t addr) 207 { 208 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(db_breakpoint_t bkpt) 229 { 230 db_put_value(bkpt->address, BKPT_SIZE, bkpt->bkpt_inst); 231 db_breakpoint_free(bkpt); 232 } 233 #endif /* SOFTWARE_SSTEP */ 234 235 /* 236 * List breakpoints. 237 */ 238 static void 239 db_list_breakpoints(void) 240 { 241 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 db_printf("%s%8p %5d ", 253 db_map_current(bkpt->map) ? "*" : " ", 254 (void *)bkpt->map, bkpt->init_count); 255 db_printsym(bkpt->address, DB_STGY_PROC); 256 db_printf("\n"); 257 } 258 } 259 260 /* Delete breakpoint */ 261 /*ARGSUSED*/ 262 void 263 db_delete_cmd(db_expr_t addr, boolean_t have_addr, db_expr_t count, char *modif) 264 { 265 db_delete_breakpoint(db_map_addr(addr), (db_addr_t)addr); 266 } 267 268 /* Set breakpoint with skip count */ 269 /*ARGSUSED*/ 270 void 271 db_breakpoint_cmd(db_expr_t addr, boolean_t have_addr, db_expr_t count, 272 char *modif) 273 { 274 if (count == -1) 275 count = 1; 276 277 db_set_breakpoint(db_map_addr(addr), (db_addr_t)addr, count); 278 } 279 280 /* list breakpoints */ 281 void 282 db_listbreak_cmd(db_expr_t dummy1, boolean_t dummy2, db_expr_t dummy3, 283 char *dummy4) 284 { 285 db_list_breakpoints(); 286 } 287 288 /* 289 * We want ddb to be usable before most of the kernel has been 290 * initialized. In particular, current_thread() or kernel_map 291 * (or both) may be null. 292 */ 293 294 boolean_t 295 db_map_equal(vm_map_t map1, vm_map_t map2) 296 { 297 return ((map1 == map2) || 298 ((map1 == NULL) && (map2 == &kernel_map)) || 299 ((map1 == &kernel_map) && (map2 == NULL))); 300 } 301 302 boolean_t 303 db_map_current(vm_map_t map) 304 { 305 #if 0 306 thread_t thread; 307 308 return ((map == NULL) || 309 (map == &kernel_map) || 310 (((thread = current_thread()) != NULL) && 311 (map == thread->task->map))); 312 #else 313 return (1); 314 #endif 315 } 316 317 vm_map_t 318 db_map_addr(vm_offset_t addr) 319 { 320 return &kernel_map; 321 } 322