xref: /openbsd-src/sys/ddb/db_run.c (revision 8500990981f885cbe5e6a4958549cacc238b5ae6)
1 /*	$OpenBSD: db_run.c,v 1.16 2003/02/12 14:41:07 jason 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(regs, is_breakpoint)
79 	db_regs_t	*regs;
80 	boolean_t	*is_breakpoint;
81 {
82 	db_addr_t	pc, old_pc;
83 	db_breakpoint_t	bkpt;
84 
85 	db_clear_breakpoints();
86 	db_clear_watchpoints();
87 	old_pc = pc = PC_REGS(regs);
88 
89 #ifdef	FIXUP_PC_AFTER_BREAK
90 	if (*is_breakpoint) {
91 		/*
92 		 * Breakpoint trap.  Fix up the PC if the
93 		 * machine requires it.
94 		 */
95 		FIXUP_PC_AFTER_BREAK(regs);
96 		pc = PC_REGS(regs);
97 	}
98 #endif
99 
100 	/*
101 	 * Now check for a breakpoint at this address.
102 	 */
103 	bkpt = db_find_breakpoint_here(pc);
104 	if (bkpt) {
105 		if (--bkpt->count == 0) {
106 			db_clear_single_step(regs);
107 			bkpt->count = bkpt->init_count;
108 			*is_breakpoint = TRUE;
109 			return (TRUE);	/* stop here */
110 		}
111 	} else if (*is_breakpoint
112 #ifdef SOFTWARE_SSTEP
113 	    && !((db_taken_bkpt && db_taken_bkpt->address == pc) ||
114 	    (db_not_taken_bkpt && db_not_taken_bkpt->address == pc))
115 #endif
116 	    ) {
117 #ifdef PC_ADVANCE
118 		PC_ADVANCE(regs);
119 #else
120 		/*
121 		 * XXX why on earth is this ifndef'd?  Please explain!
122 		 * I believe this was a workaround a bug where singlestep
123 		 * breakpoints got deleted before recognized as such.  This
124 		 * bug is now gone and probably this #ifndef should go too.
125 		 */
126 #ifndef m88k
127 		PC_REGS(regs) = old_pc;
128 #endif
129 #endif
130 	}
131 	db_clear_single_step(regs);
132 
133 	*is_breakpoint = FALSE;
134 
135 	if (db_run_mode == STEP_INVISIBLE) {
136 		db_run_mode = STEP_CONTINUE;
137 		return (FALSE);	/* continue */
138 	}
139 	if (db_run_mode == STEP_COUNT) {
140 		return (FALSE); /* continue */
141 	}
142 	if (db_run_mode == STEP_ONCE) {
143 		if (--db_loop_count > 0) {
144 			if (db_sstep_print) {
145 				db_printf("\t\t");
146 				db_print_loc_and_inst(pc);
147 				db_printf("\n");
148 			}
149 			return (FALSE);	/* continue */
150 		}
151 	}
152 	if (db_run_mode == STEP_RETURN) {
153 	    db_expr_t ins = db_get_value(pc, sizeof(int), FALSE);
154 
155 	    /* continue until matching return */
156 
157 	    if (!inst_trap_return(ins) &&
158 		(!inst_return(ins) || --db_call_depth != 0)) {
159 		if (db_sstep_print) {
160 		    if (inst_call(ins) || inst_return(ins)) {
161 			register int i;
162 
163 			db_printf("[after %6d]     ", db_inst_count);
164 			for (i = db_call_depth; --i > 0; )
165 			    db_printf("  ");
166 			db_print_loc_and_inst(pc);
167 			db_printf("\n");
168 		    }
169 		}
170 		if (inst_call(ins))
171 		    db_call_depth++;
172 		return (FALSE);	/* continue */
173 	    }
174 	}
175 	if (db_run_mode == STEP_CALLT) {
176 	    db_expr_t ins = db_get_value(pc, sizeof(int), FALSE);
177 
178 	    /* continue until call or return */
179 
180 	    if (!inst_call(ins) && !inst_return(ins) &&
181 		!inst_trap_return(ins)) {
182 		return (FALSE);	/* continue */
183 	    }
184 	}
185 	db_run_mode = STEP_NONE;
186 	return (TRUE);
187 }
188 
189 void
190 db_restart_at_pc(regs, watchpt)
191 	db_regs_t *regs;
192 	boolean_t watchpt;
193 {
194 	db_addr_t pc = PC_REGS(regs);
195 
196 	if ((db_run_mode == STEP_COUNT) || (db_run_mode == STEP_RETURN) ||
197 	    (db_run_mode == STEP_CALLT)) {
198 		db_expr_t	ins;
199 
200 		/*
201 		 * We are about to execute this instruction,
202 		 * so count it now.
203 		 */
204 		ins = 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 #ifdef	SOFTWARE_SSTEP
209 		/* XXX works on mips, but... */
210 		if (inst_branch(ins) || inst_call(ins)) {
211 			ins = db_get_value(next_instr_address(pc, 1),
212 			    sizeof(int), FALSE);
213 			db_inst_count++;
214 			db_load_count += inst_load(ins);
215 			db_store_count += inst_store(ins);
216 		}
217 #endif	/* SOFTWARE_SSTEP */
218 	}
219 
220 	if (db_run_mode == STEP_CONTINUE) {
221 		if (watchpt || db_find_breakpoint_here(pc)) {
222 			/*
223 			 * Step over breakpoint/watchpoint.
224 			 */
225 			db_run_mode = STEP_INVISIBLE;
226 			db_set_single_step(regs);
227 		} else {
228 			db_set_breakpoints();
229 			db_set_watchpoints();
230 		}
231 	} else {
232 		db_set_single_step(regs);
233 	}
234 }
235 
236 void
237 db_single_step(regs)
238 	db_regs_t *regs;
239 {
240 	if (db_run_mode == STEP_CONTINUE) {
241 	    db_run_mode = STEP_INVISIBLE;
242 	    db_set_single_step(regs);
243 	}
244 }
245 
246 extern int	db_cmd_loop_done;
247 
248 /* single-step */
249 /*ARGSUSED*/
250 void
251 db_single_step_cmd(addr, have_addr, count, modif)
252 	db_expr_t	addr;
253 	int		have_addr;
254 	db_expr_t	count;
255 	char *		modif;
256 {
257 	boolean_t	print = FALSE;
258 
259 	if (count == -1)
260 	    count = 1;
261 
262 	if (modif[0] == 'p')
263 	    print = TRUE;
264 
265 	db_run_mode = STEP_ONCE;
266 	db_loop_count = count;
267 	db_sstep_print = print;
268 	db_inst_count = 0;
269 	db_load_count = 0;
270 	db_store_count = 0;
271 
272 	db_cmd_loop_done = 1;
273 }
274 
275 /* trace and print until call/return */
276 /*ARGSUSED*/
277 void
278 db_trace_until_call_cmd(addr, have_addr, count, modif)
279 	db_expr_t	addr;
280 	int		have_addr;
281 	db_expr_t	count;
282 	char *		modif;
283 {
284 	boolean_t	print = FALSE;
285 
286 	if (modif[0] == 'p')
287 	    print = TRUE;
288 
289 	db_run_mode = STEP_CALLT;
290 	db_sstep_print = print;
291 	db_inst_count = 0;
292 	db_load_count = 0;
293 	db_store_count = 0;
294 
295 	db_cmd_loop_done = 1;
296 }
297 
298 /*ARGSUSED*/
299 void
300 db_trace_until_matching_cmd(addr, have_addr, count, modif)
301 	db_expr_t	addr;
302 	int		have_addr;
303 	db_expr_t	count;
304 	char *		modif;
305 {
306 	boolean_t	print = FALSE;
307 
308 	if (modif[0] == 'p')
309 	    print = TRUE;
310 
311 	db_run_mode = STEP_RETURN;
312 	db_call_depth = 1;
313 	db_sstep_print = print;
314 	db_inst_count = 0;
315 	db_load_count = 0;
316 	db_store_count = 0;
317 
318 	db_cmd_loop_done = 1;
319 }
320 
321 /* continue */
322 /*ARGSUSED*/
323 void
324 db_continue_cmd(addr, have_addr, count, modif)
325 	db_expr_t	addr;
326 	int		have_addr;
327 	db_expr_t	count;
328 	char *		modif;
329 {
330 	if (modif[0] == 'c')
331 	    db_run_mode = STEP_COUNT;
332 	else
333 	    db_run_mode = STEP_CONTINUE;
334 	db_inst_count = 0;
335 	db_load_count = 0;
336 	db_store_count = 0;
337 
338 	db_cmd_loop_done = 1;
339 }
340 #endif /* NO KGDB */
341 
342 #ifdef	SOFTWARE_SSTEP
343 /*
344  *	Software implementation of single-stepping.
345  *	If your machine does not have a trace mode
346  *	similar to the vax or sun ones you can use
347  *	this implementation, done for the mips.
348  *	Just define the above conditional and provide
349  *	the functions/macros defined below.
350  *
351  * extern boolean_t
352  *	inst_branch(ins),	returns true if the instruction might branch
353  * extern unsigned
354  *	branch_taken(ins, pc, getreg_val, regs),
355  *				return the address the instruction might
356  *				branch to
357  *	getreg_val(regs, reg),	return the value of a user register,
358  *				as indicated in the hardware instruction
359  *				encoding, e.g. 8 for r8
360  *
361  * next_instr_address(pc, bd)	returns the address of the first
362  *				instruction following the one at "pc",
363  *				which is either in the taken path of
364  *				the branch (bd==1) or not.  This is
365  *				for machines (mips) with branch delays.
366  *
367  *	A single-step may involve at most 2 breakpoints -
368  *	one for branch-not-taken and one for branch taken.
369  *	If one of these addresses does not already have a breakpoint,
370  *	we allocate a breakpoint and save it here.
371  *	These breakpoints are deleted on return.
372  */
373 
374 void
375 db_set_single_step(regs)
376 	register db_regs_t *regs;
377 {
378 	db_addr_t pc = PC_REGS(regs);
379 #ifndef SOFTWARE_SSTEP_EMUL
380 	db_addr_t brpc;
381 	u_int inst;
382 
383 	/*
384 	 * User was stopped at pc, e.g. the instruction
385 	 * at pc was not executed.
386 	 */
387 	inst = db_get_value(pc, sizeof(int), FALSE);
388 	if (inst_branch(inst) || inst_call(inst) || inst_return(inst)) {
389 	    brpc = branch_taken(inst, pc, getreg_val, regs);
390 	    if (brpc != pc) {	/* self-branches are hopeless */
391 		db_taken_bkpt = db_set_temp_breakpoint(brpc);
392 	    }
393 #if 0
394 	    /* XXX this seems like a true bug, no?  */
395 	    pc = next_instr_address(pc, 1);
396 #endif
397 	}
398 #endif /*SOFTWARE_SSTEP_EMUL*/
399 	pc = next_instr_address(pc, 0);
400 	db_not_taken_bkpt = db_set_temp_breakpoint(pc);
401 }
402 
403 void
404 db_clear_single_step(regs)
405 	db_regs_t *regs;
406 {
407 	if (db_taken_bkpt != 0) {
408 	    db_delete_temp_breakpoint(db_taken_bkpt);
409 	    db_taken_bkpt = 0;
410 	}
411 	if (db_not_taken_bkpt != 0) {
412 	    db_delete_temp_breakpoint(db_not_taken_bkpt);
413 	    db_not_taken_bkpt = 0;
414 	}
415 }
416 
417 #endif	/* SOFTWARE_SSTEP */
418