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