ee9a32a9fb3cca6bba29d479dd84ddac2b3744e6
[dragonfly.git] / sys / ddb / db_break.c
1 /*
2  * Mach Operating System
3  * Copyright (c) 1991,1990 Carnegie Mellon University
4  * All Rights Reserved.
5  *
6  * Permission to use, copy, modify and distribute this software and its
7  * documentation is hereby granted, provided that both the copyright
8  * notice and this permission notice appear in all copies of the
9  * software, derivative works or modified versions, and any portions
10  * thereof, and that both notices appear in supporting documentation.
11  *
12  * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS
13  * CONDITION.  CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
14  * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
15  *
16  * Carnegie Mellon requests users of this software to return to
17  *
18  *  Software Distribution Coordinator  or  Software.Distribution@CS.CMU.EDU
19  *  School of Computer Science
20  *  Carnegie Mellon University
21  *  Pittsburgh PA 15213-3890
22  *
23  * any improvements or extensions that they make and grant Carnegie the
24  * rights to redistribute these changes.
25  *
26  * $FreeBSD: src/sys/ddb/db_break.c,v 1.18 1999/08/28 00:41:05 peter Exp $
27  * $DragonFly: src/sys/ddb/db_break.c,v 1.2 2003/06/17 04:28:20 dillon Exp $
28  */
29
30 /*
31  *      Author: David B. Golub, Carnegie Mellon University
32  *      Date:   7/90
33  */
34 /*
35  * Breakpoints.
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 __P((void));
54 static void     db_breakpoint_free __P((db_breakpoint_t bkpt));
55 static void     db_delete_breakpoint __P((vm_map_t map, db_addr_t addr));
56 static db_breakpoint_t  db_find_breakpoint __P((vm_map_t map, db_addr_t addr));
57 static void     db_list_breakpoints __P((void));
58 static void     db_set_breakpoint __P((vm_map_t map, db_addr_t addr,
59                                        int count));
60
61 static db_breakpoint_t
62 db_breakpoint_alloc()
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(bkpt)
82         register db_breakpoint_t        bkpt;
83 {
84         bkpt->link = db_free_breakpoints;
85         db_free_breakpoints = bkpt;
86 }
87
88 static void
89 db_set_breakpoint(map, addr, count)
90         vm_map_t        map;
91         db_addr_t       addr;
92         int             count;
93 {
94         register db_breakpoint_t        bkpt;
95
96         if (db_find_breakpoint(map, addr)) {
97             db_printf("Already set.\n");
98             return;
99         }
100
101         bkpt = db_breakpoint_alloc();
102         if (bkpt == 0) {
103             db_printf("Too many breakpoints.\n");
104             return;
105         }
106
107         bkpt->map = map;
108         bkpt->address = addr;
109         bkpt->flags = 0;
110         bkpt->init_count = count;
111         bkpt->count = count;
112
113         bkpt->link = db_breakpoint_list;
114         db_breakpoint_list = bkpt;
115 }
116
117 static void
118 db_delete_breakpoint(map, addr)
119         vm_map_t        map;
120         db_addr_t       addr;
121 {
122         register db_breakpoint_t        bkpt;
123         register db_breakpoint_t        *prev;
124
125         for (prev = &db_breakpoint_list;
126              (bkpt = *prev) != 0;
127              prev = &bkpt->link) {
128             if (db_map_equal(bkpt->map, map) &&
129                 (bkpt->address == addr)) {
130                 *prev = bkpt->link;
131                 break;
132             }
133         }
134         if (bkpt == 0) {
135             db_printf("Not set.\n");
136             return;
137         }
138
139         db_breakpoint_free(bkpt);
140 }
141
142 static db_breakpoint_t
143 db_find_breakpoint(map, addr)
144         vm_map_t        map;
145         db_addr_t       addr;
146 {
147         register db_breakpoint_t        bkpt;
148
149         for (bkpt = db_breakpoint_list;
150              bkpt != 0;
151              bkpt = bkpt->link)
152         {
153             if (db_map_equal(bkpt->map, map) &&
154                 (bkpt->address == addr))
155                 return (bkpt);
156         }
157         return (0);
158 }
159
160 db_breakpoint_t
161 db_find_breakpoint_here(addr)
162         db_addr_t       addr;
163 {
164     return db_find_breakpoint(db_map_addr(addr), addr);
165 }
166
167 static boolean_t        db_breakpoints_inserted = TRUE;
168
169 void
170 db_set_breakpoints()
171 {
172         register db_breakpoint_t        bkpt;
173
174         if (!db_breakpoints_inserted) {
175
176             for (bkpt = db_breakpoint_list;
177                  bkpt != 0;
178                  bkpt = bkpt->link)
179                 if (db_map_current(bkpt->map)) {
180                     bkpt->bkpt_inst = db_get_value(bkpt->address,
181                                                    BKPT_SIZE,
182                                                    FALSE);
183                     db_put_value(bkpt->address,
184                                  BKPT_SIZE,
185                                  BKPT_SET(bkpt->bkpt_inst));
186                 }
187             db_breakpoints_inserted = TRUE;
188         }
189 }
190
191 void
192 db_clear_breakpoints()
193 {
194         register db_breakpoint_t        bkpt;
195
196         if (db_breakpoints_inserted) {
197
198             for (bkpt = db_breakpoint_list;
199                  bkpt != 0;
200                  bkpt = bkpt->link)
201                 if (db_map_current(bkpt->map)) {
202                     db_put_value(bkpt->address, BKPT_SIZE, bkpt->bkpt_inst);
203                 }
204             db_breakpoints_inserted = FALSE;
205         }
206 }
207
208 #ifdef SOFTWARE_SSTEP
209 /*
210  * Set a temporary breakpoint.
211  * The instruction is changed immediately,
212  * so the breakpoint does not have to be on the breakpoint list.
213  */
214 db_breakpoint_t
215 db_set_temp_breakpoint(addr)
216         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->bkpt_inst = db_get_value(bkpt->address, BKPT_SIZE, FALSE);
233         db_put_value(bkpt->address, BKPT_SIZE, BKPT_SET(bkpt->bkpt_inst));
234         return bkpt;
235 }
236
237 void
238 db_delete_temp_breakpoint(bkpt)
239         db_breakpoint_t bkpt;
240 {
241         db_put_value(bkpt->address, BKPT_SIZE, bkpt->bkpt_inst);
242         db_breakpoint_free(bkpt);
243 }
244 #endif /* SOFTWARE_SSTEP */
245
246 /*
247  * List breakpoints.
248  */
249 static void
250 db_list_breakpoints()
251 {
252         register db_breakpoint_t        bkpt;
253
254         if (db_breakpoint_list == 0) {
255             db_printf("No breakpoints set\n");
256             return;
257         }
258
259         db_printf(" Map      Count    Address\n");
260         for (bkpt = db_breakpoint_list;
261              bkpt != 0;
262              bkpt = bkpt->link) {
263             db_printf("%s%8p %5d    ",
264                       db_map_current(bkpt->map) ? "*" : " ",
265                       (void *)bkpt->map, bkpt->init_count);
266             db_printsym(bkpt->address, DB_STGY_PROC);
267             db_printf("\n");
268         }
269 }
270
271 /* Delete breakpoint */
272 /*ARGSUSED*/
273 void
274 db_delete_cmd(addr, have_addr, count, modif)
275         db_expr_t       addr;
276         boolean_t       have_addr;
277         db_expr_t       count;
278         char *          modif;
279 {
280         db_delete_breakpoint(db_map_addr(addr), (db_addr_t)addr);
281 }
282
283 /* Set breakpoint with skip count */
284 /*ARGSUSED*/
285 void
286 db_breakpoint_cmd(addr, have_addr, count, modif)
287         db_expr_t       addr;
288         boolean_t       have_addr;
289         db_expr_t       count;
290         char *          modif;
291 {
292         if (count == -1)
293             count = 1;
294
295         db_set_breakpoint(db_map_addr(addr), (db_addr_t)addr, count);
296 }
297
298 /* list breakpoints */
299 void
300 db_listbreak_cmd(dummy1, dummy2, dummy3, dummy4)
301         db_expr_t       dummy1;
302         boolean_t       dummy2;
303         db_expr_t       dummy3;
304         char *          dummy4;
305 {
306         db_list_breakpoints();
307 }
308
309 /*
310  *      We want ddb to be usable before most of the kernel has been
311  *      initialized.  In particular, current_thread() or kernel_map
312  *      (or both) may be null.
313  */
314
315 boolean_t
316 db_map_equal(map1, map2)
317         vm_map_t        map1, map2;
318 {
319         return ((map1 == map2) ||
320                 ((map1 == NULL) && (map2 == kernel_map)) ||
321                 ((map1 == kernel_map) && (map2 == NULL)));
322 }
323
324 boolean_t
325 db_map_current(map)
326         vm_map_t        map;
327 {
328 #if 0
329         thread_t        thread;
330
331         return ((map == NULL) ||
332                 (map == kernel_map) ||
333                 (((thread = current_thread()) != NULL) &&
334                  (map == thread->task->map)));
335 #else
336         return (1);
337 #endif
338 }
339
340 vm_map_t
341 db_map_addr(addr)
342         vm_offset_t addr;
343 {
344 #if 0
345         thread_t        thread;
346
347         /*
348          *      We want to return kernel_map for all
349          *      non-user addresses, even when debugging
350          *      kernel tasks with their own maps.
351          */
352
353         if ((VM_MIN_ADDRESS <= addr) &&
354             (addr < VM_MAX_ADDRESS) &&
355             ((thread = current_thread()) != NULL))
356             return thread->task->map;
357         else
358 #endif
359             return kernel_map;
360 }