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