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 #include <sys/param.h> 39 40 #include <vm/vm.h> 41 #include <vm/vm_kern.h> 42 43 #include <ddb/ddb.h> 44 #include <ddb/db_break.h> 45 #include <ddb/db_access.h> 46 #include <ddb/db_sym.h> 47 48 #define NBREAKPOINTS 100 49 static struct db_breakpoint db_break_table[NBREAKPOINTS]; 50 static db_breakpoint_t db_next_free_breakpoint = &db_break_table[0]; 51 static db_breakpoint_t db_free_breakpoints = 0; 52 static db_breakpoint_t db_breakpoint_list = 0; 53 54 static db_breakpoint_t db_breakpoint_alloc(void); 55 static void db_breakpoint_free(db_breakpoint_t bkpt); 56 static void db_delete_breakpoint(vm_map_t map, db_addr_t addr); 57 static db_breakpoint_t db_find_breakpoint(vm_map_t map, db_addr_t addr); 58 static void db_list_breakpoints(void); 59 static void db_set_breakpoint(vm_map_t map, db_addr_t addr, int count); 60 61 static db_breakpoint_t 62 db_breakpoint_alloc(void) 63 { 64 register 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 register 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 register db_breakpoint_t bkpt; 117 register 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 register 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 bool db_breakpoints_inserted = true; 159 160 #ifndef BKPT_WRITE 161 #define BKPT_WRITE(addr, storage) \ 162 do { \ 163 *storage = db_get_value(addr, BKPT_SIZE, false); \ 164 db_put_value(addr, BKPT_SIZE, BKPT_SET(*storage)); \ 165 } while (0) 166 #endif 167 168 #ifndef BKPT_CLEAR 169 #define BKPT_CLEAR(addr, storage) \ 170 db_put_value(addr, BKPT_SIZE, *storage) 171 #endif 172 173 void 174 db_set_breakpoints(void) 175 { 176 register db_breakpoint_t bkpt; 177 178 if (!db_breakpoints_inserted) { 179 for (bkpt = db_breakpoint_list; 180 bkpt != 0; 181 bkpt = bkpt->link) 182 if (db_map_current(bkpt->map)) { 183 BKPT_WRITE(bkpt->address, &bkpt->bkpt_inst); 184 } 185 db_breakpoints_inserted = true; 186 } 187 } 188 189 void 190 db_clear_breakpoints(void) 191 { 192 register db_breakpoint_t bkpt; 193 194 if (db_breakpoints_inserted) { 195 for (bkpt = db_breakpoint_list; 196 bkpt != 0; 197 bkpt = bkpt->link) 198 if (db_map_current(bkpt->map)) { 199 BKPT_CLEAR(bkpt->address, &bkpt->bkpt_inst); 200 } 201 db_breakpoints_inserted = false; 202 } 203 } 204 205 /* 206 * List breakpoints. 207 */ 208 static void 209 db_list_breakpoints(void) 210 { 211 register db_breakpoint_t bkpt; 212 213 if (db_breakpoint_list == 0) { 214 db_printf("No breakpoints set\n"); 215 return; 216 } 217 218 db_printf(" Map Count Address\n"); 219 for (bkpt = db_breakpoint_list; 220 bkpt != 0; 221 bkpt = bkpt->link) { 222 db_printf("%s%8p %5d ", 223 db_map_current(bkpt->map) ? "*" : " ", 224 (void *)bkpt->map, bkpt->init_count); 225 db_printsym(bkpt->address, DB_STGY_PROC); 226 db_printf("\n"); 227 } 228 } 229 230 /* Delete breakpoint */ 231 /*ARGSUSED*/ 232 void 233 db_delete_cmd(db_expr_t addr, bool have_addr, db_expr_t count, char *modif) 234 { 235 db_delete_breakpoint(db_map_addr(addr), (db_addr_t)addr); 236 } 237 238 /* Set breakpoint with skip count */ 239 /*ARGSUSED*/ 240 void 241 db_breakpoint_cmd(db_expr_t addr, bool have_addr, db_expr_t count, char *modif) 242 { 243 if (count == -1) 244 count = 1; 245 246 db_set_breakpoint(db_map_addr(addr), (db_addr_t)addr, count); 247 } 248 249 /* list breakpoints */ 250 void 251 db_listbreak_cmd(db_expr_t dummy1, bool dummy2, db_expr_t dummy3, char *dummy4) 252 { 253 db_list_breakpoints(); 254 } 255 256 /* 257 * We want ddb to be usable before most of the kernel has been 258 * initialized. In particular, current_thread() or kernel_map 259 * (or both) may be null. 260 */ 261 262 bool 263 db_map_equal(vm_map_t map1, vm_map_t map2) 264 { 265 return ((map1 == map2) || 266 ((map1 == NULL) && (map2 == kernel_map)) || 267 ((map1 == kernel_map) && (map2 == NULL))); 268 } 269 270 bool 271 db_map_current(vm_map_t map) 272 { 273 #if 0 274 thread_t thread; 275 276 return ((map == NULL) || 277 (map == kernel_map) || 278 (((thread = current_thread()) != NULL) && 279 (map == thread->task->map))); 280 #else 281 return (true); 282 #endif 283 } 284 285 vm_map_t 286 db_map_addr(vm_offset_t addr) 287 { 288 #if 0 289 thread_t thread; 290 291 /* 292 * We want to return kernel_map for all 293 * non-user addresses, even when debugging 294 * kernel tasks with their own maps. 295 */ 296 297 if ((VM_MIN_ADDRESS <= addr) && 298 (addr < VM_MAX_ADDRESS) && 299 ((thread = current_thread()) != NULL)) 300 return thread->task->map; 301 else 302 #endif 303 return kernel_map; 304 } 305