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