1/* $NetBSD: db_break.c,v 1.25 2007/02/22 04:38:04 matt Exp $ */ 2 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 "AS IS" 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 * Author: David B. Golub, Carnegie Mellon University 29 * Date: 7/90 30 */ 31 32/* 33 * Breakpoints. 34 */ 35 36#include <sys/cdefs.h> 37__KERNEL_RCSID(0, "$NetBSD: db_break.c,v 1.25 2007/02/22 04:38:04 matt Exp $"); 38 39#include <sys/param.h> 40#include <sys/proc.h> 41 42#include <machine/db_machdep.h> /* type definitions */ 43 44#include <ddb/db_lex.h> 45#include <ddb/db_access.h> 46#include <ddb/db_sym.h> 47#include <ddb/db_break.h> 48#include <ddb/db_output.h> 49 50#define NBREAKPOINTS 100 51static struct db_breakpoint db_break_table[NBREAKPOINTS]; 52static db_breakpoint_t db_next_free_breakpoint = &db_break_table[0]; 53static db_breakpoint_t db_free_breakpoints = 0; 54static db_breakpoint_t db_breakpoint_list = 0; 55 56static db_breakpoint_t db_breakpoint_alloc(void); 57static void db_breakpoint_free(db_breakpoint_t); 58static void db_delete_breakpoint(struct vm_map *, db_addr_t); 59static db_breakpoint_t db_find_breakpoint(struct vm_map *, db_addr_t); 60static void db_list_breakpoints(void); 61static void db_set_breakpoint(struct vm_map *, db_addr_t, int); 62 63static db_breakpoint_t 64db_breakpoint_alloc(void) 65{ 66 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 82static void 83db_breakpoint_free(db_breakpoint_t bkpt) 84{ 85 bkpt->link = db_free_breakpoints; 86 db_free_breakpoints = bkpt; 87} 88 89void 90db_set_breakpoint(struct vm_map *map, db_addr_t addr, int count) 91{ 92 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 = BKPT_ADDR(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 115static void 116db_delete_breakpoint(struct vm_map *map, db_addr_t addr) 117{ 118 db_breakpoint_t bkpt; 119 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 == BKPT_ADDR(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 138db_breakpoint_t 139db_find_breakpoint(struct vm_map *map, db_addr_t addr) 140{ 141 db_breakpoint_t bkpt; 142 143 for (bkpt = db_breakpoint_list; 144 bkpt != 0; 145 bkpt = bkpt->link) 146 if (db_map_equal(bkpt->map, map) && 147 (bkpt->address == BKPT_ADDR(addr))) 148 return (bkpt); 149 150 return (0); 151} 152 153db_breakpoint_t 154db_find_breakpoint_here(db_addr_t addr) 155{ 156 return db_find_breakpoint(db_map_addr(addr), addr); 157} 158 159static bool db_breakpoints_inserted = true; 160 161void 162db_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, 173 BKPT_SIZE, false); 174 db_put_value(bkpt->address, 175 BKPT_SIZE, 176 BKPT_SET(bkpt->bkpt_inst, bkpt->address)); 177 } 178 db_breakpoints_inserted = true; 179 } 180} 181 182void 183db_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, 194 bkpt->bkpt_inst); 195 db_breakpoints_inserted = false; 196 } 197} 198 199/* 200 * List breakpoints. 201 */ 202void 203db_list_breakpoints(void) 204{ 205 db_breakpoint_t bkpt; 206 207 if (db_breakpoint_list == 0) { 208 db_printf("No breakpoints set\n"); 209 return; 210 } 211 212 db_printf(" Map Count Address\n"); 213 for (bkpt = db_breakpoint_list; 214 bkpt != 0; 215 bkpt = bkpt->link) { 216 db_printf("%s%p %5d ", 217 db_map_current(bkpt->map) ? "*" : " ", 218 bkpt->map, bkpt->init_count); 219 db_printsym(bkpt->address, DB_STGY_PROC, db_printf); 220 db_printf("\n"); 221 } 222} 223 224/* Delete breakpoint */ 225/*ARGSUSED*/ 226void 227db_delete_cmd(db_expr_t addr, bool have_addr, db_expr_t count, 228 const char *modif) 229{ 230 231 db_delete_breakpoint(db_map_addr(addr), (db_addr_t)addr); 232} 233 234/* Set breakpoint with skip count */ 235/*ARGSUSED*/ 236void 237db_breakpoint_cmd(db_expr_t addr, bool have_addr, db_expr_t count, 238 const char *modif) 239{ 240 241 if (count == -1) 242 count = 1; 243 244 db_set_breakpoint(db_map_addr(addr), (db_addr_t)addr, count); 245} 246 247/* list breakpoints */ 248/*ARGSUSED*/ 249void 250db_listbreak_cmd(db_expr_t addr, bool have_addr, 251 db_expr_t count, const char *modif) 252{ 253 254 db_list_breakpoints(); 255} 256 257#include <uvm/uvm_extern.h> 258 259/* 260 * We want ddb to be usable before most of the kernel has been 261 * initialized. In particular, current_thread() or kernel_map 262 * (or both) may be null. 263 */ 264 265bool 266db_map_equal(struct vm_map *map1, struct vm_map *map2) 267{ 268 269 return ((map1 == map2) || 270 ((map1 == NULL) && (map2 == kernel_map)) || 271 ((map1 == kernel_map) && (map2 == NULL))); 272} 273 274bool 275db_map_current(struct vm_map *map) 276{ 277#if 0 278 thread_t thread; 279 280 return ((map == NULL) || 281 (map == kernel_map) || 282 (((thread = current_thread()) != NULL) && 283 (map == thread->task->map))); 284#else 285 286 return (1); 287#endif 288} 289 290struct vm_map * 291db_map_addr(vaddr_t addr) 292{ 293#if 0 294 thread_t thread; 295 296 /* 297 * We want to return kernel_map for all 298 * non-user addresses, even when debugging 299 * kernel tasks with their own maps. 300 */ 301 302 if ((VM_MIN_ADDRESS <= addr) && (addr < VM_MAX_ADDRESS) && 303 ((thread = current_thread()) != NULL)) 304 return thread->task->map; 305 else 306#endif 307 return kernel_map; 308} 309