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