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