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