1 /*        $NetBSD: db_break.c,v 1.26 2007/02/22 06:41:00 thorpej 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.26 2007/02/22 06:41:00 thorpej 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
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);
58 static void                   db_delete_breakpoint(struct vm_map *, db_addr_t);
59 static db_breakpoint_t        db_find_breakpoint(struct vm_map *, db_addr_t);
60 static void                   db_list_breakpoints(void);
61 static void                   db_set_breakpoint(struct vm_map *, db_addr_t, int);
62 
63 static db_breakpoint_t
db_breakpoint_alloc(void)64 db_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 
82 static void
db_breakpoint_free(db_breakpoint_t bkpt)83 db_breakpoint_free(db_breakpoint_t bkpt)
84 {
85           bkpt->link = db_free_breakpoints;
86           db_free_breakpoints = bkpt;
87 }
88 
89 void
db_set_breakpoint(struct vm_map * map,db_addr_t addr,int count)90 db_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 
115 static void
db_delete_breakpoint(struct vm_map * map,db_addr_t addr)116 db_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 
138 db_breakpoint_t
db_find_breakpoint(struct vm_map * map,db_addr_t addr)139 db_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 
153 db_breakpoint_t
db_find_breakpoint_here(db_addr_t addr)154 db_find_breakpoint_here(db_addr_t addr)
155 {
156           return db_find_breakpoint(db_map_addr(addr), addr);
157 }
158 
159 static bool db_breakpoints_inserted = true;
160 
161 void
db_set_breakpoints(void)162 db_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 
182 void
db_clear_breakpoints(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,
194                                         bkpt->bkpt_inst);
195                     db_breakpoints_inserted = false;
196           }
197 }
198 
199 /*
200  * List breakpoints.
201  */
202 void
db_list_breakpoints(void)203 db_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*/
226 void
db_delete_cmd(db_expr_t addr,bool have_addr,db_expr_t count,const char * modif)227 db_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*/
236 void
db_breakpoint_cmd(db_expr_t addr,bool have_addr,db_expr_t count,const char * modif)237 db_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*/
249 void
db_listbreak_cmd(db_expr_t addr,bool have_addr,db_expr_t count,const char * modif)250 db_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 
265 bool
db_map_equal(struct vm_map * map1,struct vm_map * map2)266 db_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 
274 bool
db_map_current(struct vm_map * map)275 db_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 
290 struct vm_map *
db_map_addr(vaddr_t addr)291 db_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