db_run.c revision 1.30
1/*	$OpenBSD: db_run.c,v 1.30 2020/10/15 03:14:00 deraadt 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
66int	db_sstep_print;
67int		db_loop_count;
68int		db_call_depth;
69
70int
71db_stop_at_pc(db_regs_t *regs, int *is_breakpoint)
72{
73	vaddr_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 = 1;
100			return 1;	/* 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 = 0;
121
122	if (db_run_mode == STEP_INVISIBLE) {
123		db_run_mode = STEP_CONTINUE;
124		return 0;	/* continue */
125	}
126	if (db_run_mode == STEP_COUNT) {
127		return 0; /* 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 0;	/* continue */
137		}
138	}
139	if (db_run_mode == STEP_RETURN) {
140		db_expr_t ins = db_get_value(pc, sizeof(int), 0);
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 0;	/* continue */
160		}
161	}
162	if (db_run_mode == STEP_CALLT) {
163		db_expr_t ins = db_get_value(pc, sizeof(int), 0);
164
165		/* continue until call or return */
166
167		if (!inst_call(ins) && !inst_return(ins) &&
168		    !inst_trap_return(ins)) {
169			return 0;	/* continue */
170		}
171	}
172	db_run_mode = STEP_NONE;
173	return 1;
174}
175
176void
177db_restart_at_pc(db_regs_t *regs, int watchpt)
178{
179	vaddr_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), 0);
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), 0);
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	int	print = 0;
232
233	if (count == -1)
234		count = 1;
235
236	if (modif[0] == 'p')
237		print = 1;
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	int	print = 0;
254
255	if (modif[0] == 'p')
256		print = 1;
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	int	print = 0;
271
272	if (modif[0] == 'p')
273		print = 1;
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 int
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	vaddr_t pc = PC_REGS(regs);
333#ifndef SOFTWARE_SSTEP_EMUL
334	vaddr_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), 0);
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