xref: /freebsd/sys/ddb/db_break.c (revision 2be1a816b9ff69588e55be0a84cbe2a31efc0f2f)
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  */
27 /*
28  *	Author: David B. Golub, Carnegie Mellon University
29  *	Date:	7/90
30  */
31 /*
32  * Breakpoints.
33  */
34 
35 #include <sys/cdefs.h>
36 __FBSDID("$FreeBSD$");
37 
38 #include "opt_comconsole.h"
39 
40 #include <sys/param.h>
41 
42 #include <vm/vm.h>
43 #include <vm/vm_kern.h>
44 
45 #include <ddb/ddb.h>
46 #include <ddb/db_break.h>
47 #include <ddb/db_access.h>
48 #include <ddb/db_sym.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 bkpt);
58 static void	db_delete_breakpoint(vm_map_t map, db_addr_t addr);
59 static db_breakpoint_t	db_find_breakpoint(vm_map_t map, db_addr_t addr);
60 static void	db_list_breakpoints(void);
61 static void	db_set_breakpoint(vm_map_t map, db_addr_t addr, int count);
62 
63 static db_breakpoint_t
64 db_breakpoint_alloc()
65 {
66 	register 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
83 db_breakpoint_free(bkpt)
84 	register db_breakpoint_t	bkpt;
85 {
86 	bkpt->link = db_free_breakpoints;
87 	db_free_breakpoints = bkpt;
88 }
89 
90 static void
91 db_set_breakpoint(map, addr, count)
92 	vm_map_t	map;
93 	db_addr_t	addr;
94 	int		count;
95 {
96 	register db_breakpoint_t	bkpt;
97 
98 	if (db_find_breakpoint(map, addr)) {
99 	    db_printf("Already set.\n");
100 	    return;
101 	}
102 
103 	bkpt = db_breakpoint_alloc();
104 	if (bkpt == 0) {
105 	    db_printf("Too many breakpoints.\n");
106 	    return;
107 	}
108 
109 	bkpt->map = map;
110 	bkpt->address = addr;
111 	bkpt->flags = 0;
112 	bkpt->init_count = count;
113 	bkpt->count = count;
114 
115 	bkpt->link = db_breakpoint_list;
116 	db_breakpoint_list = bkpt;
117 }
118 
119 static void
120 db_delete_breakpoint(map, addr)
121 	vm_map_t	map;
122 	db_addr_t	addr;
123 {
124 	register db_breakpoint_t	bkpt;
125 	register db_breakpoint_t	*prev;
126 
127 	for (prev = &db_breakpoint_list;
128 	     (bkpt = *prev) != 0;
129 	     prev = &bkpt->link) {
130 	    if (db_map_equal(bkpt->map, map) &&
131 		(bkpt->address == addr)) {
132 		*prev = bkpt->link;
133 		break;
134 	    }
135 	}
136 	if (bkpt == 0) {
137 	    db_printf("Not set.\n");
138 	    return;
139 	}
140 
141 	db_breakpoint_free(bkpt);
142 }
143 
144 static db_breakpoint_t
145 db_find_breakpoint(map, addr)
146 	vm_map_t	map;
147 	db_addr_t	addr;
148 {
149 	register db_breakpoint_t	bkpt;
150 
151 	for (bkpt = db_breakpoint_list;
152 	     bkpt != 0;
153 	     bkpt = bkpt->link)
154 	{
155 	    if (db_map_equal(bkpt->map, map) &&
156 		(bkpt->address == addr))
157 		return (bkpt);
158 	}
159 	return (0);
160 }
161 
162 db_breakpoint_t
163 db_find_breakpoint_here(addr)
164 	db_addr_t	addr;
165 {
166     return db_find_breakpoint(db_map_addr(addr), addr);
167 }
168 
169 static boolean_t	db_breakpoints_inserted = TRUE;
170 
171 #ifndef BKPT_WRITE
172 #define BKPT_WRITE(addr, storage)				\
173 do {								\
174 	*storage = db_get_value(addr, BKPT_SIZE, FALSE);	\
175 	db_put_value(addr, BKPT_SIZE, BKPT_SET(*storage));	\
176 } while (0)
177 #endif
178 
179 #ifndef BKPT_CLEAR
180 #define BKPT_CLEAR(addr, storage) \
181 	db_put_value(addr, BKPT_SIZE, *storage)
182 #endif
183 
184 void
185 db_set_breakpoints()
186 {
187 	register db_breakpoint_t	bkpt;
188 
189 	if (!db_breakpoints_inserted) {
190 
191 		for (bkpt = db_breakpoint_list;
192 		     bkpt != 0;
193 		     bkpt = bkpt->link)
194 			if (db_map_current(bkpt->map)) {
195 				BKPT_WRITE(bkpt->address, &bkpt->bkpt_inst);
196 			}
197 		db_breakpoints_inserted = TRUE;
198 	}
199 }
200 
201 void
202 db_clear_breakpoints()
203 {
204 	register db_breakpoint_t	bkpt;
205 
206 	if (db_breakpoints_inserted) {
207 
208 		for (bkpt = db_breakpoint_list;
209 		     bkpt != 0;
210 		     bkpt = bkpt->link)
211 			if (db_map_current(bkpt->map)) {
212 				BKPT_CLEAR(bkpt->address, &bkpt->bkpt_inst);
213 			}
214 		db_breakpoints_inserted = FALSE;
215 	}
216 }
217 
218 #ifdef SOFTWARE_SSTEP
219 /*
220  * Set a temporary breakpoint.
221  * The instruction is changed immediately,
222  * so the breakpoint does not have to be on the breakpoint list.
223  */
224 db_breakpoint_t
225 db_set_temp_breakpoint(addr)
226 	db_addr_t	addr;
227 {
228 	register db_breakpoint_t	bkpt;
229 
230 	bkpt = db_breakpoint_alloc();
231 	if (bkpt == 0) {
232 	    db_printf("Too many breakpoints.\n");
233 	    return 0;
234 	}
235 
236 	bkpt->map = NULL;
237 	bkpt->address = addr;
238 	bkpt->flags = BKPT_TEMP;
239 	bkpt->init_count = 1;
240 	bkpt->count = 1;
241 
242 	BKPT_WRITE(bkpt->address, &bkpt->bkpt_inst);
243 	return bkpt;
244 }
245 
246 void
247 db_delete_temp_breakpoint(bkpt)
248 	db_breakpoint_t	bkpt;
249 {
250 	BKPT_CLEAR(bkpt->address, &bkpt->bkpt_inst);
251 	db_breakpoint_free(bkpt);
252 }
253 #endif /* SOFTWARE_SSTEP */
254 
255 /*
256  * List breakpoints.
257  */
258 static void
259 db_list_breakpoints()
260 {
261 	register db_breakpoint_t	bkpt;
262 
263 	if (db_breakpoint_list == 0) {
264 	    db_printf("No breakpoints set\n");
265 	    return;
266 	}
267 
268 	db_printf(" Map      Count    Address\n");
269 	for (bkpt = db_breakpoint_list;
270 	     bkpt != 0;
271 	     bkpt = bkpt->link) {
272 	    db_printf("%s%8p %5d    ",
273 		      db_map_current(bkpt->map) ? "*" : " ",
274 		      (void *)bkpt->map, bkpt->init_count);
275 	    db_printsym(bkpt->address, DB_STGY_PROC);
276 	    db_printf("\n");
277 	}
278 }
279 
280 /* Delete breakpoint */
281 /*ARGSUSED*/
282 void
283 db_delete_cmd(addr, have_addr, count, modif)
284 	db_expr_t	addr;
285 	boolean_t	have_addr;
286 	db_expr_t	count;
287 	char *		modif;
288 {
289 	db_delete_breakpoint(db_map_addr(addr), (db_addr_t)addr);
290 }
291 
292 /* Set breakpoint with skip count */
293 /*ARGSUSED*/
294 void
295 db_breakpoint_cmd(addr, have_addr, count, modif)
296 	db_expr_t	addr;
297 	boolean_t	have_addr;
298 	db_expr_t	count;
299 	char *		modif;
300 {
301 	if (count == -1)
302 	    count = 1;
303 
304 	db_set_breakpoint(db_map_addr(addr), (db_addr_t)addr, count);
305 }
306 
307 /* list breakpoints */
308 void
309 db_listbreak_cmd(dummy1, dummy2, dummy3, dummy4)
310 	db_expr_t	dummy1;
311 	boolean_t	dummy2;
312 	db_expr_t	dummy3;
313 	char *		dummy4;
314 {
315 	db_list_breakpoints();
316 }
317 
318 /*
319  *	We want ddb to be usable before most of the kernel has been
320  *	initialized.  In particular, current_thread() or kernel_map
321  *	(or both) may be null.
322  */
323 
324 boolean_t
325 db_map_equal(map1, map2)
326 	vm_map_t	map1, map2;
327 {
328 	return ((map1 == map2) ||
329 		((map1 == NULL) && (map2 == kernel_map)) ||
330 		((map1 == kernel_map) && (map2 == NULL)));
331 }
332 
333 boolean_t
334 db_map_current(map)
335 	vm_map_t	map;
336 {
337 #if 0
338 	thread_t	thread;
339 
340 	return ((map == NULL) ||
341 		(map == kernel_map) ||
342 		(((thread = current_thread()) != NULL) &&
343 		 (map == thread->task->map)));
344 #else
345 	return (1);
346 #endif
347 }
348 
349 vm_map_t
350 db_map_addr(addr)
351 	vm_offset_t addr;
352 {
353 #if 0
354 	thread_t	thread;
355 
356 	/*
357 	 *	We want to return kernel_map for all
358 	 *	non-user addresses, even when debugging
359 	 *	kernel tasks with their own maps.
360 	 */
361 
362 	if ((VM_MIN_ADDRESS <= addr) &&
363 	    (addr < VM_MAX_ADDRESS) &&
364 	    ((thread = current_thread()) != NULL))
365 	    return thread->task->map;
366 	else
367 #endif
368 	    return kernel_map;
369 }
370