db_run.c revision 1.12 1 /* $NetBSD: db_run.c,v 1.12 1997/09/10 19:37:31 pk Exp $ */
2
3 /*
4 * Mach Operating System
5 * Copyright (c) 1993-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
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 (at) 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 * Commands to run process.
34 */
35 #include <sys/param.h>
36 #include <sys/proc.h>
37
38 #include <machine/db_machdep.h>
39
40 #include <ddb/db_run.h>
41 #include <ddb/db_lex.h>
42 #include <ddb/db_break.h>
43 #include <ddb/db_access.h>
44 #include <ddb/db_watch.h>
45 #include <ddb/db_output.h>
46 #include <ddb/db_sym.h>
47 #include <ddb/db_extern.h>
48
49 int db_run_mode;
50 #define STEP_NONE 0
51 #define STEP_ONCE 1
52 #define STEP_RETURN 2
53 #define STEP_CALLT 3
54 #define STEP_CONTINUE 4
55 #define STEP_INVISIBLE 5
56 #define STEP_COUNT 6
57
58 boolean_t db_sstep_print;
59 int db_loop_count;
60 int db_call_depth;
61
62 boolean_t
63 db_stop_at_pc(regs, is_breakpoint)
64 db_regs_t *regs;
65 boolean_t *is_breakpoint;
66 {
67 register db_addr_t pc;
68 register db_breakpoint_t bkpt;
69
70 db_clear_single_step(regs);
71 db_clear_breakpoints();
72 db_clear_watchpoints();
73 pc = PC_REGS(regs);
74
75 #ifdef FIXUP_PC_AFTER_BREAK
76 if (*is_breakpoint) {
77 /*
78 * Breakpoint trap. Fix up the PC if the
79 * machine requires it.
80 */
81 FIXUP_PC_AFTER_BREAK(regs);
82 pc = PC_REGS(regs);
83 }
84 #endif
85
86 /*
87 * Now check for a breakpoint at this address.
88 */
89 bkpt = db_find_breakpoint_here(pc);
90 if (bkpt) {
91 if (--bkpt->count == 0) {
92 bkpt->count = bkpt->init_count;
93 *is_breakpoint = TRUE;
94 return (TRUE); /* stop here */
95 }
96 } else if (*is_breakpoint) {
97 #ifdef PC_ADVANCE
98 PC_ADVANCE(regs);
99 #else
100 PC_REGS(regs) += BKPT_SIZE;
101 #endif
102 }
103
104 *is_breakpoint = FALSE;
105
106 if (db_run_mode == STEP_INVISIBLE) {
107 db_run_mode = STEP_CONTINUE;
108 return (FALSE); /* continue */
109 }
110 if (db_run_mode == STEP_COUNT) {
111 return (FALSE); /* continue */
112 }
113 if (db_run_mode == STEP_ONCE) {
114 if (--db_loop_count > 0) {
115 if (db_sstep_print) {
116 db_printf("\t\t");
117 db_print_loc_and_inst(pc);
118 db_printf("\n");
119 }
120 return (FALSE); /* continue */
121 }
122 }
123 if (db_run_mode == STEP_RETURN) {
124 db_expr_t ins = db_get_value(pc, sizeof(int), FALSE);
125
126 /* continue until matching return */
127
128 if (!inst_trap_return(ins) &&
129 (!inst_return(ins) || --db_call_depth != 0)) {
130 if (db_sstep_print) {
131 if (inst_call(ins) || inst_return(ins)) {
132 register int i;
133
134 db_printf("[after %6d] ", db_inst_count);
135 for (i = db_call_depth; --i > 0; )
136 db_printf(" ");
137 db_print_loc_and_inst(pc);
138 db_printf("\n");
139 }
140 }
141 if (inst_call(ins))
142 db_call_depth++;
143 return (FALSE); /* continue */
144 }
145 }
146 if (db_run_mode == STEP_CALLT) {
147 db_expr_t ins = db_get_value(pc, sizeof(int), FALSE);
148
149 /* continue until call or return */
150
151 if (!inst_call(ins) &&
152 !inst_return(ins) &&
153 !inst_trap_return(ins)) {
154 return (FALSE); /* continue */
155 }
156 }
157 db_run_mode = STEP_NONE;
158 return (TRUE);
159 }
160
161 void
162 db_restart_at_pc(regs, watchpt)
163 db_regs_t *regs;
164 boolean_t watchpt;
165 {
166 register db_addr_t pc = PC_REGS(regs);
167
168 if ((db_run_mode == STEP_COUNT) ||
169 (db_run_mode == STEP_RETURN) ||
170 (db_run_mode == STEP_CALLT)) {
171 db_expr_t ins;
172
173 /*
174 * We are about to execute this instruction,
175 * so count it now.
176 */
177 ins = db_get_value(pc, sizeof(int), FALSE);
178 db_inst_count++;
179 db_load_count += inst_load(ins);
180 db_store_count += inst_store(ins);
181
182 #ifdef SOFTWARE_SSTEP
183 /*
184 * Account for instructions in delay slots.
185 */
186 {
187 db_addr_t brpc;
188
189 brpc = next_instr_address(pc, TRUE);
190 if ((brpc != pc) && (inst_branch(ins) || inst_call(ins))) {
191 ins = db_get_value(brpc, sizeof(int), FALSE);
192 db_inst_count++;
193 db_load_count += inst_load(ins);
194 db_store_count += inst_store(ins);
195 }
196 }
197 #endif
198 }
199
200 if (db_run_mode == STEP_CONTINUE) {
201 if (watchpt || db_find_breakpoint_here(pc)) {
202 /*
203 * Step over breakpoint/watchpoint.
204 */
205 db_run_mode = STEP_INVISIBLE;
206 db_set_single_step(regs);
207 } else {
208 db_set_breakpoints();
209 db_set_watchpoints();
210 }
211 } else {
212 db_set_single_step(regs);
213 }
214 }
215
216 void
217 db_single_step(regs)
218 db_regs_t *regs;
219 {
220 if (db_run_mode == STEP_CONTINUE) {
221 db_run_mode = STEP_INVISIBLE;
222 db_set_single_step(regs);
223 }
224 }
225
226 #ifdef SOFTWARE_SSTEP
227 /*
228 * Software implementation of single-stepping.
229 * If your machine does not have a trace mode
230 * similar to the vax or sun ones you can use
231 * this implementation, done for the mips.
232 * Just define the above conditional and provide
233 * the functions/macros defined below.
234 *
235 * boolean_t inst_branch(int inst)
236 * boolean_t inst_call(int inst)
237 * returns TRUE if the instruction might branch
238 *
239 * boolean_t inst_unconditional_flow_transfer(int inst)
240 * returns TRUE if the instruction is an unconditional
241 * transter of flow (i.e. unconditional branch)
242 *
243 * db_addr_t branch_taken(int inst, db_addr_t pc, db_regs_t *regs)
244 * returns the target address of the branch
245 *
246 * db_addr_t next_instr_address(db_addr_t pc, boolean_t bd)
247 * returns the address of the first instruction following the
248 * one at "pc", which is either in the taken path of the branch
249 * (bd == TRUE) or not. This is for machines (e.g. mips) with
250 * branch delays.
251 *
252 * A single-step may involve at most 2 breakpoints -
253 * one for branch-not-taken and one for branch taken.
254 * If one of these addresses does not already have a breakpoint,
255 * we allocate a breakpoint and save it here.
256 * These breakpoints are deleted on return.
257 */
258 db_breakpoint_t db_not_taken_bkpt = 0;
259 db_breakpoint_t db_taken_bkpt = 0;
260
261 void
262 db_set_single_step(regs)
263 register db_regs_t *regs;
264 {
265 db_addr_t pc = PC_REGS(regs), brpc;
266 boolean_t unconditional;
267 unsigned int inst;
268
269 /*
270 * User was stopped at pc, e.g. the instruction
271 * at pc was not executed.
272 */
273 inst = db_get_value(pc, sizeof(int), FALSE);
274 if (inst_branch(inst) || inst_call(inst)) {
275 brpc = branch_taken(inst, pc, regs);
276 if (brpc != pc) { /* self-branches are hopeless */
277 db_taken_bkpt = db_set_temp_breakpoint(brpc);
278 } else
279 db_taken_bkpt = 0;
280 pc = next_instr_address(pc, TRUE);
281 }
282
283 /*
284 * Check if this control flow instruction is an
285 * unconditional transfer.
286 */
287 unconditional = inst_unconditional_flow_transfer(inst);
288
289 pc = next_instr_address(pc, FALSE);
290
291 /*
292 * We only set the sequential breakpoint if previous
293 * instruction was not an unconditional change of flow
294 * control. If the previous instruction is an
295 * unconditional change of flow control, setting a
296 * breakpoint in the next sequential location may set
297 * a breakpoint in data or in another routine, which
298 * could screw up in either the program or the debugger.
299 * (Consider, for instance, that the next sequential
300 * instruction is the start of a routine needed by the
301 * debugger.)
302 */
303 if (unconditional == FALSE && db_find_breakpoint_here(pc) == 0)
304 db_not_taken_bkpt = db_set_temp_breakpoint(pc);
305 else
306 db_not_taken_bkpt = 0;
307 }
308
309 void
310 db_clear_single_step(regs)
311 db_regs_t *regs;
312 {
313
314 if (db_taken_bkpt != 0) {
315 db_delete_temp_breakpoint(db_taken_bkpt);
316 db_taken_bkpt = 0;
317 }
318 if (db_not_taken_bkpt != 0) {
319 db_delete_temp_breakpoint(db_not_taken_bkpt);
320 db_not_taken_bkpt = 0;
321 }
322 }
323
324 #endif /* SOFTWARE_SSTEP */
325
326 extern int db_cmd_loop_done;
327
328 /* single-step */
329 /*ARGSUSED*/
330 void
331 db_single_step_cmd(addr, have_addr, count, modif)
332 db_expr_t addr;
333 int have_addr;
334 db_expr_t count;
335 char * modif;
336 {
337 boolean_t print = FALSE;
338
339 if (count == -1)
340 count = 1;
341
342 if (modif[0] == 'p')
343 print = TRUE;
344
345 db_run_mode = STEP_ONCE;
346 db_loop_count = count;
347 db_sstep_print = print;
348 db_inst_count = 0;
349 db_load_count = 0;
350 db_store_count = 0;
351
352 db_cmd_loop_done = 1;
353 }
354
355 /* trace and print until call/return */
356 /*ARGSUSED*/
357 void
358 db_trace_until_call_cmd(addr, have_addr, count, modif)
359 db_expr_t addr;
360 int have_addr;
361 db_expr_t count;
362 char * modif;
363 {
364 boolean_t print = FALSE;
365
366 if (modif[0] == 'p')
367 print = TRUE;
368
369 db_run_mode = STEP_CALLT;
370 db_sstep_print = print;
371 db_inst_count = 0;
372 db_load_count = 0;
373 db_store_count = 0;
374
375 db_cmd_loop_done = 1;
376 }
377
378 /*ARGSUSED*/
379 void
380 db_trace_until_matching_cmd(addr, have_addr, count, modif)
381 db_expr_t addr;
382 int have_addr;
383 db_expr_t count;
384 char * modif;
385 {
386 boolean_t print = FALSE;
387
388 if (modif[0] == 'p')
389 print = TRUE;
390
391 db_run_mode = STEP_RETURN;
392 db_call_depth = 1;
393 db_sstep_print = print;
394 db_inst_count = 0;
395 db_load_count = 0;
396 db_store_count = 0;
397
398 db_cmd_loop_done = 1;
399 }
400
401 /* continue */
402 /*ARGSUSED*/
403 void
404 db_continue_cmd(addr, have_addr, count, modif)
405 db_expr_t addr;
406 int have_addr;
407 db_expr_t count;
408 char * modif;
409 {
410 if (modif[0] == 'c')
411 db_run_mode = STEP_COUNT;
412 else
413 db_run_mode = STEP_CONTINUE;
414 db_inst_count = 0;
415 db_load_count = 0;
416 db_store_count = 0;
417
418 db_cmd_loop_done = 1;
419 }
420