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