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