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