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