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