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