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