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