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