db_run.c revision 1.26
1/*	$OpenBSD: db_run.c,v 1.26 2016/01/25 14:30:30 mpi 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/systm.h>
38
39#include <machine/db_machdep.h>
40
41#include <ddb/db_run.h>
42#include <ddb/db_break.h>
43#include <ddb/db_access.h>
44
45#ifdef SOFTWARE_SSTEP
46db_breakpoint_t	db_not_taken_bkpt = 0;
47db_breakpoint_t	db_taken_bkpt = 0;
48#endif
49
50int		db_inst_count;
51
52#ifndef KGDB
53
54#include <ddb/db_watch.h>
55#include <ddb/db_output.h>
56#include <ddb/db_sym.h>
57#include <ddb/db_extern.h>
58
59int	db_run_mode;
60#define	STEP_NONE	0
61#define	STEP_ONCE	1
62#define	STEP_RETURN	2
63#define	STEP_CALLT	3
64#define	STEP_CONTINUE	4
65#define STEP_INVISIBLE	5
66#define	STEP_COUNT	6
67
68boolean_t	db_sstep_print;
69int		db_loop_count;
70int		db_call_depth;
71
72boolean_t
73db_stop_at_pc(db_regs_t *regs, 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(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#ifdef PC_ADVANCE
111		PC_ADVANCE(regs);
112#else
113# ifdef SET_PC_REGS
114		SET_PC_REGS(regs, old_pc);
115# else
116		PC_REGS(regs) = old_pc;
117# endif
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			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(db_regs_t *regs, boolean_t watchpt)
180{
181	db_addr_t pc = PC_REGS(regs);
182
183	if ((db_run_mode == STEP_COUNT) || (db_run_mode == STEP_RETURN) ||
184	    (db_run_mode == STEP_CALLT)) {
185		db_expr_t	ins;
186
187		/*
188		 * We are about to execute this instruction,
189		 * so count it now.
190		 */
191		ins = db_get_value(pc, sizeof(int), FALSE);
192		db_inst_count++;
193#ifdef	SOFTWARE_SSTEP
194		/* XXX works on mips, but... */
195		if (inst_branch(ins) || inst_call(ins)) {
196			ins = db_get_value(next_instr_address(pc, 1),
197			    sizeof(int), FALSE);
198			db_inst_count++;
199		}
200#endif	/* SOFTWARE_SSTEP */
201	}
202
203	if (db_run_mode == STEP_CONTINUE) {
204		if (watchpt || db_find_breakpoint(pc)) {
205			/*
206			 * Step over breakpoint/watchpoint.
207			 */
208			db_run_mode = STEP_INVISIBLE;
209			db_set_single_step(regs);
210		} else {
211			db_set_breakpoints();
212			db_set_watchpoints();
213		}
214	} else {
215		db_set_single_step(regs);
216	}
217}
218
219void
220db_single_step(db_regs_t *regs)
221{
222	if (db_run_mode == STEP_CONTINUE) {
223	    db_run_mode = STEP_INVISIBLE;
224	    db_set_single_step(regs);
225	}
226}
227
228/* single-step */
229/*ARGSUSED*/
230void
231db_single_step_cmd(db_expr_t addr, int have_addr, db_expr_t count, char *modif)
232{
233	boolean_t	print = FALSE;
234
235	if (count == -1)
236	    count = 1;
237
238	if (modif[0] == 'p')
239	    print = TRUE;
240
241	db_run_mode = STEP_ONCE;
242	db_loop_count = count;
243	db_sstep_print = print;
244	db_inst_count = 0;
245
246	db_cmd_loop_done = 1;
247}
248
249/* trace and print until call/return */
250/*ARGSUSED*/
251void
252db_trace_until_call_cmd(db_expr_t addr, int have_addr, db_expr_t count,
253    char *modif)
254{
255	boolean_t	print = FALSE;
256
257	if (modif[0] == 'p')
258	    print = TRUE;
259
260	db_run_mode = STEP_CALLT;
261	db_sstep_print = print;
262	db_inst_count = 0;
263
264	db_cmd_loop_done = 1;
265}
266
267/*ARGSUSED*/
268void
269db_trace_until_matching_cmd(db_expr_t addr, int have_addr, db_expr_t count,
270    char *modif)
271{
272	boolean_t	print = FALSE;
273
274	if (modif[0] == 'p')
275	    print = TRUE;
276
277	db_run_mode = STEP_RETURN;
278	db_call_depth = 1;
279	db_sstep_print = print;
280	db_inst_count = 0;
281
282	db_cmd_loop_done = 1;
283}
284
285/* continue */
286/*ARGSUSED*/
287void
288db_continue_cmd(db_expr_t addr, int have_addr, db_expr_t count, char *modif)
289{
290	if (modif[0] == 'c')
291	    db_run_mode = STEP_COUNT;
292	else
293	    db_run_mode = STEP_CONTINUE;
294	db_inst_count = 0;
295
296	db_cmd_loop_done = 1;
297}
298#endif /* NO KGDB */
299
300#ifdef	SOFTWARE_SSTEP
301/*
302 *	Software implementation of single-stepping.
303 *	If your machine does not have a trace mode
304 *	similar to the vax or sun ones you can use
305 *	this implementation, done for the mips.
306 *	Just define the above conditional and provide
307 *	the functions/macros defined below.
308 *
309 * extern boolean_t
310 *	inst_branch(ins),	returns true if the instruction might branch
311 * extern unsigned
312 *	branch_taken(ins, pc, getreg_val, regs),
313 *				return the address the instruction might
314 *				branch to
315 *	getreg_val(regs, reg),	return the value of a user register,
316 *				as indicated in the hardware instruction
317 *				encoding, e.g. 8 for r8
318 *
319 * next_instr_address(pc, bd)	returns the address of the first
320 *				instruction following the one at "pc",
321 *				which is either in the taken path of
322 *				the branch (bd==1) or not.  This is
323 *				for machines (mips) with branch delays.
324 *
325 *	A single-step may involve at most 2 breakpoints -
326 *	one for branch-not-taken and one for branch taken.
327 *	If one of these addresses does not already have a breakpoint,
328 *	we allocate a breakpoint and save it here.
329 *	These breakpoints are deleted on return.
330 */
331
332void
333db_set_single_step(db_regs_t *regs)
334{
335	db_addr_t pc = PC_REGS(regs);
336#ifndef SOFTWARE_SSTEP_EMUL
337	db_addr_t brpc;
338	u_int inst;
339
340	/*
341	 * User was stopped at pc, e.g. the instruction
342	 * at pc was not executed.
343	 */
344	inst = db_get_value(pc, sizeof(int), FALSE);
345	if (inst_branch(inst) || inst_call(inst) || inst_return(inst)) {
346	    brpc = branch_taken(inst, pc, getreg_val, regs);
347	    if (brpc != pc) {	/* self-branches are hopeless */
348		db_taken_bkpt = db_set_temp_breakpoint(brpc);
349	    }
350#if 0
351	    /* XXX this seems like a true bug, no?  */
352	    pc = next_instr_address(pc, 1);
353#endif
354	}
355#endif /*SOFTWARE_SSTEP_EMUL*/
356	pc = next_instr_address(pc, 0);
357	db_not_taken_bkpt = db_set_temp_breakpoint(pc);
358}
359
360void
361db_clear_single_step(db_regs_t *regs)
362{
363	if (db_taken_bkpt != 0) {
364	    db_delete_temp_breakpoint(db_taken_bkpt);
365	    db_taken_bkpt = 0;
366	}
367	if (db_not_taken_bkpt != 0) {
368	    db_delete_temp_breakpoint(db_not_taken_bkpt);
369	    db_not_taken_bkpt = 0;
370	}
371}
372
373#endif	/* SOFTWARE_SSTEP */
374