1 /* $OpenBSD: db_break.c,v 1.12 2002/05/16 13:01:41 art Exp $ */
2 /* $NetBSD: db_break.c,v 1.7 1996/03/30 22:30:03 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: David B. Golub, Carnegie Mellon University
30 * Date: 7/90
31 */
32
33 /*
34 * Breakpoints.
35 */
36 #include <sys/param.h>
37 #include <sys/proc.h>
38
39 #include <uvm/uvm_extern.h>
40
41 #include <machine/db_machdep.h> /* type definitions */
42
43 #include <ddb/db_lex.h>
44 #include <ddb/db_access.h>
45 #include <ddb/db_sym.h>
46 #include <ddb/db_break.h>
47 #include <ddb/db_output.h>
48
49 #define NBREAKPOINTS 100
50 struct db_breakpoint db_break_table[NBREAKPOINTS];
51 db_breakpoint_t db_next_free_breakpoint = &db_break_table[0];
52 db_breakpoint_t db_free_breakpoints = 0;
53 db_breakpoint_t db_breakpoint_list = 0;
54
55 db_breakpoint_t
db_breakpoint_alloc()56 db_breakpoint_alloc()
57 {
58 register db_breakpoint_t bkpt;
59
60 if ((bkpt = db_free_breakpoints) != 0) {
61 db_free_breakpoints = bkpt->link;
62 return (bkpt);
63 }
64 if (db_next_free_breakpoint == &db_break_table[NBREAKPOINTS]) {
65 db_printf("All breakpoints used.\n");
66 return (0);
67 }
68 bkpt = db_next_free_breakpoint;
69 db_next_free_breakpoint++;
70
71 return (bkpt);
72 }
73
74 void
db_breakpoint_free(bkpt)75 db_breakpoint_free(bkpt)
76 register db_breakpoint_t bkpt;
77 {
78 bkpt->link = db_free_breakpoints;
79 db_free_breakpoints = bkpt;
80 }
81
82 void
db_set_breakpoint(map,addr,count)83 db_set_breakpoint(map, addr, count)
84 struct vm_map *map;
85 db_addr_t addr;
86 int count;
87 {
88 register db_breakpoint_t bkpt;
89
90 if (db_find_breakpoint(map, addr)) {
91 db_printf("Already set.\n");
92 return;
93 }
94
95 #ifdef DB_VALID_BREAKPOINT
96 if (!DB_VALID_BREAKPOINT(addr)) {
97 db_printf("Not a valid address for a breakpoint.\n");
98 return;
99 }
100 #endif
101
102 bkpt = db_breakpoint_alloc();
103 if (bkpt == 0) {
104 db_printf("Too many breakpoints.\n");
105 return;
106 }
107
108 bkpt->map = map;
109 bkpt->address = addr;
110 bkpt->flags = 0;
111 bkpt->init_count = count;
112 bkpt->count = count;
113
114 bkpt->link = db_breakpoint_list;
115 db_breakpoint_list = bkpt;
116 }
117
118 void
db_delete_breakpoint(map,addr)119 db_delete_breakpoint(map, addr)
120 struct vm_map *map;
121 db_addr_t addr;
122 {
123 register db_breakpoint_t bkpt;
124 register db_breakpoint_t *prev;
125
126 for (prev = &db_breakpoint_list;
127 (bkpt = *prev) != 0;
128 prev = &bkpt->link) {
129 if (db_map_equal(bkpt->map, map) &&
130 (bkpt->address == addr)) {
131 *prev = bkpt->link;
132 break;
133 }
134 }
135 if (bkpt == 0) {
136 db_printf("Not set.\n");
137 return;
138 }
139
140 db_breakpoint_free(bkpt);
141 }
142
143 db_breakpoint_t
db_find_breakpoint(map,addr)144 db_find_breakpoint(map, addr)
145 struct vm_map *map;
146 db_addr_t addr;
147 {
148 register db_breakpoint_t bkpt;
149
150 for (bkpt = db_breakpoint_list;
151 bkpt != 0;
152 bkpt = bkpt->link)
153 {
154 if (db_map_equal(bkpt->map, map) &&
155 (bkpt->address == addr))
156 return (bkpt);
157 }
158 return (0);
159 }
160
161 db_breakpoint_t
db_find_breakpoint_here(addr)162 db_find_breakpoint_here(addr)
163 db_addr_t addr;
164 {
165 return db_find_breakpoint(db_map_addr(addr), addr);
166 }
167
168 boolean_t db_breakpoints_inserted = TRUE;
169
170 void
db_set_breakpoints()171 db_set_breakpoints()
172 {
173 register db_breakpoint_t bkpt;
174
175 if (!db_breakpoints_inserted) {
176
177 for (bkpt = db_breakpoint_list;
178 bkpt != 0;
179 bkpt = bkpt->link)
180 if (db_map_current(bkpt->map)) {
181 bkpt->bkpt_inst = db_get_value(bkpt->address, BKPT_SIZE,
182 FALSE);
183 db_put_value(bkpt->address, BKPT_SIZE,
184 BKPT_SET(bkpt->bkpt_inst));
185 }
186 db_breakpoints_inserted = TRUE;
187 }
188 }
189
190 void
db_clear_breakpoints()191 db_clear_breakpoints()
192 {
193 register db_breakpoint_t bkpt;
194
195 if (db_breakpoints_inserted) {
196
197 for (bkpt = db_breakpoint_list;
198 bkpt != 0;
199 bkpt = bkpt->link)
200 if (db_map_current(bkpt->map)) {
201 db_put_value(bkpt->address, BKPT_SIZE, bkpt->bkpt_inst);
202 }
203 db_breakpoints_inserted = FALSE;
204 }
205 }
206
207 /*
208 * Set a temporary breakpoint.
209 * The instruction is changed immediately,
210 * so the breakpoint does not have to be on the breakpoint list.
211 */
212 db_breakpoint_t
db_set_temp_breakpoint(addr)213 db_set_temp_breakpoint(addr)
214 db_addr_t addr;
215 {
216 db_breakpoint_t bkpt;
217
218 #ifdef DB_VALID_BREAKPOINT
219 if (!DB_VALID_BREAKPOINT(addr)) {
220 db_printf("Not a valid address for a breakpoint.\n");
221 return (0);
222 }
223 #endif
224
225 bkpt = db_breakpoint_alloc();
226 if (bkpt == 0) {
227 db_printf("Too many breakpoints.\n");
228 return (0);
229 }
230
231 bkpt->map = NULL;
232 bkpt->address = addr;
233 bkpt->flags = BKPT_TEMP;
234 bkpt->init_count = 1;
235 bkpt->count = 1;
236
237 bkpt->bkpt_inst = db_get_value(bkpt->address, BKPT_SIZE, FALSE);
238 db_put_value(bkpt->address, BKPT_SIZE, BKPT_SET(bkpt->bkpt_inst));
239 return bkpt;
240 }
241
242 void
db_delete_temp_breakpoint(bkpt)243 db_delete_temp_breakpoint(bkpt)
244 db_breakpoint_t bkpt;
245 {
246 db_put_value(bkpt->address, BKPT_SIZE, bkpt->bkpt_inst);
247 db_breakpoint_free(bkpt);
248 }
249
250 /*
251 * List breakpoints.
252 */
253 void
db_list_breakpoints()254 db_list_breakpoints()
255 {
256 register db_breakpoint_t bkpt;
257
258 if (db_breakpoint_list == 0) {
259 db_printf("No breakpoints set\n");
260 return;
261 }
262
263 db_printf(" Map Count Address\n");
264 for (bkpt = db_breakpoint_list;
265 bkpt != 0;
266 bkpt = bkpt->link)
267 {
268 db_printf("%s%p %5d ",
269 db_map_current(bkpt->map) ? "*" : " ",
270 bkpt->map, bkpt->init_count);
271 db_printsym(bkpt->address, DB_STGY_PROC, db_printf);
272 db_printf("\n");
273 }
274 }
275
276 /* Delete breakpoint */
277 /*ARGSUSED*/
278 void
db_delete_cmd(addr,have_addr,count,modif)279 db_delete_cmd(addr, have_addr, count, modif)
280 db_expr_t addr;
281 int have_addr;
282 db_expr_t count;
283 char * modif;
284 {
285 db_delete_breakpoint(db_map_addr(addr), (db_addr_t)addr);
286 }
287
288 /* Set breakpoint with skip count */
289 /*ARGSUSED*/
290 void
db_breakpoint_cmd(addr,have_addr,count,modif)291 db_breakpoint_cmd(addr, have_addr, count, modif)
292 db_expr_t addr;
293 int have_addr;
294 db_expr_t count;
295 char * modif;
296 {
297 if (count == -1)
298 count = 1;
299
300 db_set_breakpoint(db_map_addr(addr), (db_addr_t)addr, count);
301 }
302
303 /* list breakpoints */
304 /*ARGSUSED*/
305 void
db_listbreak_cmd(addr,have_addr,count,modif)306 db_listbreak_cmd(addr, have_addr, count, modif)
307 db_expr_t addr;
308 int have_addr;
309 db_expr_t count;
310 char * modif;
311 {
312 db_list_breakpoints();
313 }
314
315 /*
316 * We want ddb to be usable before most of the kernel has been
317 * initialized. In particular, current_thread() or kernel_map
318 * (or both) may be null.
319 */
320
321 boolean_t
db_map_equal(map1,map2)322 db_map_equal(map1, map2)
323 struct vm_map *map1, *map2;
324 {
325 return ((map1 == map2) ||
326 ((map1 == NULL) && (map2 == kernel_map)) ||
327 ((map1 == kernel_map) && (map2 == NULL)));
328 }
329
330 boolean_t
db_map_current(map)331 db_map_current(map)
332 struct vm_map *map;
333 {
334 #if 0
335 thread_t thread;
336
337 return ((map == NULL) ||
338 (map == kernel_map) ||
339 (((thread = current_thread()) != NULL) &&
340 (map == thread->proc->map)));
341 #else
342 return (1);
343 #endif
344 }
345
346 struct vm_map *
db_map_addr(addr)347 db_map_addr(addr)
348 vaddr_t addr;
349 {
350 #if 0
351 thread_t thread;
352
353 /*
354 * We want to return kernel_map for all
355 * non-user addresses, even when debugging
356 * kernel tasks with their own maps.
357 */
358
359 if ((VM_MIN_ADDRESS <= addr) &&
360 (addr < VM_MAX_ADDRESS) &&
361 ((thread = current_thread()) != NULL))
362 return thread->proc->map;
363 else
364 #endif
365 return kernel_map;
366 }
367