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