db_run.c revision 1.7
1/*	$OpenBSD: db_run.c,v 1.7 1997/07/06 16:35:27 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 <machine/db_machdep.h>
40
41#include <ddb/db_run.h>
42#include <ddb/db_lex.h>
43#include <ddb/db_break.h>
44#include <ddb/db_access.h>
45#include <ddb/db_watch.h>
46#include <ddb/db_output.h>
47#include <ddb/db_sym.h>
48#include <ddb/db_extern.h>
49
50int	db_run_mode;
51#define	STEP_NONE	0
52#define	STEP_ONCE	1
53#define	STEP_RETURN	2
54#define	STEP_CALLT	3
55#define	STEP_CONTINUE	4
56#define STEP_INVISIBLE	5
57#define	STEP_COUNT	6
58
59boolean_t	db_sstep_print;
60int		db_loop_count;
61int		db_call_depth;
62
63boolean_t
64db_stop_at_pc(regs, is_breakpoint)
65	db_regs_t *regs;
66	boolean_t	*is_breakpoint;
67{
68	register db_addr_t	pc;
69	register db_breakpoint_t bkpt;
70
71	db_clear_single_step(regs);
72	db_clear_breakpoints();
73	db_clear_watchpoints();
74	pc = PC_REGS(regs);
75
76#ifdef	FIXUP_PC_AFTER_BREAK
77	if (*is_breakpoint) {
78	    /*
79	     * Breakpoint trap.  Fix up the PC if the
80	     * machine requires it.
81	     */
82	    FIXUP_PC_AFTER_BREAK(regs);
83	    pc = PC_REGS(regs);
84	}
85#endif
86
87	/*
88	 * Now check for a breakpoint at this address.
89	 */
90	bkpt = db_find_breakpoint_here(pc);
91	if (bkpt) {
92	    if (--bkpt->count == 0) {
93		bkpt->count = bkpt->init_count;
94		*is_breakpoint = TRUE;
95		return (TRUE);	/* stop here */
96	    }
97	} else if (*is_breakpoint) {
98#ifndef m88k
99		PC_REGS(regs) += BKPT_SIZE;
100#endif
101	}
102
103	*is_breakpoint = FALSE;
104
105	if (db_run_mode == STEP_INVISIBLE) {
106	    db_run_mode = STEP_CONTINUE;
107	    return (FALSE);	/* continue */
108	}
109	if (db_run_mode == STEP_COUNT) {
110	    return (FALSE); /* continue */
111	}
112	if (db_run_mode == STEP_ONCE) {
113	    if (--db_loop_count > 0) {
114		if (db_sstep_print) {
115		    db_printf("\t\t");
116		    db_print_loc_and_inst(pc);
117		    db_printf("\n");
118		}
119		return (FALSE);	/* continue */
120	    }
121	}
122	if (db_run_mode == STEP_RETURN) {
123	    db_expr_t ins = db_get_value(pc, sizeof(int), FALSE);
124
125	    /* continue until matching return */
126
127	    if (!inst_trap_return(ins) &&
128		(!inst_return(ins) || --db_call_depth != 0)) {
129		if (db_sstep_print) {
130		    if (inst_call(ins) || inst_return(ins)) {
131			register int i;
132
133			db_printf("[after %6d]     ", db_inst_count);
134			for (i = db_call_depth; --i > 0; )
135			    db_printf("  ");
136			db_print_loc_and_inst(pc);
137			db_printf("\n");
138		    }
139		}
140		if (inst_call(ins))
141		    db_call_depth++;
142		return (FALSE);	/* continue */
143	    }
144	}
145	if (db_run_mode == STEP_CALLT) {
146	    db_expr_t ins = db_get_value(pc, sizeof(int), FALSE);
147
148	    /* continue until call or return */
149
150	    if (!inst_call(ins) &&
151		!inst_return(ins) &&
152		!inst_trap_return(ins)) {
153		return (FALSE);	/* continue */
154	    }
155	}
156	db_run_mode = STEP_NONE;
157	return (TRUE);
158}
159
160void
161db_restart_at_pc(regs, watchpt)
162	db_regs_t *regs;
163	boolean_t watchpt;
164{
165	register db_addr_t pc = PC_REGS(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(regs);
200	    } else {
201		db_set_breakpoints();
202		db_set_watchpoints();
203	    }
204	} else {
205	    db_set_single_step(regs);
206	}
207}
208
209void
210db_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
250/*
251 * XXX is this proto right?  We don't use this facility in any of our ports.
252 * I've seen it defined to (0) when it is not needed and then the proto will
253 * not be correct, so skip it then.
254 */
255#ifndef getreg_val
256extern u_int getreg_val __P((db_regs_t *, int));
257#endif
258
259db_breakpoint_t	db_not_taken_bkpt = 0;
260db_breakpoint_t	db_taken_bkpt = 0;
261
262void
263db_set_single_step(regs)
264	register db_regs_t *regs;
265{
266	db_addr_t pc = PC_REGS(regs);
267	register unsigned	 inst, brpc;
268
269	/*
270	 *	User was stopped at pc, e.g. the instruction
271	 *	at pc was not executed.
272	 */
273	inst = db_get_value(pc, sizeof(int), FALSE);
274	if (inst_branch(inst) || inst_call(inst)) {
275	    brpc = branch_taken(inst, pc, getreg_val, regs);
276	    if (brpc != pc) {	/* self-branches are hopeless */
277		db_taken_bkpt = db_set_temp_breakpoint(brpc);
278	    }
279	    pc = next_instr_address(pc, 1);
280	}
281	pc = next_instr_address(pc, 0);
282	db_not_taken_bkpt = db_set_temp_breakpoint(pc);
283}
284
285void
286db_clear_single_step(regs)
287	db_regs_t *regs;
288{
289	if (db_taken_bkpt != 0) {
290	    db_delete_temp_breakpoint(db_taken_bkpt);
291	    db_taken_bkpt = 0;
292	}
293	if (db_not_taken_bkpt != 0) {
294	    db_delete_temp_breakpoint(db_not_taken_bkpt);
295	    db_not_taken_bkpt = 0;
296	}
297}
298
299#endif	SOFTWARE_SSTEP
300
301extern int	db_cmd_loop_done;
302
303/* single-step */
304/*ARGSUSED*/
305void
306db_single_step_cmd(addr, have_addr, count, modif)
307	db_expr_t	addr;
308	int		have_addr;
309	db_expr_t	count;
310	char *		modif;
311{
312	boolean_t	print = FALSE;
313
314	if (count == -1)
315	    count = 1;
316
317	if (modif[0] == 'p')
318	    print = TRUE;
319
320	db_run_mode = STEP_ONCE;
321	db_loop_count = count;
322	db_sstep_print = print;
323	db_inst_count = 0;
324	db_load_count = 0;
325	db_store_count = 0;
326
327	db_cmd_loop_done = 1;
328}
329
330/* trace and print until call/return */
331/*ARGSUSED*/
332void
333db_trace_until_call_cmd(addr, have_addr, count, modif)
334	db_expr_t	addr;
335	int		have_addr;
336	db_expr_t	count;
337	char *		modif;
338{
339	boolean_t	print = FALSE;
340
341	if (modif[0] == 'p')
342	    print = TRUE;
343
344	db_run_mode = STEP_CALLT;
345	db_sstep_print = print;
346	db_inst_count = 0;
347	db_load_count = 0;
348	db_store_count = 0;
349
350	db_cmd_loop_done = 1;
351}
352
353/*ARGSUSED*/
354void
355db_trace_until_matching_cmd(addr, have_addr, count, modif)
356	db_expr_t	addr;
357	int		have_addr;
358	db_expr_t	count;
359	char *		modif;
360{
361	boolean_t	print = FALSE;
362
363	if (modif[0] == 'p')
364	    print = TRUE;
365
366	db_run_mode = STEP_RETURN;
367	db_call_depth = 1;
368	db_sstep_print = print;
369	db_inst_count = 0;
370	db_load_count = 0;
371	db_store_count = 0;
372
373	db_cmd_loop_done = 1;
374}
375
376/* continue */
377/*ARGSUSED*/
378void
379db_continue_cmd(addr, have_addr, count, modif)
380	db_expr_t	addr;
381	int		have_addr;
382	db_expr_t	count;
383	char *		modif;
384{
385	if (modif[0] == 'c')
386	    db_run_mode = STEP_COUNT;
387	else
388	    db_run_mode = STEP_CONTINUE;
389	db_inst_count = 0;
390	db_load_count = 0;
391	db_store_count = 0;
392
393	db_cmd_loop_done = 1;
394}
395