1 | /* $NetBSD: db_break.c,v 1.26 2007/02/22 06:41:00 thorpej Exp $ */ |
2 | |
3 | /* |
4 | * Mach Operating System |
5 | * Copyright (c) 1991,1990 Carnegie Mellon University |
6 | * All Rights Reserved. |
7 | * |
8 | * Permission to use, copy, modify and distribute this software and its |
9 | * documentation is hereby granted, provided that both the copyright |
10 | * notice and this permission notice appear in all copies of the |
11 | * software, derivative works or modified versions, and any portions |
12 | * thereof, and that both notices appear in supporting documentation. |
13 | * |
14 | * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS" |
15 | * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR |
16 | * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. |
17 | * |
18 | * Carnegie Mellon requests users of this software to return to |
19 | * |
20 | * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU |
21 | * School of Computer Science |
22 | * Carnegie Mellon University |
23 | * Pittsburgh PA 15213-3890 |
24 | * |
25 | * any improvements or extensions that they make and grant Carnegie the |
26 | * rights to redistribute these changes. |
27 | * |
28 | * Author: David B. Golub, Carnegie Mellon University |
29 | * Date: 7/90 |
30 | */ |
31 | |
32 | /* |
33 | * Breakpoints. |
34 | */ |
35 | |
36 | #include <sys/cdefs.h> |
37 | __KERNEL_RCSID(0, "$NetBSD: db_break.c,v 1.26 2007/02/22 06:41:00 thorpej Exp $" ); |
38 | |
39 | #include <sys/param.h> |
40 | #include <sys/proc.h> |
41 | |
42 | #include <machine/db_machdep.h> /* type definitions */ |
43 | |
44 | #include <ddb/db_lex.h> |
45 | #include <ddb/db_access.h> |
46 | #include <ddb/db_sym.h> |
47 | #include <ddb/db_break.h> |
48 | #include <ddb/db_output.h> |
49 | |
50 | #define NBREAKPOINTS 100 |
51 | static struct db_breakpoint db_break_table[NBREAKPOINTS]; |
52 | static db_breakpoint_t db_next_free_breakpoint = &db_break_table[0]; |
53 | static db_breakpoint_t db_free_breakpoints = 0; |
54 | static db_breakpoint_t db_breakpoint_list = 0; |
55 | |
56 | static db_breakpoint_t db_breakpoint_alloc(void); |
57 | static void db_breakpoint_free(db_breakpoint_t); |
58 | static void db_delete_breakpoint(struct vm_map *, db_addr_t); |
59 | static db_breakpoint_t db_find_breakpoint(struct vm_map *, db_addr_t); |
60 | static void db_list_breakpoints(void); |
61 | static void db_set_breakpoint(struct vm_map *, db_addr_t, int); |
62 | |
63 | static db_breakpoint_t |
64 | db_breakpoint_alloc(void) |
65 | { |
66 | db_breakpoint_t bkpt; |
67 | |
68 | if ((bkpt = db_free_breakpoints) != 0) { |
69 | db_free_breakpoints = bkpt->link; |
70 | return (bkpt); |
71 | } |
72 | if (db_next_free_breakpoint == &db_break_table[NBREAKPOINTS]) { |
73 | db_printf("All breakpoints used.\n" ); |
74 | return (0); |
75 | } |
76 | bkpt = db_next_free_breakpoint; |
77 | db_next_free_breakpoint++; |
78 | |
79 | return (bkpt); |
80 | } |
81 | |
82 | static void |
83 | db_breakpoint_free(db_breakpoint_t bkpt) |
84 | { |
85 | bkpt->link = db_free_breakpoints; |
86 | db_free_breakpoints = bkpt; |
87 | } |
88 | |
89 | void |
90 | db_set_breakpoint(struct vm_map *map, db_addr_t addr, int count) |
91 | { |
92 | db_breakpoint_t bkpt; |
93 | |
94 | if (db_find_breakpoint(map, addr)) { |
95 | db_printf("Already set.\n" ); |
96 | return; |
97 | } |
98 | |
99 | bkpt = db_breakpoint_alloc(); |
100 | if (bkpt == 0) { |
101 | db_printf("Too many breakpoints.\n" ); |
102 | return; |
103 | } |
104 | |
105 | bkpt->map = map; |
106 | bkpt->address = BKPT_ADDR(addr); |
107 | bkpt->flags = 0; |
108 | bkpt->init_count = count; |
109 | bkpt->count = count; |
110 | |
111 | bkpt->link = db_breakpoint_list; |
112 | db_breakpoint_list = bkpt; |
113 | } |
114 | |
115 | static void |
116 | db_delete_breakpoint(struct vm_map *map, db_addr_t addr) |
117 | { |
118 | db_breakpoint_t bkpt; |
119 | db_breakpoint_t *prev; |
120 | |
121 | for (prev = &db_breakpoint_list; |
122 | (bkpt = *prev) != 0; |
123 | prev = &bkpt->link) { |
124 | if (db_map_equal(bkpt->map, map) && |
125 | (bkpt->address == BKPT_ADDR(addr))) { |
126 | *prev = bkpt->link; |
127 | break; |
128 | } |
129 | } |
130 | if (bkpt == 0) { |
131 | db_printf("Not set.\n" ); |
132 | return; |
133 | } |
134 | |
135 | db_breakpoint_free(bkpt); |
136 | } |
137 | |
138 | db_breakpoint_t |
139 | db_find_breakpoint(struct vm_map *map, db_addr_t addr) |
140 | { |
141 | db_breakpoint_t bkpt; |
142 | |
143 | for (bkpt = db_breakpoint_list; |
144 | bkpt != 0; |
145 | bkpt = bkpt->link) |
146 | if (db_map_equal(bkpt->map, map) && |
147 | (bkpt->address == BKPT_ADDR(addr))) |
148 | return (bkpt); |
149 | |
150 | return (0); |
151 | } |
152 | |
153 | db_breakpoint_t |
154 | db_find_breakpoint_here(db_addr_t addr) |
155 | { |
156 | return db_find_breakpoint(db_map_addr(addr), addr); |
157 | } |
158 | |
159 | static bool db_breakpoints_inserted = true; |
160 | |
161 | void |
162 | db_set_breakpoints(void) |
163 | { |
164 | db_breakpoint_t bkpt; |
165 | |
166 | if (!db_breakpoints_inserted) { |
167 | |
168 | for (bkpt = db_breakpoint_list; |
169 | bkpt != 0; |
170 | bkpt = bkpt->link) |
171 | if (db_map_current(bkpt->map)) { |
172 | bkpt->bkpt_inst = db_get_value(bkpt->address, |
173 | BKPT_SIZE, false); |
174 | db_put_value(bkpt->address, |
175 | BKPT_SIZE, |
176 | BKPT_SET(bkpt->bkpt_inst, bkpt->address)); |
177 | } |
178 | db_breakpoints_inserted = true; |
179 | } |
180 | } |
181 | |
182 | void |
183 | db_clear_breakpoints(void) |
184 | { |
185 | db_breakpoint_t bkpt; |
186 | |
187 | if (db_breakpoints_inserted) { |
188 | |
189 | for (bkpt = db_breakpoint_list; |
190 | bkpt != 0; |
191 | bkpt = bkpt->link) |
192 | if (db_map_current(bkpt->map)) |
193 | db_put_value(bkpt->address, BKPT_SIZE, |
194 | bkpt->bkpt_inst); |
195 | db_breakpoints_inserted = false; |
196 | } |
197 | } |
198 | |
199 | /* |
200 | * List breakpoints. |
201 | */ |
202 | void |
203 | db_list_breakpoints(void) |
204 | { |
205 | db_breakpoint_t bkpt; |
206 | |
207 | if (db_breakpoint_list == 0) { |
208 | db_printf("No breakpoints set\n" ); |
209 | return; |
210 | } |
211 | |
212 | db_printf(" Map Count Address\n" ); |
213 | for (bkpt = db_breakpoint_list; |
214 | bkpt != 0; |
215 | bkpt = bkpt->link) { |
216 | db_printf("%s%p %5d " , |
217 | db_map_current(bkpt->map) ? "*" : " " , |
218 | bkpt->map, bkpt->init_count); |
219 | db_printsym(bkpt->address, DB_STGY_PROC, db_printf); |
220 | db_printf("\n" ); |
221 | } |
222 | } |
223 | |
224 | /* Delete breakpoint */ |
225 | /*ARGSUSED*/ |
226 | void |
227 | db_delete_cmd(db_expr_t addr, bool have_addr, db_expr_t count, |
228 | const char *modif) |
229 | { |
230 | |
231 | db_delete_breakpoint(db_map_addr(addr), (db_addr_t)addr); |
232 | } |
233 | |
234 | /* Set breakpoint with skip count */ |
235 | /*ARGSUSED*/ |
236 | void |
237 | db_breakpoint_cmd(db_expr_t addr, bool have_addr, db_expr_t count, |
238 | const char *modif) |
239 | { |
240 | |
241 | if (count == -1) |
242 | count = 1; |
243 | |
244 | db_set_breakpoint(db_map_addr(addr), (db_addr_t)addr, count); |
245 | } |
246 | |
247 | /* list breakpoints */ |
248 | /*ARGSUSED*/ |
249 | void |
250 | db_listbreak_cmd(db_expr_t addr, bool have_addr, |
251 | db_expr_t count, const char *modif) |
252 | { |
253 | |
254 | db_list_breakpoints(); |
255 | } |
256 | |
257 | #include <uvm/uvm_extern.h> |
258 | |
259 | /* |
260 | * We want ddb to be usable before most of the kernel has been |
261 | * initialized. In particular, current_thread() or kernel_map |
262 | * (or both) may be null. |
263 | */ |
264 | |
265 | bool |
266 | db_map_equal(struct vm_map *map1, struct vm_map *map2) |
267 | { |
268 | |
269 | return ((map1 == map2) || |
270 | ((map1 == NULL) && (map2 == kernel_map)) || |
271 | ((map1 == kernel_map) && (map2 == NULL))); |
272 | } |
273 | |
274 | bool |
275 | db_map_current(struct vm_map *map) |
276 | { |
277 | #if 0 |
278 | thread_t thread; |
279 | |
280 | return ((map == NULL) || |
281 | (map == kernel_map) || |
282 | (((thread = current_thread()) != NULL) && |
283 | (map == thread->task->map))); |
284 | #else |
285 | |
286 | return (1); |
287 | #endif |
288 | } |
289 | |
290 | struct vm_map * |
291 | db_map_addr(vaddr_t addr) |
292 | { |
293 | #if 0 |
294 | thread_t thread; |
295 | |
296 | /* |
297 | * We want to return kernel_map for all |
298 | * non-user addresses, even when debugging |
299 | * kernel tasks with their own maps. |
300 | */ |
301 | |
302 | if ((VM_MIN_ADDRESS <= addr) && (addr < VM_MAX_ADDRESS) && |
303 | ((thread = current_thread()) != NULL)) |
304 | return thread->task->map; |
305 | else |
306 | #endif |
307 | return kernel_map; |
308 | } |
309 | |