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