xref: /freebsd/sys/ddb/db_watch.c (revision 0c43d89a0d8e976ca494d4837f4c1f3734d2c300)
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_watch.c,v 1.4 1994/08/13 03:49:25 wollman Exp $
27  */
28 
29 /*
30  * 	Author: Richard P. Draves, Carnegie Mellon University
31  *	Date:	10/90
32  */
33 
34 #include <sys/param.h>
35 #include <sys/systm.h>
36 #include <sys/proc.h>
37 #include <ddb/ddb.h>
38 
39 #include <vm/vm_map.h>
40 #include <ddb/db_lex.h>
41 #include <ddb/db_watch.h>
42 #include <ddb/db_access.h>
43 #include <ddb/db_sym.h>
44 
45 /*
46  * Watchpoints.
47  */
48 
49 boolean_t	db_watchpoints_inserted = TRUE;
50 
51 #define	NWATCHPOINTS	100
52 struct db_watchpoint	db_watch_table[NWATCHPOINTS];
53 db_watchpoint_t		db_next_free_watchpoint = &db_watch_table[0];
54 db_watchpoint_t		db_free_watchpoints = 0;
55 db_watchpoint_t		db_watchpoint_list = 0;
56 
57 db_watchpoint_t
58 db_watchpoint_alloc()
59 {
60 	register db_watchpoint_t	watch;
61 
62 	if ((watch = db_free_watchpoints) != 0) {
63 	    db_free_watchpoints = watch->link;
64 	    return (watch);
65 	}
66 	if (db_next_free_watchpoint == &db_watch_table[NWATCHPOINTS]) {
67 	    db_printf("All watchpoints used.\n");
68 	    return (0);
69 	}
70 	watch = db_next_free_watchpoint;
71 	db_next_free_watchpoint++;
72 
73 	return (watch);
74 }
75 
76 void
77 db_watchpoint_free(watch)
78 	register db_watchpoint_t	watch;
79 {
80 	watch->link = db_free_watchpoints;
81 	db_free_watchpoints = watch;
82 }
83 
84 void
85 db_set_watchpoint(map, addr, size)
86 	vm_map_t	map;
87 	db_addr_t	addr;
88 	vm_size_t	size;
89 {
90 	register db_watchpoint_t	watch;
91 
92 	if (map == NULL) {
93 	    db_printf("No map.\n");
94 	    return;
95 	}
96 
97 	/*
98 	 *	Should we do anything fancy with overlapping regions?
99 	 */
100 
101 	for (watch = db_watchpoint_list;
102 	     watch != 0;
103 	     watch = watch->link)
104 	    if (db_map_equal(watch->map, map) &&
105 		(watch->loaddr == addr) &&
106 		(watch->hiaddr == addr+size)) {
107 		db_printf("Already set.\n");
108 		return;
109 	    }
110 
111 	watch = db_watchpoint_alloc();
112 	if (watch == 0) {
113 	    db_printf("Too many watchpoints.\n");
114 	    return;
115 	}
116 
117 	watch->map = map;
118 	watch->loaddr = addr;
119 	watch->hiaddr = addr+size;
120 
121 	watch->link = db_watchpoint_list;
122 	db_watchpoint_list = watch;
123 
124 	db_watchpoints_inserted = FALSE;
125 }
126 
127 void
128 db_delete_watchpoint(map, addr)
129 	vm_map_t	map;
130 	db_addr_t	addr;
131 {
132 	register db_watchpoint_t	watch;
133 	register db_watchpoint_t	*prev;
134 
135 	for (prev = &db_watchpoint_list;
136 	     (watch = *prev) != 0;
137 	     prev = &watch->link)
138 	    if (db_map_equal(watch->map, map) &&
139 		(watch->loaddr <= addr) &&
140 		(addr < watch->hiaddr)) {
141 		*prev = watch->link;
142 		db_watchpoint_free(watch);
143 		return;
144 	    }
145 
146 	db_printf("Not set.\n");
147 }
148 
149 void
150 db_list_watchpoints()
151 {
152 	register db_watchpoint_t	watch;
153 
154 	if (db_watchpoint_list == 0) {
155 	    db_printf("No watchpoints set\n");
156 	    return;
157 	}
158 
159 	db_printf(" Map        Address  Size\n");
160 	for (watch = db_watchpoint_list;
161 	     watch != 0;
162 	     watch = watch->link)
163 	    db_printf("%s%8x  %8x  %x\n",
164 		      db_map_current(watch->map) ? "*" : " ",
165 		      watch->map, watch->loaddr,
166 		      watch->hiaddr - watch->loaddr);
167 }
168 
169 /* Delete watchpoint */
170 /*ARGSUSED*/
171 void
172 db_deletewatch_cmd(addr, have_addr, count, modif)
173 	db_expr_t	addr;
174 	int		have_addr;
175 	db_expr_t	count;
176 	char *		modif;
177 {
178 	db_delete_watchpoint(db_map_addr(addr), addr);
179 }
180 
181 /* Set watchpoint */
182 /*ARGSUSED*/
183 void
184 db_watchpoint_cmd(addr, have_addr, count, modif)
185 	db_expr_t	addr;
186 	int		have_addr;
187 	db_expr_t	count;
188 	char *		modif;
189 {
190 	vm_size_t	size;
191 	db_expr_t	value;
192 
193 	if (db_expression(&value))
194 	    size = (vm_size_t) value;
195 	else
196 	    size = 4;
197 	db_skip_to_eol();
198 
199 	db_set_watchpoint(db_map_addr(addr), addr, size);
200 }
201 
202 /* list watchpoints */
203 void
204 db_listwatch_cmd(db_expr_t dummy1, int dummy2, db_expr_t dummy3, char *dummmy4)
205 {
206 	db_list_watchpoints();
207 }
208 
209 void
210 db_set_watchpoints()
211 {
212 	register db_watchpoint_t	watch;
213 
214 	if (!db_watchpoints_inserted) {
215 	    for (watch = db_watchpoint_list;
216 	         watch != 0;
217 	         watch = watch->link)
218 		pmap_protect(watch->map->pmap,
219 			     trunc_page(watch->loaddr),
220 			     round_page(watch->hiaddr),
221 			     VM_PROT_READ);
222 
223 	    db_watchpoints_inserted = TRUE;
224 	}
225 }
226 
227 void
228 db_clear_watchpoints()
229 {
230 	db_watchpoints_inserted = FALSE;
231 }
232 
233 boolean_t
234 db_find_watchpoint(map, addr, regs)
235 	vm_map_t	map;
236 	db_addr_t	addr;
237 	db_regs_t	*regs;
238 {
239 	register db_watchpoint_t watch;
240 	db_watchpoint_t found = 0;
241 
242 	for (watch = db_watchpoint_list;
243 	     watch != 0;
244 	     watch = watch->link)
245 	    if (db_map_equal(watch->map, map)) {
246 		if ((watch->loaddr <= addr) &&
247 		    (addr < watch->hiaddr))
248 		    return (TRUE);
249 		else if ((trunc_page(watch->loaddr) <= addr) &&
250 			 (addr < round_page(watch->hiaddr)))
251 		    found = watch;
252 	    }
253 
254 	/*
255 	 *	We didn't hit exactly on a watchpoint, but we are
256 	 *	in a protected region.  We want to single-step
257 	 *	and then re-protect.
258 	 */
259 
260 	if (found) {
261 	    db_watchpoints_inserted = FALSE;
262 	    db_single_step(regs);
263 	}
264 
265 	return (FALSE);
266 }
267