db_run.c revision 1.31
1/*	$OpenBSD: db_run.c,v 1.31 2023/03/08 04:43:07 guenther 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 */
227void
228db_single_step_cmd(db_expr_t addr, int have_addr, db_expr_t count, char *modif)
229{
230	int	print = 0;
231
232	if (count == -1)
233		count = 1;
234
235	if (modif[0] == 'p')
236		print = 1;
237
238	db_run_mode = STEP_ONCE;
239	db_loop_count = count;
240	db_sstep_print = print;
241	db_inst_count = 0;
242
243	db_cmd_loop_done = 1;
244}
245
246/* trace and print until call/return */
247void
248db_trace_until_call_cmd(db_expr_t addr, int have_addr, db_expr_t count,
249    char *modif)
250{
251	int	print = 0;
252
253	if (modif[0] == 'p')
254		print = 1;
255
256	db_run_mode = STEP_CALLT;
257	db_sstep_print = print;
258	db_inst_count = 0;
259
260	db_cmd_loop_done = 1;
261}
262
263void
264db_trace_until_matching_cmd(db_expr_t addr, int have_addr, db_expr_t count,
265    char *modif)
266{
267	int	print = 0;
268
269	if (modif[0] == 'p')
270		print = 1;
271
272	db_run_mode = STEP_RETURN;
273	db_call_depth = 1;
274	db_sstep_print = print;
275	db_inst_count = 0;
276
277	db_cmd_loop_done = 1;
278}
279
280/* continue */
281void
282db_continue_cmd(db_expr_t addr, int have_addr, db_expr_t count, char *modif)
283{
284	if (modif[0] == 'c')
285		db_run_mode = STEP_COUNT;
286	else
287		db_run_mode = STEP_CONTINUE;
288	db_inst_count = 0;
289
290	db_cmd_loop_done = 1;
291}
292
293#ifdef	SOFTWARE_SSTEP
294/*
295 *	Software implementation of single-stepping.
296 *	If your machine does not have a trace mode
297 *	similar to the vax or sun ones you can use
298 *	this implementation, done for the mips.
299 *	Just define the above conditional and provide
300 *	the functions/macros defined below.
301 *
302 * extern int
303 *	inst_branch(ins),	returns true if the instruction might branch
304 * extern unsigned
305 *	branch_taken(ins, pc, getreg_val, regs),
306 *				return the address the instruction might
307 *				branch to
308 *	getreg_val(regs, reg),	return the value of a user register,
309 *				as indicated in the hardware instruction
310 *				encoding, e.g. 8 for r8
311 *
312 * next_instr_address(pc, bd)	returns the address of the first
313 *				instruction following the one at "pc",
314 *				which is either in the taken path of
315 *				the branch (bd==1) or not.  This is
316 *				for machines (mips) with branch delays.
317 *
318 *	A single-step may involve at most 2 breakpoints -
319 *	one for branch-not-taken and one for branch taken.
320 *	If one of these addresses does not already have a breakpoint,
321 *	we allocate a breakpoint and save it here.
322 *	These breakpoints are deleted on return.
323 */
324
325void
326db_set_single_step(db_regs_t *regs)
327{
328	vaddr_t pc = PC_REGS(regs);
329#ifndef SOFTWARE_SSTEP_EMUL
330	vaddr_t brpc;
331	u_int inst;
332
333	/*
334	 * User was stopped at pc, e.g. the instruction
335	 * at pc was not executed.
336	 */
337	inst = db_get_value(pc, sizeof(int), 0);
338	if (inst_branch(inst) || inst_call(inst) || inst_return(inst)) {
339		brpc = branch_taken(inst, pc, getreg_val, regs);
340		if (brpc != pc) {	/* self-branches are hopeless */
341			db_taken_bkpt = db_set_temp_breakpoint(brpc);
342		}
343#if 0
344		/* XXX this seems like a true bug, no?  */
345		pc = next_instr_address(pc, 1);
346#endif
347	}
348#endif /*SOFTWARE_SSTEP_EMUL*/
349	pc = next_instr_address(pc, 0);
350	db_not_taken_bkpt = db_set_temp_breakpoint(pc);
351}
352
353void
354db_clear_single_step(db_regs_t *regs)
355{
356	if (db_taken_bkpt != 0) {
357		db_delete_temp_breakpoint(db_taken_bkpt);
358		db_taken_bkpt = 0;
359	}
360	if (db_not_taken_bkpt != 0) {
361		db_delete_temp_breakpoint(db_not_taken_bkpt);
362		db_not_taken_bkpt = 0;
363	}
364}
365
366#endif	/* SOFTWARE_SSTEP */
367