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