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