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