xref: /openbsd/sys/ddb/db_break.c (revision 78b63d65)
1 /*	$OpenBSD: db_break.c,v 1.11 2001/11/28 16:13:29 art Exp $	*/
2 /*	$NetBSD: db_break.c,v 1.7 1996/03/30 22:30:03 christos Exp $	*/
3 
4 /*
5  * Mach Operating System
6  * Copyright (c) 1993,1992,1991,1990 Carnegie Mellon University
7  * All Rights Reserved.
8  *
9  * Permission to use, copy, modify and distribute this software and its
10  * documentation is hereby granted, provided that both the copyright
11  * notice and this permission notice appear in all copies of the
12  * software, derivative works or modified versions, and any portions
13  * thereof, and that both notices appear in supporting documentation.
14  *
15  * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
16  * CONDITION.  CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
17  * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
18  *
19  * Carnegie Mellon requests users of this software to return to
20  *
21  *  Software Distribution Coordinator  or  Software.Distribution@CS.CMU.EDU
22  *  School of Computer Science
23  *  Carnegie Mellon University
24  *  Pittsburgh PA 15213-3890
25  *
26  * any improvements or extensions that they make and grant Carnegie Mellon
27  * the rights to redistribute these changes.
28  *
29  *	Author: David B. Golub, Carnegie Mellon University
30  *	Date:	7/90
31  */
32 
33 /*
34  * Breakpoints.
35  */
36 #include <sys/param.h>
37 #include <sys/proc.h>
38 
39 #include <uvm/uvm_extern.h>
40 
41 #include <machine/db_machdep.h>		/* type definitions */
42 
43 #include <ddb/db_lex.h>
44 #include <ddb/db_access.h>
45 #include <ddb/db_sym.h>
46 #include <ddb/db_break.h>
47 #include <ddb/db_output.h>
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 	struct vm_map  *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 #ifdef DB_VALID_BREAKPOINT
96 	if (!DB_VALID_BREAKPOINT(addr)) {
97 		db_printf("Not a valid address for a breakpoint.\n");
98 		return;
99 	}
100 #endif
101 
102 	bkpt = db_breakpoint_alloc();
103 	if (bkpt == 0) {
104 	    db_printf("Too many breakpoints.\n");
105 	    return;
106 	}
107 
108 	bkpt->map = map;
109 	bkpt->address = addr;
110 	bkpt->flags = 0;
111 	bkpt->init_count = count;
112 	bkpt->count = count;
113 
114 	bkpt->link = db_breakpoint_list;
115 	db_breakpoint_list = bkpt;
116 }
117 
118 void
119 db_delete_breakpoint(map, addr)
120 	struct vm_map  *map;
121 	db_addr_t	addr;
122 {
123 	register db_breakpoint_t	bkpt;
124 	register db_breakpoint_t	*prev;
125 
126 	for (prev = &db_breakpoint_list;
127 	     (bkpt = *prev) != 0;
128 	     prev = &bkpt->link) {
129 	    if (db_map_equal(bkpt->map, map) &&
130 		(bkpt->address == addr)) {
131 		*prev = bkpt->link;
132 		break;
133 	    }
134 	}
135 	if (bkpt == 0) {
136 	    db_printf("Not set.\n");
137 	    return;
138 	}
139 
140 	db_breakpoint_free(bkpt);
141 }
142 
143 db_breakpoint_t
144 db_find_breakpoint(map, addr)
145 	struct vm_map  *map;
146 	db_addr_t	addr;
147 {
148 	register db_breakpoint_t	bkpt;
149 
150 	for (bkpt = db_breakpoint_list;
151 	     bkpt != 0;
152 	     bkpt = bkpt->link)
153 	{
154 	    if (db_map_equal(bkpt->map, map) &&
155 		(bkpt->address == addr))
156 		return (bkpt);
157 	}
158 	return (0);
159 }
160 
161 db_breakpoint_t
162 db_find_breakpoint_here(addr)
163 	db_addr_t	addr;
164 {
165     return db_find_breakpoint(db_map_addr(addr), addr);
166 }
167 
168 boolean_t	db_breakpoints_inserted = TRUE;
169 
170 void
171 db_set_breakpoints()
172 {
173 	register db_breakpoint_t	bkpt;
174 
175 	if (!db_breakpoints_inserted) {
176 
177 	    for (bkpt = db_breakpoint_list;
178 	         bkpt != 0;
179 	         bkpt = bkpt->link)
180 		if (db_map_current(bkpt->map)) {
181 		    bkpt->bkpt_inst = db_get_value(bkpt->address, BKPT_SIZE,
182 			FALSE);
183 		    db_put_value(bkpt->address, BKPT_SIZE,
184 			BKPT_SET(bkpt->bkpt_inst));
185 		}
186 	    db_breakpoints_inserted = TRUE;
187 	}
188 }
189 
190 void
191 db_clear_breakpoints()
192 {
193 	register db_breakpoint_t	bkpt;
194 
195 	if (db_breakpoints_inserted) {
196 
197 	    for (bkpt = db_breakpoint_list;
198 	         bkpt != 0;
199 		 bkpt = bkpt->link)
200 		if (db_map_current(bkpt->map)) {
201 		    db_put_value(bkpt->address, BKPT_SIZE, bkpt->bkpt_inst);
202 		}
203 	    db_breakpoints_inserted = FALSE;
204 	}
205 }
206 
207 /*
208  * Set a temporary breakpoint.
209  * The instruction is changed immediately,
210  * so the breakpoint does not have to be on the breakpoint list.
211  */
212 db_breakpoint_t
213 db_set_temp_breakpoint(addr)
214 	db_addr_t	addr;
215 {
216 	db_breakpoint_t	bkpt;
217 
218 #ifdef DB_VALID_BREAKPOINT
219 	if (!DB_VALID_BREAKPOINT(addr)) {
220 		db_printf("Not a valid address for a breakpoint.\n");
221 		return (0);
222 	}
223 #endif
224 
225 	bkpt = db_breakpoint_alloc();
226 	if (bkpt == 0) {
227 	    db_printf("Too many breakpoints.\n");
228 	    return (0);
229 	}
230 
231 	bkpt->map = NULL;
232 	bkpt->address = addr;
233 	bkpt->flags = BKPT_TEMP;
234 	bkpt->init_count = 1;
235 	bkpt->count = 1;
236 
237 	bkpt->bkpt_inst = db_get_value(bkpt->address, BKPT_SIZE, FALSE);
238 	db_put_value(bkpt->address, BKPT_SIZE, BKPT_SET(bkpt->bkpt_inst));
239 	return bkpt;
240 }
241 
242 void
243 db_delete_temp_breakpoint(bkpt)
244 	db_breakpoint_t	bkpt;
245 {
246 	db_put_value(bkpt->address, BKPT_SIZE, bkpt->bkpt_inst);
247 	db_breakpoint_free(bkpt);
248 }
249 
250 /*
251  * List breakpoints.
252  */
253 void
254 db_list_breakpoints()
255 {
256 	register db_breakpoint_t	bkpt;
257 
258 	if (db_breakpoint_list == 0) {
259 	    db_printf("No breakpoints set\n");
260 	    return;
261 	}
262 
263 	db_printf(" Map      Count    Address\n");
264 	for (bkpt = db_breakpoint_list;
265 	     bkpt != 0;
266 	     bkpt = bkpt->link)
267 	{
268 	    db_printf("%s%p %5d    ",
269 		      db_map_current(bkpt->map) ? "*" : " ",
270 		      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 	int		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 	int		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 /*ARGSUSED*/
305 void
306 db_listbreak_cmd(addr, have_addr, count, modif)
307 	db_expr_t	addr;
308 	int		have_addr;
309 	db_expr_t	count;
310 	char *		modif;
311 {
312 	db_list_breakpoints();
313 }
314 
315 /*
316  *	We want ddb to be usable before most of the kernel has been
317  *	initialized.  In particular, current_thread() or kernel_map
318  *	(or both) may be null.
319  */
320 
321 boolean_t
322 db_map_equal(map1, map2)
323 	struct vm_map *map1, *map2;
324 {
325 	return ((map1 == map2) ||
326 		((map1 == NULL) && (map2 == kernel_map)) ||
327 		((map1 == kernel_map) && (map2 == NULL)));
328 }
329 
330 boolean_t
331 db_map_current(map)
332 	struct vm_map *map;
333 {
334 #if 0
335 	thread_t	thread;
336 
337 	return ((map == NULL) ||
338 		(map == kernel_map) ||
339 		(((thread = current_thread()) != NULL) &&
340 		 (map == thread->proc->map)));
341 #else
342 	return (1);
343 #endif
344 }
345 
346 struct vm_map *
347 db_map_addr(addr)
348 	vaddr_t addr;
349 {
350 #if 0
351 	thread_t	thread;
352 
353 	/*
354 	 *	We want to return kernel_map for all
355 	 *	non-user addresses, even when debugging
356 	 *	kernel tasks with their own maps.
357 	 */
358 
359 	if ((VM_MIN_ADDRESS <= addr) &&
360 	    (addr < VM_MAX_ADDRESS) &&
361 	    ((thread = current_thread()) != NULL))
362 	    return thread->proc->map;
363 	else
364 #endif
365 	    return kernel_map;
366 }
367