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