]> git.saurik.com Git - apple/xnu.git/blob - osfmk/ddb/db_watch.c
xnu-1456.1.26.tar.gz
[apple/xnu.git] / osfmk / ddb / db_watch.c
1 /*
2 * Copyright (c) 2000-2005 Apple Computer, Inc. All rights reserved.
3 *
4 * @APPLE_OSREFERENCE_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. The rights granted to you under the License
10 * may not be used to create, or enable the creation or redistribution of,
11 * unlawful or unlicensed copies of an Apple operating system, or to
12 * circumvent, violate, or enable the circumvention or violation of, any
13 * terms of an Apple operating system software license agreement.
14 *
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
17 *
18 * The Original Code and all software distributed under the License are
19 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
22 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
23 * Please see the License for the specific language governing rights and
24 * limitations under the License.
25 *
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
27 */
28 /*
29 * @OSF_COPYRIGHT@
30 */
31 /*
32 * Mach Operating System
33 * Copyright (c) 1991,1990 Carnegie Mellon University
34 * All Rights Reserved.
35 *
36 * Permission to use, copy, modify and distribute this software and its
37 * documentation is hereby granted, provided that both the copyright
38 * notice and this permission notice appear in all copies of the
39 * software, derivative works or modified versions, and any portions
40 * thereof, and that both notices appear in supporting documentation.
41 *
42 * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
43 * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
44 * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
45 *
46 * Carnegie Mellon requests users of this software to return to
47 *
48 * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
49 * School of Computer Science
50 * Carnegie Mellon University
51 * Pittsburgh PA 15213-3890
52 *
53 * any improvements or extensions that they make and grant Carnegie Mellon
54 * the rights to redistribute these changes.
55 */
56 /*
57 */
58 /*
59 * Author: Richard P. Draves, Carnegie Mellon University
60 * Date: 10/90
61 */
62
63 #include <mach/boolean.h>
64 #include <mach/vm_param.h>
65 #include <mach/machine/vm_types.h>
66 #include <mach/machine/vm_param.h>
67 #include <vm/vm_map.h>
68
69 #include <machine/db_machdep.h>
70 #include <ddb/db_lex.h>
71 #include <ddb/db_watch.h>
72 #include <ddb/db_access.h>
73 #include <ddb/db_sym.h>
74 #include <ddb/db_task_thread.h>
75 #include <ddb/db_command.h>
76 #include <ddb/db_expr.h>
77 #include <ddb/db_output.h> /* For db_printf() */
78 #include <ddb/db_run.h> /* For db_single_step() */
79
80 /*
81 * Watchpoints.
82 */
83
84 boolean_t db_watchpoints_inserted = TRUE;
85
86 #define NWATCHPOINTS 100
87 struct db_watchpoint db_watch_table[NWATCHPOINTS];
88 db_watchpoint_t db_next_free_watchpoint = &db_watch_table[0];
89 db_watchpoint_t db_free_watchpoints = 0;
90 db_watchpoint_t db_watchpoint_list = 0;
91
92 extern vm_map_t kernel_map;
93
94
95
96 /* Prototypes for functions local to this file. XXX -- should be static.
97 */
98
99 db_watchpoint_t db_watchpoint_alloc(void);
100
101 void db_watchpoint_free(register db_watchpoint_t watch);
102
103 void db_set_watchpoint(
104 task_t task,
105 db_addr_t addr,
106 vm_size_t size);
107
108 void db_delete_watchpoint(
109 task_t task,
110 db_addr_t addr);
111
112 static int db_get_task(
113 char *modif,
114 task_t *taskp,
115 db_addr_t addr);
116
117 void db_list_watchpoints(void);
118
119
120
121 db_watchpoint_t
122 db_watchpoint_alloc(void)
123 {
124 register db_watchpoint_t watch;
125
126 if ((watch = db_free_watchpoints) != 0) {
127 db_free_watchpoints = watch->link;
128 return (watch);
129 }
130 if (db_next_free_watchpoint == &db_watch_table[NWATCHPOINTS]) {
131 db_printf("All watchpoints used.\n");
132 return (0);
133 }
134 watch = db_next_free_watchpoint;
135 db_next_free_watchpoint++;
136
137 return (watch);
138 }
139
140 void
141 db_watchpoint_free(register db_watchpoint_t watch)
142 {
143 watch->link = db_free_watchpoints;
144 db_free_watchpoints = watch;
145 }
146
147 void
148 db_set_watchpoint(
149 task_t task,
150 db_addr_t addr,
151 vm_size_t size)
152 {
153 register db_watchpoint_t watch;
154
155 /*
156 * Should we do anything fancy with overlapping regions?
157 */
158
159 for (watch = db_watchpoint_list; watch != 0; watch = watch->link) {
160 if (watch->task == task &&
161 (watch->loaddr == addr) &&
162 (watch->hiaddr == addr+size)) {
163 db_printf("Already set.\n");
164 return;
165 }
166 }
167
168 watch = db_watchpoint_alloc();
169 if (watch == 0) {
170 db_printf("Too many watchpoints.\n");
171 return;
172 }
173
174 watch->task = task;
175 watch->loaddr = addr;
176 watch->hiaddr = addr+size;
177
178 watch->link = db_watchpoint_list;
179 db_watchpoint_list = watch;
180
181 db_watchpoints_inserted = FALSE;
182 }
183
184 void
185 db_delete_watchpoint(
186 task_t task,
187 db_addr_t addr)
188 {
189 register db_watchpoint_t watch;
190 register db_watchpoint_t *prev;
191
192 for (prev = &db_watchpoint_list; (watch = *prev) != 0;
193 prev = &watch->link) {
194 if (watch->task == task &&
195 (watch->loaddr <= addr) &&
196 (addr < watch->hiaddr)) {
197 *prev = watch->link;
198 db_watchpoint_free(watch);
199 return;
200 }
201 }
202
203 db_printf("Not set.\n");
204 }
205
206 void
207 db_list_watchpoints(void)
208 {
209 register db_watchpoint_t watch;
210 int task_id;
211
212 if (db_watchpoint_list == 0) {
213 db_printf("No watchpoints set\n");
214 return;
215 }
216
217 db_printf("Space Address Size\n");
218 for (watch = db_watchpoint_list; watch != 0; watch = watch->link) {
219 if (watch->task == TASK_NULL)
220 db_printf("kernel ");
221 else {
222 task_id = db_lookup_task(watch->task);
223 if (task_id < 0)
224 db_printf("%*X", 2*sizeof(vm_offset_t), watch->task);
225 else
226 db_printf("task%-3d ", task_id);
227 }
228 db_printf(" %*X %X\n", 2*sizeof(vm_offset_t), watch->loaddr,
229 watch->hiaddr - watch->loaddr);
230 }
231 }
232
233 static int
234 db_get_task(
235 char *modif,
236 task_t *taskp,
237 db_addr_t addr)
238 {
239 task_t task = TASK_NULL;
240 db_expr_t value;
241 boolean_t user_space;
242
243 user_space = db_option(modif, 'T');
244 if (user_space) {
245 if (db_expression(&value)) {
246 task = (task_t)(unsigned long)value;
247 if (db_lookup_task(task) < 0) {
248 db_printf("bad task address %X\n", task);
249 return(-1);
250 }
251 } else {
252 task = db_default_task;
253 if (task == TASK_NULL) {
254 if ((task = db_current_task()) == TASK_NULL) {
255 db_printf("no task\n");
256 return(-1);
257 }
258 }
259 }
260 }
261 if (!DB_VALID_ADDRESS(addr, user_space)) {
262 db_printf("Address %#X is not in %s space\n", addr,
263 (user_space)? "user": "kernel");
264 return(-1);
265 }
266 *taskp = task;
267 return(0);
268 }
269
270 /* Delete watchpoint */
271 void
272 db_deletewatch_cmd(db_expr_t addr, __unused boolean_t have_addr,
273 __unused db_expr_t count, char *modif)
274 {
275 task_t task;
276
277 if (db_get_task(modif, &task, addr) < 0)
278 return;
279 db_delete_watchpoint(task, addr);
280 }
281
282 /* Set watchpoint */
283 void
284 db_watchpoint_cmd(db_expr_t addr, __unused boolean_t have_addr,
285 __unused db_expr_t count, 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(__unused db_expr_t addr, __unused boolean_t have_addr,
303 __unused db_expr_t count, __unused char *modif)
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 }