1 /* $OpenBSD: db_run.c,v 1.22 2010/11/27 19:59:11 miod Exp $ */
2 /* $NetBSD: db_run.c,v 1.8 1996/02/05 01:57:12 christos Exp $ */
5 * Mach Operating System
6 * Copyright (c) 1993,1992,1991,1990 Carnegie Mellon University
9 * Permission to use, copy, modify and distribute this software and its
10 * documentation is hereby granted, provided that both the copyright
11 * notice and this permission notice appear in all copies of the
12 * software, derivative works or modified versions, and any portions
13 * thereof, and that both notices appear in supporting documentation.
15 * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
16 * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
17 * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
19 * Carnegie Mellon requests users of this software to return to
21 * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
22 * School of Computer Science
23 * Carnegie Mellon University
24 * Pittsburgh PA 15213-3890
26 * any improvements or extensions that they make and grant Carnegie Mellon
27 * the rights to redistribute these changes.
29 * Author: David B. Golub, Carnegie Mellon University
34 * Commands to run process.
36 #include <sys/param.h>
39 #include <uvm/uvm_extern.h>
41 #include <machine/db_machdep.h>
43 #include <ddb/db_run.h>
44 #include <ddb/db_break.h>
45 #include <ddb/db_access.h>
48 db_breakpoint_t db_not_taken_bkpt
= 0;
49 db_breakpoint_t db_taken_bkpt
= 0;
56 #include <ddb/db_lex.h>
57 #include <ddb/db_watch.h>
58 #include <ddb/db_output.h>
59 #include <ddb/db_sym.h>
60 #include <ddb/db_extern.h>
67 #define STEP_CONTINUE 4
68 #define STEP_INVISIBLE 5
71 boolean_t db_sstep_print
;
76 db_stop_at_pc(db_regs_t
*regs
, boolean_t
*is_breakpoint
)
81 db_clear_breakpoints();
82 db_clear_watchpoints();
83 old_pc
= pc
= PC_REGS(regs
);
85 #ifdef FIXUP_PC_AFTER_BREAK
88 * Breakpoint trap. Fix up the PC if the
89 * machine requires it.
91 FIXUP_PC_AFTER_BREAK(regs
);
97 * Now check for a breakpoint at this address.
99 bkpt
= db_find_breakpoint(pc
);
101 if (--bkpt
->count
== 0) {
102 db_clear_single_step(regs
);
103 bkpt
->count
= bkpt
->init_count
;
104 *is_breakpoint
= TRUE
;
105 return (TRUE
); /* stop here */
107 } else if (*is_breakpoint
108 #ifdef SOFTWARE_SSTEP
109 && !((db_taken_bkpt
&& db_taken_bkpt
->address
== pc
) ||
110 (db_not_taken_bkpt
&& db_not_taken_bkpt
->address
== pc
))
117 SET_PC_REGS(regs
, old_pc
);
119 PC_REGS(regs
) = old_pc
;
123 db_clear_single_step(regs
);
125 *is_breakpoint
= FALSE
;
127 if (db_run_mode
== STEP_INVISIBLE
) {
128 db_run_mode
= STEP_CONTINUE
;
129 return (FALSE
); /* continue */
131 if (db_run_mode
== STEP_COUNT
) {
132 return (FALSE
); /* continue */
134 if (db_run_mode
== STEP_ONCE
) {
135 if (--db_loop_count
> 0) {
136 if (db_sstep_print
) {
138 db_print_loc_and_inst(pc
);
141 return (FALSE
); /* continue */
144 if (db_run_mode
== STEP_RETURN
) {
145 db_expr_t ins
= db_get_value(pc
, sizeof(int), FALSE
);
147 /* continue until matching return */
149 if (!inst_trap_return(ins
) &&
150 (!inst_return(ins
) || --db_call_depth
!= 0)) {
151 if (db_sstep_print
) {
152 if (inst_call(ins
) || inst_return(ins
)) {
155 db_printf("[after %6d] ", db_inst_count
);
156 for (i
= db_call_depth
; --i
> 0; )
158 db_print_loc_and_inst(pc
);
164 return (FALSE
); /* continue */
167 if (db_run_mode
== STEP_CALLT
) {
168 db_expr_t ins
= db_get_value(pc
, sizeof(int), FALSE
);
170 /* continue until call or return */
172 if (!inst_call(ins
) && !inst_return(ins
) &&
173 !inst_trap_return(ins
)) {
174 return (FALSE
); /* continue */
177 db_run_mode
= STEP_NONE
;
182 db_restart_at_pc(db_regs_t
*regs
, boolean_t watchpt
)
184 db_addr_t pc
= PC_REGS(regs
);
186 if ((db_run_mode
== STEP_COUNT
) || (db_run_mode
== STEP_RETURN
) ||
187 (db_run_mode
== STEP_CALLT
)) {
191 * We are about to execute this instruction,
194 ins
= db_get_value(pc
, sizeof(int), FALSE
);
196 #ifdef SOFTWARE_SSTEP
197 /* XXX works on mips, but... */
198 if (inst_branch(ins
) || inst_call(ins
)) {
199 ins
= db_get_value(next_instr_address(pc
, 1),
203 #endif /* SOFTWARE_SSTEP */
206 if (db_run_mode
== STEP_CONTINUE
) {
207 if (watchpt
|| db_find_breakpoint(pc
)) {
209 * Step over breakpoint/watchpoint.
211 db_run_mode
= STEP_INVISIBLE
;
212 db_set_single_step(regs
);
214 db_set_breakpoints();
215 db_set_watchpoints();
218 db_set_single_step(regs
);
223 db_single_step(db_regs_t
*regs
)
225 if (db_run_mode
== STEP_CONTINUE
) {
226 db_run_mode
= STEP_INVISIBLE
;
227 db_set_single_step(regs
);
234 db_single_step_cmd(db_expr_t addr
, int have_addr
, db_expr_t count
, char *modif
)
236 boolean_t print
= FALSE
;
244 db_run_mode
= STEP_ONCE
;
245 db_loop_count
= count
;
246 db_sstep_print
= print
;
249 db_cmd_loop_done
= 1;
252 /* trace and print until call/return */
255 db_trace_until_call_cmd(db_expr_t addr
, int have_addr
, db_expr_t count
,
258 boolean_t print
= FALSE
;
263 db_run_mode
= STEP_CALLT
;
264 db_sstep_print
= print
;
267 db_cmd_loop_done
= 1;
272 db_trace_until_matching_cmd(db_expr_t addr
, int have_addr
, db_expr_t count
,
275 boolean_t print
= FALSE
;
280 db_run_mode
= STEP_RETURN
;
282 db_sstep_print
= print
;
285 db_cmd_loop_done
= 1;
291 db_continue_cmd(db_expr_t addr
, int have_addr
, db_expr_t count
, char *modif
)
294 db_run_mode
= STEP_COUNT
;
296 db_run_mode
= STEP_CONTINUE
;
299 db_cmd_loop_done
= 1;
303 #ifdef SOFTWARE_SSTEP
305 * Software implementation of single-stepping.
306 * If your machine does not have a trace mode
307 * similar to the vax or sun ones you can use
308 * this implementation, done for the mips.
309 * Just define the above conditional and provide
310 * the functions/macros defined below.
313 * inst_branch(ins), returns true if the instruction might branch
315 * branch_taken(ins, pc, getreg_val, regs),
316 * return the address the instruction might
318 * getreg_val(regs, reg), return the value of a user register,
319 * as indicated in the hardware instruction
320 * encoding, e.g. 8 for r8
322 * next_instr_address(pc, bd) returns the address of the first
323 * instruction following the one at "pc",
324 * which is either in the taken path of
325 * the branch (bd==1) or not. This is
326 * for machines (mips) with branch delays.
328 * A single-step may involve at most 2 breakpoints -
329 * one for branch-not-taken and one for branch taken.
330 * If one of these addresses does not already have a breakpoint,
331 * we allocate a breakpoint and save it here.
332 * These breakpoints are deleted on return.
336 db_set_single_step(db_regs_t
*regs
)
338 db_addr_t pc
= PC_REGS(regs
);
339 #ifndef SOFTWARE_SSTEP_EMUL
344 * User was stopped at pc, e.g. the instruction
345 * at pc was not executed.
347 inst
= db_get_value(pc
, sizeof(int), FALSE
);
348 if (inst_branch(inst
) || inst_call(inst
) || inst_return(inst
)) {
349 brpc
= branch_taken(inst
, pc
, getreg_val
, regs
);
350 if (brpc
!= pc
) { /* self-branches are hopeless */
351 db_taken_bkpt
= db_set_temp_breakpoint(brpc
);
354 /* XXX this seems like a true bug, no? */
355 pc
= next_instr_address(pc
, 1);
358 #endif /*SOFTWARE_SSTEP_EMUL*/
359 pc
= next_instr_address(pc
, 0);
360 db_not_taken_bkpt
= db_set_temp_breakpoint(pc
);
364 db_clear_single_step(db_regs_t
*regs
)
366 if (db_taken_bkpt
!= 0) {
367 db_delete_temp_breakpoint(db_taken_bkpt
);
370 if (db_not_taken_bkpt
!= 0) {
371 db_delete_temp_breakpoint(db_not_taken_bkpt
);
372 db_not_taken_bkpt
= 0;
376 #endif /* SOFTWARE_SSTEP */