xref: /netbsd-src/sys/ddb/db_watch.c (revision 9fbd88883c38d0c0fbfcbe66d76fe6b0fab3f9de)
1 /*	$NetBSD: db_watch.c,v 1.18 2001/11/12 22:54:07 lukem Exp $	*/
2 
3 /*
4  * Mach Operating System
5  * Copyright (c) 1991,1990 Carnegie Mellon University
6  * All Rights Reserved.
7  *
8  * Permission to use, copy, modify and distribute this software and its
9  * documentation is hereby granted, provided that both the copyright
10  * notice and this permission notice appear in all copies of the
11  * software, derivative works or modified versions, and any portions
12  * thereof, and that both notices appear in supporting documentation.
13  *
14  * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
15  * CONDITION.  CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
16  * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
17  *
18  * Carnegie Mellon requests users of this software to return to
19  *
20  *  Software Distribution Coordinator  or  Software.Distribution@CS.CMU.EDU
21  *  School of Computer Science
22  *  Carnegie Mellon University
23  *  Pittsburgh PA 15213-3890
24  *
25  * any improvements or extensions that they make and grant Carnegie the
26  * rights to redistribute these changes.
27  *
28  * 	Author: Richard P. Draves, Carnegie Mellon University
29  *	Date:	10/90
30  */
31 
32 #include <sys/cdefs.h>
33 __KERNEL_RCSID(0, "$NetBSD: db_watch.c,v 1.18 2001/11/12 22:54:07 lukem Exp $");
34 
35 #include <sys/param.h>
36 #include <sys/proc.h>
37 
38 #include <machine/db_machdep.h>
39 
40 #include <ddb/db_break.h>
41 #include <ddb/db_watch.h>
42 #include <ddb/db_lex.h>
43 #include <ddb/db_access.h>
44 #include <ddb/db_run.h>
45 #include <ddb/db_sym.h>
46 #include <ddb/db_output.h>
47 #include <ddb/db_command.h>
48 #include <ddb/db_extern.h>
49 
50 /*
51  * Watchpoints.
52  */
53 
54 boolean_t	db_watchpoints_inserted = TRUE;
55 
56 #define	NWATCHPOINTS	100
57 struct db_watchpoint	db_watch_table[NWATCHPOINTS];
58 db_watchpoint_t		db_next_free_watchpoint = &db_watch_table[0];
59 db_watchpoint_t		db_free_watchpoints = 0;
60 db_watchpoint_t		db_watchpoint_list = 0;
61 
62 db_watchpoint_t
63 db_watchpoint_alloc()
64 {
65 	db_watchpoint_t	watch;
66 
67 	if ((watch = db_free_watchpoints) != 0) {
68 	    db_free_watchpoints = watch->link;
69 	    return (watch);
70 	}
71 	if (db_next_free_watchpoint == &db_watch_table[NWATCHPOINTS]) {
72 	    db_printf("All watchpoints used.\n");
73 	    return (0);
74 	}
75 	watch = db_next_free_watchpoint;
76 	db_next_free_watchpoint++;
77 
78 	return (watch);
79 }
80 
81 void
82 db_watchpoint_free(watch)
83 	db_watchpoint_t	watch;
84 {
85 	watch->link = db_free_watchpoints;
86 	db_free_watchpoints = watch;
87 }
88 
89 void
90 db_set_watchpoint(map, addr, size)
91 	struct vm_map	*map;
92 	db_addr_t	addr;
93 	vsize_t	size;
94 {
95 	db_watchpoint_t	watch;
96 
97 	if (map == NULL) {
98 	    db_printf("No map.\n");
99 	    return;
100 	}
101 
102 	/*
103 	 *	Should we do anything fancy with overlapping regions?
104 	 */
105 
106 	for (watch = db_watchpoint_list;
107 	     watch != 0;
108 	     watch = watch->link)
109 	    if (db_map_equal(watch->map, map) &&
110 		(watch->loaddr == addr) &&
111 		(watch->hiaddr == addr+size)) {
112 		db_printf("Already set.\n");
113 		return;
114 	    }
115 
116 	watch = db_watchpoint_alloc();
117 	if (watch == 0) {
118 	    db_printf("Too many watchpoints.\n");
119 	    return;
120 	}
121 
122 	watch->map = map;
123 	watch->loaddr = addr;
124 	watch->hiaddr = addr+size;
125 
126 	watch->link = db_watchpoint_list;
127 	db_watchpoint_list = watch;
128 
129 	db_watchpoints_inserted = FALSE;
130 }
131 
132 void
133 db_delete_watchpoint(map, addr)
134 	struct vm_map	*map;
135 	db_addr_t	addr;
136 {
137 	db_watchpoint_t	watch;
138 	db_watchpoint_t	*prev;
139 
140 	for (prev = &db_watchpoint_list;
141 	     (watch = *prev) != 0;
142 	     prev = &watch->link)
143 	    if (db_map_equal(watch->map, map) &&
144 		(watch->loaddr <= addr) &&
145 		(addr < watch->hiaddr)) {
146 		*prev = watch->link;
147 		db_watchpoint_free(watch);
148 		return;
149 	    }
150 
151 	db_printf("Not set.\n");
152 }
153 
154 void
155 db_list_watchpoints()
156 {
157 	db_watchpoint_t	watch;
158 
159 	if (db_watchpoint_list == 0) {
160 	    db_printf("No watchpoints set\n");
161 	    return;
162 	}
163 
164 	db_printf(" Map        Address  Size\n");
165 	for (watch = db_watchpoint_list;
166 	     watch != 0;
167 	     watch = watch->link)
168 	    db_printf("%s%p  %8lx  %lx\n",
169 		      db_map_current(watch->map) ? "*" : " ",
170 		      watch->map, watch->loaddr,
171 		      watch->hiaddr - watch->loaddr);
172 }
173 
174 /* Delete watchpoint */
175 /*ARGSUSED*/
176 void
177 db_deletewatch_cmd(addr, have_addr, count, modif)
178 	db_expr_t	addr;
179 	int		have_addr;
180 	db_expr_t	count;
181 	char *		modif;
182 {
183 	db_delete_watchpoint(db_map_addr(addr), addr);
184 }
185 
186 /* Set watchpoint */
187 /*ARGSUSED*/
188 void
189 db_watchpoint_cmd(addr, have_addr, count, modif)
190 	db_expr_t	addr;
191 	int		have_addr;
192 	db_expr_t	count;
193 	char *		modif;
194 {
195 	vsize_t	size;
196 	db_expr_t	value;
197 
198 	if (db_expression(&value))
199 	    size = (vsize_t) value;
200 	else
201 	    size = 4;
202 	db_skip_to_eol();
203 
204 	db_set_watchpoint(db_map_addr(addr), addr, size);
205 }
206 
207 /* list watchpoints */
208 /*ARGSUSED*/
209 void
210 db_listwatch_cmd(addr, have_addr, count, modif)
211 	db_expr_t	addr;
212 	int		have_addr;
213 	db_expr_t	count;
214 	char *		modif;
215 {
216 	db_list_watchpoints();
217 }
218 
219 void
220 db_set_watchpoints()
221 {
222 	db_watchpoint_t	watch;
223 
224 	if (!db_watchpoints_inserted) {
225 	    for (watch = db_watchpoint_list;
226 	         watch != 0;
227 	         watch = watch->link) {
228 		pmap_protect(watch->map->pmap,
229 			     trunc_page(watch->loaddr),
230 			     round_page(watch->hiaddr),
231 			     VM_PROT_READ);
232 		pmap_update(watch->map->pmap);
233 	    }
234 
235 	    db_watchpoints_inserted = TRUE;
236 	}
237 }
238 
239 void
240 db_clear_watchpoints()
241 {
242 	db_watchpoints_inserted = FALSE;
243 }
244 
245 boolean_t
246 db_find_watchpoint(map, addr, regs)
247 	struct vm_map	*map;
248 	db_addr_t	addr;
249 	db_regs_t	*regs;
250 {
251 	db_watchpoint_t watch;
252 	db_watchpoint_t found = 0;
253 
254 	for (watch = db_watchpoint_list;
255 	     watch != 0;
256 	     watch = watch->link)
257 	    if (db_map_equal(watch->map, map)) {
258 		if ((watch->loaddr <= addr) &&
259 		    (addr < watch->hiaddr))
260 		    return (TRUE);
261 		else if ((trunc_page(watch->loaddr) <= addr) &&
262 			 (addr < round_page(watch->hiaddr)))
263 		    found = watch;
264 	    }
265 
266 	/*
267 	 *	We didn't hit exactly on a watchpoint, but we are
268 	 *	in a protected region.  We want to single-step
269 	 *	and then re-protect.
270 	 */
271 
272 	if (found) {
273 	    db_watchpoints_inserted = FALSE;
274 	    db_single_step(regs);
275 	}
276 
277 	return (FALSE);
278 }
279