xref: /openbsd-src/sys/ddb/db_watch.c (revision 2b0358df1d88d06ef4139321dd05bd5e05d91eaf)
1 /*	$OpenBSD: db_watch.c,v 1.9 2006/03/13 06:23:20 jsg 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(void)
62 {
63 	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(db_watchpoint_t watch)
81 {
82 	watch->link = db_free_watchpoints;
83 	db_free_watchpoints = watch;
84 }
85 
86 void
87 db_set_watchpoint(struct vm_map *map, db_addr_t addr, vsize_t size)
88 {
89 	db_watchpoint_t	watch;
90 
91 	if (map == NULL) {
92 	    db_printf("No map.\n");
93 	    return;
94 	}
95 
96 	/*
97 	 *	Should we do anything fancy with overlapping regions?
98 	 */
99 
100 	for (watch = db_watchpoint_list;
101 	     watch != 0;
102 	     watch = watch->link)
103 	    if (db_map_equal(watch->map, map) &&
104 		(watch->loaddr == addr) &&
105 		(watch->hiaddr == addr+size)) {
106 		db_printf("Already set.\n");
107 		return;
108 	    }
109 
110 	watch = db_watchpoint_alloc();
111 	if (watch == 0) {
112 	    db_printf("Too many watchpoints.\n");
113 	    return;
114 	}
115 
116 	watch->map = map;
117 	watch->loaddr = addr;
118 	watch->hiaddr = addr+size;
119 
120 	watch->link = db_watchpoint_list;
121 	db_watchpoint_list = watch;
122 
123 	db_watchpoints_inserted = FALSE;
124 }
125 
126 void
127 db_delete_watchpoint(struct vm_map *map, db_addr_t addr)
128 {
129 	db_watchpoint_t	watch;
130 	db_watchpoint_t	*prev;
131 
132 	for (prev = &db_watchpoint_list;
133 	     (watch = *prev) != 0;
134 	     prev = &watch->link)
135 	    if (db_map_equal(watch->map, map) &&
136 		(watch->loaddr <= addr) &&
137 		(addr < watch->hiaddr)) {
138 		*prev = watch->link;
139 		db_watchpoint_free(watch);
140 		return;
141 	    }
142 
143 	db_printf("Not set.\n");
144 }
145 
146 void
147 db_list_watchpoints(void)
148 {
149 	db_watchpoint_t	watch;
150 
151 	if (db_watchpoint_list == 0) {
152 	    db_printf("No watchpoints set\n");
153 	    return;
154 	}
155 
156 	db_printf(" Map        Address  Size\n");
157 	for (watch = db_watchpoint_list;
158 	     watch != 0;
159 	     watch = watch->link)
160 	    db_printf("%s%p  %8lx  %lx\n",
161 		      db_map_current(watch->map) ? "*" : " ",
162 		      watch->map, watch->loaddr,
163 		      watch->hiaddr - watch->loaddr);
164 }
165 
166 /* Delete watchpoint */
167 /*ARGSUSED*/
168 void
169 db_deletewatch_cmd(db_expr_t addr, int have_addr, db_expr_t count, char *modif)
170 {
171 	db_delete_watchpoint(db_map_addr(addr), addr);
172 }
173 
174 /* Set watchpoint */
175 /*ARGSUSED*/
176 void
177 db_watchpoint_cmd(db_expr_t addr, int have_addr, db_expr_t count, char *modif)
178 {
179 	vsize_t 	size;
180 	db_expr_t	value;
181 
182 	if (db_expression(&value))
183 	    size = (vsize_t) value;
184 	else
185 	    size = 4;
186 	db_skip_to_eol();
187 
188 	db_set_watchpoint(db_map_addr(addr), addr, size);
189 }
190 
191 /* list watchpoints */
192 /*ARGSUSED*/
193 void
194 db_listwatch_cmd(db_expr_t addr, int have_addr, db_expr_t count, char *modif)
195 {
196 	db_list_watchpoints();
197 }
198 
199 void
200 db_set_watchpoints(void)
201 {
202 	db_watchpoint_t	watch;
203 
204 	if (!db_watchpoints_inserted && db_watchpoint_list != NULL) {
205 	    for (watch = db_watchpoint_list;
206 	         watch != 0;
207 	         watch = watch->link)
208 		pmap_protect(watch->map->pmap,
209 			     trunc_page(watch->loaddr),
210 			     round_page(watch->hiaddr),
211 			     VM_PROT_READ);
212 	    pmap_update(watch->map->pmap);
213 	    db_watchpoints_inserted = TRUE;
214 	}
215 }
216 
217 void
218 db_clear_watchpoints(void)
219 {
220 	db_watchpoints_inserted = FALSE;
221 }
222 
223 boolean_t
224 db_find_watchpoint(struct vm_map *map, db_addr_t addr, db_regs_t *regs)
225 {
226 	db_watchpoint_t watch;
227 	db_watchpoint_t found = 0;
228 
229 	for (watch = db_watchpoint_list;
230 	     watch != 0;
231 	     watch = watch->link)
232 	    if (db_map_equal(watch->map, map)) {
233 		if ((watch->loaddr <= addr) &&
234 		    (addr < watch->hiaddr))
235 		    return (TRUE);
236 		else if ((trunc_page(watch->loaddr) <= addr) &&
237 			 (addr < round_page(watch->hiaddr)))
238 		    found = watch;
239 	    }
240 
241 	/*
242 	 *	We didn't hit exactly on a watchpoint, but we are
243 	 *	in a protected region.  We want to single-step
244 	 *	and then re-protect.
245 	 */
246 
247 	if (found) {
248 	    db_watchpoints_inserted = FALSE;
249 	    db_single_step(regs);
250 	}
251 
252 	return (FALSE);
253 }
254