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