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