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