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/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, int count); 59 60 static db_breakpoint_t 61 db_breakpoint_alloc(void) 62 { 63 register db_breakpoint_t bkpt; 64 65 if ((bkpt = db_free_breakpoints) != 0) { 66 db_free_breakpoints = bkpt->link; 67 return (bkpt); 68 } 69 if (db_next_free_breakpoint == &db_break_table[NBREAKPOINTS]) { 70 db_printf("All breakpoints used.\n"); 71 return (0); 72 } 73 bkpt = db_next_free_breakpoint; 74 db_next_free_breakpoint++; 75 76 return (bkpt); 77 } 78 79 static void 80 db_breakpoint_free(db_breakpoint_t bkpt) 81 { 82 bkpt->link = db_free_breakpoints; 83 db_free_breakpoints = bkpt; 84 } 85 86 static void 87 db_set_breakpoint(vm_map_t map, db_addr_t addr, 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 static void 113 db_delete_breakpoint(vm_map_t map, db_addr_t addr) 114 { 115 register db_breakpoint_t bkpt; 116 register db_breakpoint_t *prev; 117 118 for (prev = &db_breakpoint_list; 119 (bkpt = *prev) != 0; 120 prev = &bkpt->link) { 121 if (db_map_equal(bkpt->map, map) && 122 (bkpt->address == addr)) { 123 *prev = bkpt->link; 124 break; 125 } 126 } 127 if (bkpt == 0) { 128 db_printf("Not set.\n"); 129 return; 130 } 131 132 db_breakpoint_free(bkpt); 133 } 134 135 static db_breakpoint_t 136 db_find_breakpoint(vm_map_t map, db_addr_t addr) 137 { 138 register db_breakpoint_t bkpt; 139 140 for (bkpt = db_breakpoint_list; 141 bkpt != 0; 142 bkpt = bkpt->link) 143 { 144 if (db_map_equal(bkpt->map, map) && 145 (bkpt->address == addr)) 146 return (bkpt); 147 } 148 return (0); 149 } 150 151 db_breakpoint_t 152 db_find_breakpoint_here(db_addr_t addr) 153 { 154 return db_find_breakpoint(db_map_addr(addr), addr); 155 } 156 157 static bool db_breakpoints_inserted = true; 158 159 #ifndef BKPT_WRITE 160 #define BKPT_WRITE(addr, storage) \ 161 do { \ 162 *storage = db_get_value(addr, BKPT_SIZE, false); \ 163 db_put_value(addr, BKPT_SIZE, BKPT_SET(*storage)); \ 164 } while (0) 165 #endif 166 167 #ifndef BKPT_CLEAR 168 #define BKPT_CLEAR(addr, storage) \ 169 db_put_value(addr, BKPT_SIZE, *storage) 170 #endif 171 172 void 173 db_set_breakpoints(void) 174 { 175 register db_breakpoint_t bkpt; 176 177 if (!db_breakpoints_inserted) { 178 for (bkpt = db_breakpoint_list; 179 bkpt != 0; 180 bkpt = bkpt->link) 181 if (db_map_current(bkpt->map)) { 182 BKPT_WRITE(bkpt->address, &bkpt->bkpt_inst); 183 } 184 db_breakpoints_inserted = true; 185 } 186 } 187 188 void 189 db_clear_breakpoints(void) 190 { 191 register db_breakpoint_t bkpt; 192 193 if (db_breakpoints_inserted) { 194 for (bkpt = db_breakpoint_list; 195 bkpt != 0; 196 bkpt = bkpt->link) 197 if (db_map_current(bkpt->map)) { 198 BKPT_CLEAR(bkpt->address, &bkpt->bkpt_inst); 199 } 200 db_breakpoints_inserted = false; 201 } 202 } 203 204 /* 205 * List breakpoints. 206 */ 207 static void 208 db_list_breakpoints(void) 209 { 210 register db_breakpoint_t bkpt; 211 212 if (db_breakpoint_list == 0) { 213 db_printf("No breakpoints set\n"); 214 return; 215 } 216 217 db_printf(" Map Count Address\n"); 218 for (bkpt = db_breakpoint_list; 219 bkpt != 0; 220 bkpt = bkpt->link) { 221 db_printf("%s%8p %5d ", 222 db_map_current(bkpt->map) ? "*" : " ", 223 (void *)bkpt->map, bkpt->init_count); 224 db_printsym(bkpt->address, DB_STGY_PROC); 225 db_printf("\n"); 226 } 227 } 228 229 /* Delete breakpoint */ 230 /*ARGSUSED*/ 231 void 232 db_delete_cmd(db_expr_t addr, bool have_addr, db_expr_t count, char *modif) 233 { 234 db_delete_breakpoint(db_map_addr(addr), (db_addr_t)addr); 235 } 236 237 /* Set breakpoint with skip count */ 238 /*ARGSUSED*/ 239 void 240 db_breakpoint_cmd(db_expr_t addr, bool have_addr, db_expr_t count, char *modif) 241 { 242 if (count == -1) 243 count = 1; 244 245 db_set_breakpoint(db_map_addr(addr), (db_addr_t)addr, count); 246 } 247 248 /* list breakpoints */ 249 void 250 db_listbreak_cmd(db_expr_t dummy1, bool dummy2, db_expr_t dummy3, char *dummy4) 251 { 252 db_list_breakpoints(); 253 } 254 255 /* 256 * We want ddb to be usable before most of the kernel has been 257 * initialized. In particular, current_thread() or kernel_map 258 * (or both) may be null. 259 */ 260 261 bool 262 db_map_equal(vm_map_t map1, vm_map_t map2) 263 { 264 return ((map1 == map2) || 265 ((map1 == NULL) && (map2 == kernel_map)) || 266 ((map1 == kernel_map) && (map2 == NULL))); 267 } 268 269 bool 270 db_map_current(vm_map_t map) 271 { 272 #if 0 273 thread_t thread; 274 275 return ((map == NULL) || 276 (map == kernel_map) || 277 (((thread = current_thread()) != NULL) && 278 (map == thread->task->map))); 279 #else 280 return (true); 281 #endif 282 } 283 284 vm_map_t 285 db_map_addr(vm_offset_t addr) 286 { 287 #if 0 288 thread_t thread; 289 290 /* 291 * We want to return kernel_map for all 292 * non-user addresses, even when debugging 293 * kernel tasks with their own maps. 294 */ 295 296 if ((VM_MIN_ADDRESS <= addr) && 297 (addr < VM_MAX_ADDRESS) && 298 ((thread = current_thread()) != NULL)) 299 return thread->task->map; 300 else 301 #endif 302 return kernel_map; 303 } 304