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