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