xref: /freebsd/sys/ddb/db_run.c (revision 2008043f386721d58158e37e0d7e50df8095942d)
1 /*-
2  * SPDX-License-Identifier: MIT-CMU
3  *
4  * Mach Operating System
5  * Copyright (c) 1991,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@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 /*
29  * 	Author: David B. Golub, Carnegie Mellon University
30  *	Date:	7/90
31  */
32 
33 /*
34  * Commands to run process.
35  */
36 
37 #include <sys/cdefs.h>
38 #include <sys/param.h>
39 #include <sys/kdb.h>
40 #include <sys/proc.h>
41 #include <sys/reg.h>
42 #include <sys/systm.h>
43 
44 #include <machine/kdb.h>
45 #include <machine/pcb.h>
46 
47 #include <vm/vm.h>
48 
49 #include <ddb/ddb.h>
50 #include <ddb/db_access.h>
51 #include <ddb/db_break.h>
52 #include <ddb/db_command.h>
53 
54 #define	STEP_ONCE	1
55 #define	STEP_RETURN	2
56 #define	STEP_CALLT	3
57 #define	STEP_CONTINUE	4
58 #define	STEP_INVISIBLE	5
59 #define	STEP_COUNT	6
60 static int	db_run_mode = STEP_CONTINUE;
61 
62 static bool		db_sstep_multiple;
63 static bool		db_sstep_print;
64 static int		db_loop_count;
65 static int		db_call_depth;
66 
67 int		db_inst_count;
68 int		db_load_count;
69 int		db_store_count;
70 
71 bool
72 db_stop_at_pc(int type, int code, bool *is_breakpoint, bool *is_watchpoint)
73 {
74 	db_addr_t	pc;
75 	db_breakpoint_t bkpt;
76 
77 	*is_breakpoint = IS_BREAKPOINT_TRAP(type, code);
78 	*is_watchpoint = IS_WATCHPOINT_TRAP(type, code);
79 	pc = PC_REGS();
80 
81 	db_clear_single_step();
82 	db_clear_breakpoints();
83 	db_clear_watchpoints();
84 
85 #ifdef	FIXUP_PC_AFTER_BREAK
86 	if (*is_breakpoint) {
87 	    /*
88 	     * Breakpoint trap.  Fix up the PC if the
89 	     * machine requires it.
90 	     */
91 	    FIXUP_PC_AFTER_BREAK
92 	    pc = PC_REGS();
93 	}
94 #endif
95 
96 	/*
97 	 * Now check for a breakpoint at this address.
98 	 */
99 	bkpt = db_find_breakpoint_here(pc);
100 	if (bkpt) {
101 	    if (--bkpt->count == 0) {
102 		bkpt->count = bkpt->init_count;
103 		*is_breakpoint = true;
104 		return (true);	/* stop here */
105 	    }
106 	    return (false);	/* continue the countdown */
107 	} else if (*is_breakpoint) {
108 #ifdef BKPT_SKIP
109 		BKPT_SKIP;
110 #endif
111 	}
112 
113 	*is_breakpoint = false;	/* might be a breakpoint, but not ours */
114 
115 	/*
116 	 * If not stepping, then silently ignore single-step traps
117 	 * (except for clearing the single-step-flag above).
118 	 *
119 	 * If stepping, then abort if the trap type is unexpected.
120 	 * Breakpoints owned by us are expected and were handled above.
121 	 * Single-steps are expected and are handled below.  All others
122 	 * are unexpected.
123 	 *
124 	 * Only do either of these if the MD layer claims to classify
125 	 * single-step traps unambiguously (by defining IS_SSTEP_TRAP).
126 	 * Otherwise, fall through to the bad historical behaviour
127 	 * given by turning unexpected traps into expected traps: if not
128 	 * stepping, then expect only breakpoints and stop, and if
129 	 * stepping, then expect only single-steps and step.
130 	 */
131 #ifdef IS_SSTEP_TRAP
132 	if (db_run_mode == STEP_CONTINUE && IS_SSTEP_TRAP(type, code))
133 	    return (false);
134 	if (db_run_mode != STEP_CONTINUE && !IS_SSTEP_TRAP(type, code)) {
135 	    printf("Stepping aborted\n");
136 	    return (true);
137 	}
138 #endif
139 
140 	if (db_run_mode == STEP_INVISIBLE) {
141 	    db_run_mode = STEP_CONTINUE;
142 	    return (false);	/* continue */
143 	}
144 	if (db_run_mode == STEP_COUNT) {
145 	    return (false); /* continue */
146 	}
147 	if (db_run_mode == STEP_ONCE) {
148 	    if (--db_loop_count > 0) {
149 		if (db_sstep_print) {
150 		    db_printf("\t\t");
151 		    db_print_loc_and_inst(pc);
152 		}
153 		return (false);	/* continue */
154 	    }
155 	}
156 	if (db_run_mode == STEP_RETURN) {
157 	    /* continue until matching return */
158 	    db_expr_t ins;
159 
160 	    ins = db_get_value(pc, sizeof(int), false);
161 	    if (!inst_trap_return(ins) &&
162 		(!inst_return(ins) || --db_call_depth != 0)) {
163 		if (db_sstep_print) {
164 		    if (inst_call(ins) || inst_return(ins)) {
165 			int i;
166 
167 			db_printf("[after %6d]     ", db_inst_count);
168 			for (i = db_call_depth; --i > 0; )
169 			    db_printf("  ");
170 			db_print_loc_and_inst(pc);
171 		    }
172 		}
173 		if (inst_call(ins))
174 		    db_call_depth++;
175 		return (false);	/* continue */
176 	    }
177 	}
178 	if (db_run_mode == STEP_CALLT) {
179 	    /* continue until call or return */
180 	    db_expr_t ins;
181 
182 	    ins = db_get_value(pc, sizeof(int), false);
183 	    if (!inst_call(ins) &&
184 		!inst_return(ins) &&
185 		!inst_trap_return(ins)) {
186 		return (false);	/* continue */
187 	    }
188 	}
189 	return (true);
190 }
191 
192 void
193 db_restart_at_pc(bool watchpt)
194 {
195 	db_addr_t	pc = PC_REGS();
196 
197 	if ((db_run_mode == STEP_COUNT) ||
198 	    ((db_run_mode == STEP_ONCE) && db_sstep_multiple) ||
199 	    (db_run_mode == STEP_RETURN) ||
200 	    (db_run_mode == STEP_CALLT)) {
201 	    /*
202 	     * We are about to execute this instruction,
203 	     * so count it now.
204 	     */
205 	    db_get_value(pc, sizeof(int), false);
206 	    db_inst_count++;
207 	    db_load_count += inst_load(ins);
208 	    db_store_count += inst_store(ins);
209 	}
210 
211 	if (db_run_mode == STEP_CONTINUE) {
212 	    if (watchpt || db_find_breakpoint_here(pc)) {
213 		/*
214 		 * Step over breakpoint/watchpoint.
215 		 */
216 		db_run_mode = STEP_INVISIBLE;
217 		db_set_single_step();
218 	    } else {
219 		db_set_breakpoints();
220 		db_set_watchpoints();
221 	    }
222 	} else {
223 	    db_set_single_step();
224 	}
225 }
226 
227 /* single-step */
228 /*ARGSUSED*/
229 void
230 db_single_step_cmd(db_expr_t addr, bool have_addr, db_expr_t count, char *modif)
231 {
232 	bool		print = false;
233 
234 	if (count == -1)
235 	    count = 1;
236 
237 	if (modif[0] == 'p')
238 	    print = true;
239 
240 	db_run_mode = STEP_ONCE;
241 	db_loop_count = count;
242 	db_sstep_multiple = (count != 1);
243 	db_sstep_print = print;
244 	db_inst_count = 0;
245 	db_load_count = 0;
246 	db_store_count = 0;
247 
248 	db_cmd_loop_done = 1;
249 }
250 
251 /* trace and print until call/return */
252 /*ARGSUSED*/
253 void
254 db_trace_until_call_cmd(db_expr_t addr, bool have_addr, db_expr_t count,
255     char *modif)
256 {
257 	bool	print = false;
258 
259 	if (modif[0] == 'p')
260 	    print = true;
261 
262 	db_run_mode = STEP_CALLT;
263 	db_sstep_print = print;
264 	db_inst_count = 0;
265 	db_load_count = 0;
266 	db_store_count = 0;
267 
268 	db_cmd_loop_done = 1;
269 }
270 
271 /*ARGSUSED*/
272 void
273 db_trace_until_matching_cmd(db_expr_t addr, bool have_addr, db_expr_t count,
274     char *modif)
275 {
276 	bool	print = false;
277 
278 	if (modif[0] == 'p')
279 	    print = true;
280 
281 	db_run_mode = STEP_RETURN;
282 	db_call_depth = 1;
283 	db_sstep_print = print;
284 	db_inst_count = 0;
285 	db_load_count = 0;
286 	db_store_count = 0;
287 
288 	db_cmd_loop_done = 1;
289 }
290 
291 /* continue */
292 /*ARGSUSED*/
293 void
294 db_continue_cmd(db_expr_t addr, bool have_addr, db_expr_t count, char *modif)
295 {
296 	if (modif[0] == 'c')
297 	    db_run_mode = STEP_COUNT;
298 	else
299 	    db_run_mode = STEP_CONTINUE;
300 	db_inst_count = 0;
301 	db_load_count = 0;
302 	db_store_count = 0;
303 
304 	db_cmd_loop_done = 1;
305 }
306