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