]> git.saurik.com Git - apple/xnu.git/blob - osfmk/ddb/db_watch.c
6533e2b2ea6e90e2e83448e460c05f195d539332
[apple/xnu.git] / osfmk / ddb / db_watch.c
1 /*
2 * Copyright (c) 2000-2004 Apple Computer, Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
11 * file.
12 *
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23 /*
24 * @OSF_COPYRIGHT@
25 */
26 /*
27 * Mach Operating System
28 * Copyright (c) 1991,1990 Carnegie Mellon University
29 * All Rights Reserved.
30 *
31 * Permission to use, copy, modify and distribute this software and its
32 * documentation is hereby granted, provided that both the copyright
33 * notice and this permission notice appear in all copies of the
34 * software, derivative works or modified versions, and any portions
35 * thereof, and that both notices appear in supporting documentation.
36 *
37 * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
38 * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
39 * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
40 *
41 * Carnegie Mellon requests users of this software to return to
42 *
43 * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
44 * School of Computer Science
45 * Carnegie Mellon University
46 * Pittsburgh PA 15213-3890
47 *
48 * any improvements or extensions that they make and grant Carnegie Mellon
49 * the rights to redistribute these changes.
50 */
51 /*
52 */
53 /*
54 * Author: Richard P. Draves, Carnegie Mellon University
55 * Date: 10/90
56 */
57
58 #include <mach/boolean.h>
59 #include <mach/vm_param.h>
60 #include <mach/machine/vm_types.h>
61 #include <mach/machine/vm_param.h>
62 #include <vm/vm_map.h>
63
64 #include <machine/db_machdep.h>
65 #include <ddb/db_lex.h>
66 #include <ddb/db_watch.h>
67 #include <ddb/db_access.h>
68 #include <ddb/db_sym.h>
69 #include <ddb/db_task_thread.h>
70 #include <ddb/db_command.h>
71 #include <ddb/db_expr.h>
72 #include <ddb/db_output.h> /* For db_printf() */
73 #include <ddb/db_run.h> /* For db_single_step() */
74
75 /*
76 * Watchpoints.
77 */
78
79 boolean_t db_watchpoints_inserted = TRUE;
80
81 #define NWATCHPOINTS 100
82 struct db_watchpoint db_watch_table[NWATCHPOINTS];
83 db_watchpoint_t db_next_free_watchpoint = &db_watch_table[0];
84 db_watchpoint_t db_free_watchpoints = 0;
85 db_watchpoint_t db_watchpoint_list = 0;
86
87 extern vm_map_t kernel_map;
88
89
90
91 /* Prototypes for functions local to this file. XXX -- should be static.
92 */
93
94 db_watchpoint_t db_watchpoint_alloc(void);
95
96 void db_watchpoint_free(register db_watchpoint_t watch);
97
98 void db_set_watchpoint(
99 task_t task,
100 db_addr_t addr,
101 vm_size_t size);
102
103 void db_delete_watchpoint(
104 task_t task,
105 db_addr_t addr);
106
107 static int db_get_task(
108 char *modif,
109 task_t *taskp,
110 db_addr_t addr);
111
112 void db_list_watchpoints(void);
113
114
115
116 db_watchpoint_t
117 db_watchpoint_alloc(void)
118 {
119 register db_watchpoint_t watch;
120
121 if ((watch = db_free_watchpoints) != 0) {
122 db_free_watchpoints = watch->link;
123 return (watch);
124 }
125 if (db_next_free_watchpoint == &db_watch_table[NWATCHPOINTS]) {
126 db_printf("All watchpoints used.\n");
127 return (0);
128 }
129 watch = db_next_free_watchpoint;
130 db_next_free_watchpoint++;
131
132 return (watch);
133 }
134
135 void
136 db_watchpoint_free(register db_watchpoint_t watch)
137 {
138 watch->link = db_free_watchpoints;
139 db_free_watchpoints = watch;
140 }
141
142 void
143 db_set_watchpoint(
144 task_t task,
145 db_addr_t addr,
146 vm_size_t size)
147 {
148 register db_watchpoint_t watch;
149
150 /*
151 * Should we do anything fancy with overlapping regions?
152 */
153
154 for (watch = db_watchpoint_list; watch != 0; watch = watch->link) {
155 if (watch->task == task &&
156 (watch->loaddr == addr) &&
157 (watch->hiaddr == addr+size)) {
158 db_printf("Already set.\n");
159 return;
160 }
161 }
162
163 watch = db_watchpoint_alloc();
164 if (watch == 0) {
165 db_printf("Too many watchpoints.\n");
166 return;
167 }
168
169 watch->task = task;
170 watch->loaddr = addr;
171 watch->hiaddr = addr+size;
172
173 watch->link = db_watchpoint_list;
174 db_watchpoint_list = watch;
175
176 db_watchpoints_inserted = FALSE;
177 }
178
179 void
180 db_delete_watchpoint(
181 task_t task,
182 db_addr_t addr)
183 {
184 register db_watchpoint_t watch;
185 register db_watchpoint_t *prev;
186
187 for (prev = &db_watchpoint_list; (watch = *prev) != 0;
188 prev = &watch->link) {
189 if (watch->task == task &&
190 (watch->loaddr <= addr) &&
191 (addr < watch->hiaddr)) {
192 *prev = watch->link;
193 db_watchpoint_free(watch);
194 return;
195 }
196 }
197
198 db_printf("Not set.\n");
199 }
200
201 void
202 db_list_watchpoints(void)
203 {
204 register db_watchpoint_t watch;
205 int task_id;
206
207 if (db_watchpoint_list == 0) {
208 db_printf("No watchpoints set\n");
209 return;
210 }
211
212 db_printf("Space Address Size\n");
213 for (watch = db_watchpoint_list; watch != 0; watch = watch->link) {
214 if (watch->task == TASK_NULL)
215 db_printf("kernel ");
216 else {
217 task_id = db_lookup_task(watch->task);
218 if (task_id < 0)
219 db_printf("%*X", 2*sizeof(vm_offset_t), watch->task);
220 else
221 db_printf("task%-3d ", task_id);
222 }
223 db_printf(" %*X %X\n", 2*sizeof(vm_offset_t), watch->loaddr,
224 watch->hiaddr - watch->loaddr);
225 }
226 }
227
228 static int
229 db_get_task(
230 char *modif,
231 task_t *taskp,
232 db_addr_t addr)
233 {
234 task_t task = TASK_NULL;
235 db_expr_t value;
236 boolean_t user_space;
237
238 user_space = db_option(modif, 'T');
239 if (user_space) {
240 if (db_expression(&value)) {
241 task = (task_t)value;
242 if (db_lookup_task(task) < 0) {
243 db_printf("bad task address %X\n", task);
244 return(-1);
245 }
246 } else {
247 task = db_default_task;
248 if (task == TASK_NULL) {
249 if ((task = db_current_task()) == TASK_NULL) {
250 db_printf("no task\n");
251 return(-1);
252 }
253 }
254 }
255 }
256 if (!DB_VALID_ADDRESS(addr, user_space)) {
257 db_printf("Address %#X is not in %s space\n", addr,
258 (user_space)? "user": "kernel");
259 return(-1);
260 }
261 *taskp = task;
262 return(0);
263 }
264
265 /* Delete watchpoint */
266 void
267 db_deletewatch_cmd(
268 db_expr_t addr,
269 int have_addr,
270 db_expr_t count,
271 char * modif)
272 {
273 task_t task;
274
275 if (db_get_task(modif, &task, addr) < 0)
276 return;
277 db_delete_watchpoint(task, addr);
278 }
279
280 /* Set watchpoint */
281 void
282 db_watchpoint_cmd(
283 db_expr_t addr,
284 int have_addr,
285 db_expr_t count,
286 char * modif)
287 {
288 vm_size_t size;
289 db_expr_t value;
290 task_t task;
291
292 if (db_get_task(modif, &task, addr) < 0)
293 return;
294 if (db_expression(&value))
295 size = (vm_size_t) value;
296 else
297 size = sizeof(int);
298 db_set_watchpoint(task, addr, size);
299 }
300
301 /* list watchpoints */
302 void
303 db_listwatch_cmd(void)
304 {
305 db_list_watchpoints();
306 }
307
308 void
309 db_set_watchpoints(void)
310 {
311 register db_watchpoint_t watch;
312 vm_map_t map;
313
314 if (!db_watchpoints_inserted) {
315 for (watch = db_watchpoint_list; watch != 0; watch = watch->link) {
316 map = (watch->task)? watch->task->map: kernel_map;
317 pmap_protect(map->pmap,
318 vm_map_trunc_page(watch->loaddr),
319 vm_map_round_page(watch->hiaddr),
320 VM_PROT_READ);
321 }
322 db_watchpoints_inserted = TRUE;
323 }
324 }
325
326 void
327 db_clear_watchpoints(void)
328 {
329 db_watchpoints_inserted = FALSE;
330 }
331
332 boolean_t
333 db_find_watchpoint(
334 vm_map_t map,
335 db_addr_t addr,
336 db_regs_t *regs)
337 {
338 register db_watchpoint_t watch;
339 db_watchpoint_t found = 0;
340 register task_t task_space;
341
342 task_space = (vm_map_pmap(map) == kernel_pmap)?
343 TASK_NULL: db_current_space();
344 for (watch = db_watchpoint_list; watch != 0; watch = watch->link) {
345 if (watch->task == task_space) {
346 if ((watch->loaddr <= addr) && (addr < watch->hiaddr))
347 return (TRUE);
348 else if ((trunc_page(watch->loaddr) <= addr) &&
349 (addr < round_page(watch->hiaddr)))
350 found = watch;
351 }
352 }
353
354 /*
355 * We didn't hit exactly on a watchpoint, but we are
356 * in a protected region. We want to single-step
357 * and then re-protect.
358 */
359
360 if (found) {
361 db_watchpoints_inserted = FALSE;
362 db_single_step(regs, task_space);
363 }
364
365 return (FALSE);
366 }