1 /**	$MirOS: src/sys/ddb/db_run.c,v 1.2 2005/03/06 21:27:34 tg Exp $ */
2 /*	$OpenBSD: db_run.c,v 1.16 2003/02/12 14:41:07 jason Exp $	*/
3 /*	$NetBSD: db_run.c,v 1.8 1996/02/05 01:57:12 christos Exp $	*/
4 
5 /*
6  * Mach Operating System
7  * Copyright (c) 1993,1992,1991,1990 Carnegie Mellon University
8  * All Rights Reserved.
9  *
10  * Permission to use, copy, modify and distribute this software and its
11  * documentation is hereby granted, provided that both the copyright
12  * notice and this permission notice appear in all copies of the
13  * software, derivative works or modified versions, and any portions
14  * thereof, and that both notices appear in supporting documentation.
15  *
16  * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
17  * CONDITION.  CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
18  * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
19  *
20  * Carnegie Mellon requests users of this software to return to
21  *
22  *  Software Distribution Coordinator  or  Software.Distribution@CS.CMU.EDU
23  *  School of Computer Science
24  *  Carnegie Mellon University
25  *  Pittsburgh PA 15213-3890
26  *
27  * any improvements or extensions that they make and grant Carnegie Mellon
28  * the rights to redistribute these changes.
29  *
30  * 	Author: David B. Golub, Carnegie Mellon University
31  *	Date:	7/90
32  */
33 
34 /*
35  * Commands to run process.
36  */
37 #include <sys/param.h>
38 #include <sys/proc.h>
39 
40 #include <uvm/uvm_extern.h>
41 
42 #include <machine/db_machdep.h>
43 
44 #include <ddb/db_run.h>
45 #include <ddb/db_break.h>
46 #include <ddb/db_access.h>
47 
48 #ifdef SOFTWARE_SSTEP
49 db_breakpoint_t	db_not_taken_bkpt = 0;
50 db_breakpoint_t	db_taken_bkpt = 0;
51 #endif
52 
53 int		db_inst_count;
54 int		db_load_count;
55 int		db_store_count;
56 
57 #ifndef KGDB
58 
59 #include <ddb/db_lex.h>
60 #include <ddb/db_watch.h>
61 #include <ddb/db_output.h>
62 #include <ddb/db_sym.h>
63 #include <ddb/db_extern.h>
64 
65 int	db_run_mode;
66 #define	STEP_NONE	0
67 #define	STEP_ONCE	1
68 #define	STEP_RETURN	2
69 #define	STEP_CALLT	3
70 #define	STEP_CONTINUE	4
71 #define STEP_INVISIBLE	5
72 #define	STEP_COUNT	6
73 
74 boolean_t	db_sstep_print;
75 int		db_loop_count;
76 int		db_call_depth;
77 
78 boolean_t
db_stop_at_pc(regs,is_breakpoint)79 db_stop_at_pc(regs, is_breakpoint)
80 	db_regs_t	*regs;
81 	boolean_t	*is_breakpoint;
82 {
83 	db_addr_t	pc, old_pc;
84 	db_breakpoint_t	bkpt;
85 
86 	db_clear_breakpoints();
87 	db_clear_watchpoints();
88 	old_pc = pc = PC_REGS(regs);
89 
90 #ifdef	FIXUP_PC_AFTER_BREAK
91 	if (*is_breakpoint) {
92 		/*
93 		 * Breakpoint trap.  Fix up the PC if the
94 		 * machine requires it.
95 		 */
96 		FIXUP_PC_AFTER_BREAK(regs);
97 		pc = PC_REGS(regs);
98 	}
99 #endif
100 
101 	/*
102 	 * Now check for a breakpoint at this address.
103 	 */
104 	bkpt = db_find_breakpoint_here(pc);
105 	if (bkpt) {
106 		if (--bkpt->count == 0) {
107 			db_clear_single_step(regs);
108 			bkpt->count = bkpt->init_count;
109 			*is_breakpoint = TRUE;
110 			return (TRUE);	/* stop here */
111 		}
112 	} else if (*is_breakpoint
113 #ifdef SOFTWARE_SSTEP
114 	    && !((db_taken_bkpt && db_taken_bkpt->address == pc) ||
115 	    (db_not_taken_bkpt && db_not_taken_bkpt->address == pc))
116 #endif
117 	    ) {
118 #ifdef PC_ADVANCE
119 		PC_ADVANCE(regs);
120 #else
121 		/*
122 		 * XXX why on earth is this ifndef'd?  Please explain!
123 		 * I believe this was a workaround a bug where singlestep
124 		 * breakpoints got deleted before recognized as such.  This
125 		 * bug is now gone and probably this #ifndef should go too.
126 		 */
127 #ifndef m88k
128 		PC_REGS_L(regs) = old_pc;
129 #endif
130 #endif
131 	}
132 	db_clear_single_step(regs);
133 
134 	*is_breakpoint = FALSE;
135 
136 	if (db_run_mode == STEP_INVISIBLE) {
137 		db_run_mode = STEP_CONTINUE;
138 		return (FALSE);	/* continue */
139 	}
140 	if (db_run_mode == STEP_COUNT) {
141 		return (FALSE); /* continue */
142 	}
143 	if (db_run_mode == STEP_ONCE) {
144 		if (--db_loop_count > 0) {
145 			if (db_sstep_print) {
146 				db_printf("\t\t");
147 				db_print_loc_and_inst(pc);
148 				db_printf("\n");
149 			}
150 			return (FALSE);	/* continue */
151 		}
152 	}
153 	if (db_run_mode == STEP_RETURN) {
154 	    db_expr_t ins = db_get_value(pc, sizeof(int), FALSE);
155 
156 	    /* continue until matching return */
157 
158 	    if (!inst_trap_return(ins) &&
159 		(!inst_return(ins) || --db_call_depth != 0)) {
160 		if (db_sstep_print) {
161 		    if (inst_call(ins) || inst_return(ins)) {
162 			register int i;
163 
164 			db_printf("[after %6d]     ", db_inst_count);
165 			for (i = db_call_depth; --i > 0; )
166 			    db_printf("  ");
167 			db_print_loc_and_inst(pc);
168 			db_printf("\n");
169 		    }
170 		}
171 		if (inst_call(ins))
172 		    db_call_depth++;
173 		return (FALSE);	/* continue */
174 	    }
175 	}
176 	if (db_run_mode == STEP_CALLT) {
177 	    db_expr_t ins = db_get_value(pc, sizeof(int), FALSE);
178 
179 	    /* continue until call or return */
180 
181 	    if (!inst_call(ins) && !inst_return(ins) &&
182 		!inst_trap_return(ins)) {
183 		return (FALSE);	/* continue */
184 	    }
185 	}
186 	db_run_mode = STEP_NONE;
187 	return (TRUE);
188 }
189 
190 void
db_restart_at_pc(regs,watchpt)191 db_restart_at_pc(regs, watchpt)
192 	db_regs_t *regs;
193 	boolean_t watchpt;
194 {
195 	db_addr_t pc = PC_REGS(regs);
196 
197 	if ((db_run_mode == STEP_COUNT) || (db_run_mode == STEP_RETURN) ||
198 	    (db_run_mode == STEP_CALLT)) {
199 		db_expr_t	ins;
200 
201 		/*
202 		 * We are about to execute this instruction,
203 		 * so count it now.
204 		 */
205 		ins = db_get_value(pc, sizeof(int), FALSE);
206 		db_inst_count++;
207 		db_load_count += inst_load(ins);
208 		db_store_count += inst_store(ins);
209 #ifdef	SOFTWARE_SSTEP
210 		/* XXX works on mips, but... */
211 		if (inst_branch(ins) || inst_call(ins)) {
212 			ins = db_get_value(next_instr_address(pc, 1),
213 			    sizeof(int), FALSE);
214 			db_inst_count++;
215 			db_load_count += inst_load(ins);
216 			db_store_count += inst_store(ins);
217 		}
218 #endif	/* SOFTWARE_SSTEP */
219 	}
220 
221 	if (db_run_mode == STEP_CONTINUE) {
222 		if (watchpt || db_find_breakpoint_here(pc)) {
223 			/*
224 			 * Step over breakpoint/watchpoint.
225 			 */
226 			db_run_mode = STEP_INVISIBLE;
227 			db_set_single_step(regs);
228 		} else {
229 			db_set_breakpoints();
230 			db_set_watchpoints();
231 		}
232 	} else {
233 		db_set_single_step(regs);
234 	}
235 }
236 
237 void
db_single_step(regs)238 db_single_step(regs)
239 	db_regs_t *regs;
240 {
241 	if (db_run_mode == STEP_CONTINUE) {
242 	    db_run_mode = STEP_INVISIBLE;
243 	    db_set_single_step(regs);
244 	}
245 }
246 
247 extern int	db_cmd_loop_done;
248 
249 /* single-step */
250 /*ARGSUSED*/
251 void
db_single_step_cmd(addr,have_addr,count,modif)252 db_single_step_cmd(addr, have_addr, count, modif)
253 	db_expr_t	addr;
254 	int		have_addr;
255 	db_expr_t	count;
256 	char *		modif;
257 {
258 	boolean_t	print = FALSE;
259 
260 	if (count == -1)
261 	    count = 1;
262 
263 	if (modif[0] == 'p')
264 	    print = TRUE;
265 
266 	db_run_mode = STEP_ONCE;
267 	db_loop_count = count;
268 	db_sstep_print = print;
269 	db_inst_count = 0;
270 	db_load_count = 0;
271 	db_store_count = 0;
272 
273 	db_cmd_loop_done = 1;
274 }
275 
276 /* trace and print until call/return */
277 /*ARGSUSED*/
278 void
db_trace_until_call_cmd(addr,have_addr,count,modif)279 db_trace_until_call_cmd(addr, have_addr, count, modif)
280 	db_expr_t	addr;
281 	int		have_addr;
282 	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_CALLT;
291 	db_sstep_print = print;
292 	db_inst_count = 0;
293 	db_load_count = 0;
294 	db_store_count = 0;
295 
296 	db_cmd_loop_done = 1;
297 }
298 
299 /*ARGSUSED*/
300 void
db_trace_until_matching_cmd(addr,have_addr,count,modif)301 db_trace_until_matching_cmd(addr, have_addr, count, modif)
302 	db_expr_t	addr;
303 	int		have_addr;
304 	db_expr_t	count;
305 	char *		modif;
306 {
307 	boolean_t	print = FALSE;
308 
309 	if (modif[0] == 'p')
310 	    print = TRUE;
311 
312 	db_run_mode = STEP_RETURN;
313 	db_call_depth = 1;
314 	db_sstep_print = print;
315 	db_inst_count = 0;
316 	db_load_count = 0;
317 	db_store_count = 0;
318 
319 	db_cmd_loop_done = 1;
320 }
321 
322 /* continue */
323 /*ARGSUSED*/
324 void
db_continue_cmd(addr,have_addr,count,modif)325 db_continue_cmd(addr, have_addr, count, modif)
326 	db_expr_t	addr;
327 	int		have_addr;
328 	db_expr_t	count;
329 	char *		modif;
330 {
331 	if (modif[0] == 'c')
332 	    db_run_mode = STEP_COUNT;
333 	else
334 	    db_run_mode = STEP_CONTINUE;
335 	db_inst_count = 0;
336 	db_load_count = 0;
337 	db_store_count = 0;
338 
339 	db_cmd_loop_done = 1;
340 }
341 #endif /* NO KGDB */
342 
343 #ifdef	SOFTWARE_SSTEP
344 /*
345  *	Software implementation of single-stepping.
346  *	If your machine does not have a trace mode
347  *	similar to the vax or sun ones you can use
348  *	this implementation, done for the mips.
349  *	Just define the above conditional and provide
350  *	the functions/macros defined below.
351  *
352  * extern boolean_t
353  *	inst_branch(ins),	returns true if the instruction might branch
354  * extern unsigned
355  *	branch_taken(ins, pc, getreg_val, regs),
356  *				return the address the instruction might
357  *				branch to
358  *	getreg_val(regs, reg),	return the value of a user register,
359  *				as indicated in the hardware instruction
360  *				encoding, e.g. 8 for r8
361  *
362  * next_instr_address(pc, bd)	returns the address of the first
363  *				instruction following the one at "pc",
364  *				which is either in the taken path of
365  *				the branch (bd==1) or not.  This is
366  *				for machines (mips) with branch delays.
367  *
368  *	A single-step may involve at most 2 breakpoints -
369  *	one for branch-not-taken and one for branch taken.
370  *	If one of these addresses does not already have a breakpoint,
371  *	we allocate a breakpoint and save it here.
372  *	These breakpoints are deleted on return.
373  */
374 
375 void
db_set_single_step(regs)376 db_set_single_step(regs)
377 	register db_regs_t *regs;
378 {
379 	db_addr_t pc = PC_REGS(regs);
380 #ifndef SOFTWARE_SSTEP_EMUL
381 	db_addr_t brpc;
382 	u_int inst;
383 
384 	/*
385 	 * User was stopped at pc, e.g. the instruction
386 	 * at pc was not executed.
387 	 */
388 	inst = db_get_value(pc, sizeof(int), FALSE);
389 	if (inst_branch(inst) || inst_call(inst) || inst_return(inst)) {
390 	    brpc = branch_taken(inst, pc, getreg_val, regs);
391 	    if (brpc != pc) {	/* self-branches are hopeless */
392 		db_taken_bkpt = db_set_temp_breakpoint(brpc);
393 	    }
394 #if 0
395 	    /* XXX this seems like a true bug, no?  */
396 	    pc = next_instr_address(pc, 1);
397 #endif
398 	}
399 #endif /*SOFTWARE_SSTEP_EMUL*/
400 	pc = next_instr_address(pc, 0);
401 	db_not_taken_bkpt = db_set_temp_breakpoint(pc);
402 }
403 
404 void
db_clear_single_step(regs)405 db_clear_single_step(regs)
406 	db_regs_t *regs;
407 {
408 	if (db_taken_bkpt != 0) {
409 	    db_delete_temp_breakpoint(db_taken_bkpt);
410 	    db_taken_bkpt = 0;
411 	}
412 	if (db_not_taken_bkpt != 0) {
413 	    db_delete_temp_breakpoint(db_not_taken_bkpt);
414 	    db_not_taken_bkpt = 0;
415 	}
416 }
417 
418 #endif	/* SOFTWARE_SSTEP */
419