FreeBSD/Linux Kernel Cross Reference
sys/ddb/db_run.c
1 /*-
2 * Mach Operating System
3 * Copyright (c) 1991,1990 Carnegie Mellon University
4 * All Rights Reserved.
5 *
6 * Permission to use, copy, modify and distribute this software and its
7 * documentation is hereby granted, provided that both the copyright
8 * notice and this permission notice appear in all copies of the
9 * software, derivative works or modified versions, and any portions
10 * thereof, and that both notices appear in supporting documentation.
11 *
12 * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS
13 * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
14 * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
15 *
16 * Carnegie Mellon requests users of this software to return to
17 *
18 * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
19 * School of Computer Science
20 * Carnegie Mellon University
21 * Pittsburgh PA 15213-3890
22 *
23 * any improvements or extensions that they make and grant Carnegie the
24 * rights to redistribute these changes.
25 */
26 /*
27 * Author: David B. Golub, Carnegie Mellon University
28 * Date: 7/90
29 */
30
31 /*
32 * Commands to run process.
33 */
34
35 #include <sys/cdefs.h>
36 __FBSDID("$FreeBSD: releng/5.4/sys/ddb/db_run.c 141016 2005-01-30 01:00:13Z imp $");
37
38 #include <sys/param.h>
39 #include <sys/kdb.h>
40
41 #include <machine/kdb.h>
42 #include <machine/pcb.h>
43
44 #include <vm/vm.h>
45
46 #include <ddb/ddb.h>
47 #include <ddb/db_break.h>
48 #include <ddb/db_access.h>
49
50 static int db_run_mode;
51 #define STEP_NONE 0
52 #define STEP_ONCE 1
53 #define STEP_RETURN 2
54 #define STEP_CALLT 3
55 #define STEP_CONTINUE 4
56 #define STEP_INVISIBLE 5
57 #define STEP_COUNT 6
58
59 static boolean_t db_sstep_print;
60 static int db_loop_count;
61 static int db_call_depth;
62
63 int db_inst_count;
64 int db_load_count;
65 int db_store_count;
66
67 #ifndef db_set_single_step
68 void db_set_single_step(void);
69 #endif
70 #ifndef db_clear_single_step
71 void db_clear_single_step(void);
72 #endif
73
74 boolean_t
75 db_stop_at_pc(is_breakpoint)
76 boolean_t *is_breakpoint;
77 {
78 register db_addr_t pc;
79 register db_breakpoint_t bkpt;
80
81 db_clear_single_step();
82 db_clear_breakpoints();
83 db_clear_watchpoints();
84 pc = PC_REGS();
85
86 #ifdef FIXUP_PC_AFTER_BREAK
87 if (*is_breakpoint) {
88 /*
89 * Breakpoint trap. Fix up the PC if the
90 * machine requires it.
91 */
92 FIXUP_PC_AFTER_BREAK
93 pc = PC_REGS();
94 }
95 #endif
96
97 /*
98 * Now check for a breakpoint at this address.
99 */
100 bkpt = db_find_breakpoint_here(pc);
101 if (bkpt) {
102 if (--bkpt->count == 0) {
103 bkpt->count = bkpt->init_count;
104 *is_breakpoint = TRUE;
105 return (TRUE); /* stop here */
106 }
107 } else if (*is_breakpoint) {
108 #ifdef BKPT_SKIP
109 BKPT_SKIP;
110 #endif
111 }
112
113 *is_breakpoint = FALSE;
114
115 if (db_run_mode == STEP_INVISIBLE) {
116 db_run_mode = STEP_CONTINUE;
117 return (FALSE); /* continue */
118 }
119 if (db_run_mode == STEP_COUNT) {
120 return (FALSE); /* continue */
121 }
122 if (db_run_mode == STEP_ONCE) {
123 if (--db_loop_count > 0) {
124 if (db_sstep_print) {
125 db_printf("\t\t");
126 db_print_loc_and_inst(pc);
127 db_printf("\n");
128 }
129 return (FALSE); /* continue */
130 }
131 }
132 if (db_run_mode == STEP_RETURN) {
133 /* continue until matching return */
134 db_expr_t ins;
135
136 ins = db_get_value(pc, sizeof(int), FALSE);
137 if (!inst_trap_return(ins) &&
138 (!inst_return(ins) || --db_call_depth != 0)) {
139 if (db_sstep_print) {
140 if (inst_call(ins) || inst_return(ins)) {
141 register int i;
142
143 db_printf("[after %6d] ", db_inst_count);
144 for (i = db_call_depth; --i > 0; )
145 db_printf(" ");
146 db_print_loc_and_inst(pc);
147 db_printf("\n");
148 }
149 }
150 if (inst_call(ins))
151 db_call_depth++;
152 return (FALSE); /* continue */
153 }
154 }
155 if (db_run_mode == STEP_CALLT) {
156 /* continue until call or return */
157 db_expr_t ins;
158
159 ins = db_get_value(pc, sizeof(int), FALSE);
160 if (!inst_call(ins) &&
161 !inst_return(ins) &&
162 !inst_trap_return(ins)) {
163 return (FALSE); /* continue */
164 }
165 }
166 db_run_mode = STEP_NONE;
167 return (TRUE);
168 }
169
170 void
171 db_restart_at_pc(watchpt)
172 boolean_t watchpt;
173 {
174 register db_addr_t pc = PC_REGS();
175
176 if ((db_run_mode == STEP_COUNT) ||
177 (db_run_mode == STEP_RETURN) ||
178 (db_run_mode == STEP_CALLT)) {
179 db_expr_t ins;
180
181 /*
182 * We are about to execute this instruction,
183 * so count it now.
184 */
185
186 ins = db_get_value(pc, sizeof(int), FALSE);
187 db_inst_count++;
188 db_load_count += inst_load(ins);
189 db_store_count += inst_store(ins);
190 #ifdef SOFTWARE_SSTEP
191 /* XXX works on mips, but... */
192 if (inst_branch(ins) || inst_call(ins)) {
193 ins = db_get_value(next_instr_address(pc,1),
194 sizeof(int), FALSE);
195 db_inst_count++;
196 db_load_count += inst_load(ins);
197 db_store_count += inst_store(ins);
198 }
199 #endif /* SOFTWARE_SSTEP */
200 }
201
202 if (db_run_mode == STEP_CONTINUE) {
203 if (watchpt || db_find_breakpoint_here(pc)) {
204 /*
205 * Step over breakpoint/watchpoint.
206 */
207 db_run_mode = STEP_INVISIBLE;
208 db_set_single_step();
209 } else {
210 db_set_breakpoints();
211 db_set_watchpoints();
212 }
213 } else {
214 db_set_single_step();
215 }
216 }
217
218 #ifdef SOFTWARE_SSTEP
219 /*
220 * Software implementation of single-stepping.
221 * If your machine does not have a trace mode
222 * similar to the vax or sun ones you can use
223 * this implementation, done for the mips.
224 * Just define the above conditional and provide
225 * the functions/macros defined below.
226 *
227 * extern boolean_t
228 * inst_branch(), returns true if the instruction might branch
229 * extern unsigned
230 * branch_taken(), return the address the instruction might
231 * branch to
232 * db_getreg_val(); return the value of a user register,
233 * as indicated in the hardware instruction
234 * encoding, e.g. 8 for r8
235 *
236 * next_instr_address(pc,bd) returns the address of the first
237 * instruction following the one at "pc",
238 * which is either in the taken path of
239 * the branch (bd==1) or not. This is
240 * for machines (mips) with branch delays.
241 *
242 * A single-step may involve at most 2 breakpoints -
243 * one for branch-not-taken and one for branch taken.
244 * If one of these addresses does not already have a breakpoint,
245 * we allocate a breakpoint and save it here.
246 * These breakpoints are deleted on return.
247 */
248 db_breakpoint_t db_not_taken_bkpt = 0;
249 db_breakpoint_t db_taken_bkpt = 0;
250
251 void
252 db_set_single_step(void)
253 {
254 db_addr_t pc = PC_REGS(), brpc;
255 unsigned inst;
256
257 /*
258 * User was stopped at pc, e.g. the instruction
259 * at pc was not executed.
260 */
261 inst = db_get_value(pc, sizeof(int), FALSE);
262 if (inst_branch(inst) || inst_call(inst)) {
263 brpc = branch_taken(inst, pc);
264 if (brpc != pc) { /* self-branches are hopeless */
265 db_taken_bkpt = db_set_temp_breakpoint(brpc);
266 }
267 pc = next_instr_address(pc, 1);
268 }
269 pc = next_instr_address(pc, 0);
270 db_not_taken_bkpt = db_set_temp_breakpoint(pc);
271 }
272
273 void
274 db_clear_single_step(void)
275 {
276
277 if (db_not_taken_bkpt != 0) {
278 db_delete_temp_breakpoint(db_not_taken_bkpt);
279 db_not_taken_bkpt = 0;
280 }
281 if (db_taken_bkpt != 0) {
282 db_delete_temp_breakpoint(db_taken_bkpt);
283 db_taken_bkpt = 0;
284 }
285 }
286
287 #endif /* SOFTWARE_SSTEP */
288
289 extern int db_cmd_loop_done;
290
291 /* single-step */
292 /*ARGSUSED*/
293 void
294 db_single_step_cmd(addr, have_addr, count, modif)
295 db_expr_t addr;
296 boolean_t have_addr;
297 db_expr_t count;
298 char * modif;
299 {
300 boolean_t print = FALSE;
301
302 if (count == -1)
303 count = 1;
304
305 if (modif[0] == 'p')
306 print = TRUE;
307
308 db_run_mode = STEP_ONCE;
309 db_loop_count = count;
310 db_sstep_print = print;
311 db_inst_count = 0;
312 db_load_count = 0;
313 db_store_count = 0;
314
315 db_cmd_loop_done = 1;
316 }
317
318 /* trace and print until call/return */
319 /*ARGSUSED*/
320 void
321 db_trace_until_call_cmd(addr, have_addr, count, modif)
322 db_expr_t addr;
323 boolean_t have_addr;
324 db_expr_t count;
325 char * modif;
326 {
327 boolean_t print = FALSE;
328
329 if (modif[0] == 'p')
330 print = TRUE;
331
332 db_run_mode = STEP_CALLT;
333 db_sstep_print = print;
334 db_inst_count = 0;
335 db_load_count = 0;
336 db_store_count = 0;
337
338 db_cmd_loop_done = 1;
339 }
340
341 /*ARGSUSED*/
342 void
343 db_trace_until_matching_cmd(addr, have_addr, count, modif)
344 db_expr_t addr;
345 boolean_t have_addr;
346 db_expr_t count;
347 char * modif;
348 {
349 boolean_t print = FALSE;
350
351 if (modif[0] == 'p')
352 print = TRUE;
353
354 db_run_mode = STEP_RETURN;
355 db_call_depth = 1;
356 db_sstep_print = print;
357 db_inst_count = 0;
358 db_load_count = 0;
359 db_store_count = 0;
360
361 db_cmd_loop_done = 1;
362 }
363
364 /* continue */
365 /*ARGSUSED*/
366 void
367 db_continue_cmd(addr, have_addr, count, modif)
368 db_expr_t addr;
369 boolean_t have_addr;
370 db_expr_t count;
371 char * modif;
372 {
373 if (modif[0] == 'c')
374 db_run_mode = STEP_COUNT;
375 else
376 db_run_mode = STEP_CONTINUE;
377 db_inst_count = 0;
378 db_load_count = 0;
379 db_store_count = 0;
380
381 db_cmd_loop_done = 1;
382 }
Cache object: 1d7ecfdd44a71b0fd879e9d3fcfaefc2
|