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