xref: /openbsd/sys/ddb/db_run.c (revision 1ac7f822)
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