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