db_run.c revision 1.28
1139825Simp/*	$OpenBSD: db_run.c,v 1.28 2019/11/06 07:30:08 mpi Exp $	*/
2116964Sgrehan/*	$NetBSD: db_run.c,v 1.8 1996/02/05 01:57:12 christos Exp $	*/
3116964Sgrehan
4116964Sgrehan/*
5116964Sgrehan * Mach Operating System
6116964Sgrehan * Copyright (c) 1993,1992,1991,1990 Carnegie Mellon University
7116964Sgrehan * All Rights Reserved.
8116964Sgrehan *
9116964Sgrehan * Permission to use, copy, modify and distribute this software and its
10116964Sgrehan * documentation is hereby granted, provided that both the copyright
11116964Sgrehan * notice and this permission notice appear in all copies of the
12116964Sgrehan * software, derivative works or modified versions, and any portions
13116964Sgrehan * thereof, and that both notices appear in supporting documentation.
14116964Sgrehan *
15116964Sgrehan * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
16116964Sgrehan * CONDITION.  CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
17116964Sgrehan * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
18116964Sgrehan *
19116964Sgrehan * Carnegie Mellon requests users of this software to return to
20116964Sgrehan *
21116964Sgrehan *  Software Distribution Coordinator  or  Software.Distribution@CS.CMU.EDU
22116964Sgrehan *  School of Computer Science
23116964Sgrehan *  Carnegie Mellon University
24116964Sgrehan *  Pittsburgh PA 15213-3890
25116964Sgrehan *
26116964Sgrehan * any improvements or extensions that they make and grant Carnegie Mellon
27116964Sgrehan * the rights to redistribute these changes.
28227843Smarius *
29227843Smarius * 	Author: David B. Golub, Carnegie Mellon University
30227843Smarius *	Date:	7/90
31116964Sgrehan */
32116964Sgrehan
33131102Sgrehan/*
34116964Sgrehan * Commands to run process.
35116964Sgrehan */
36116964Sgrehan#include <sys/param.h>
37266019Sian#include <sys/systm.h>
38116964Sgrehan
39116964Sgrehan#include <machine/db_machdep.h>
40116964Sgrehan
41183882Snwhitehorn#include <ddb/db_run.h>
42186128Snwhitehorn#include <ddb/db_break.h>
43116964Sgrehan#include <ddb/db_access.h>
44116964Sgrehan
45116964Sgrehan#ifdef SOFTWARE_SSTEP
46116964Sgrehandb_breakpoint_t	db_not_taken_bkpt = 0;
47116964Sgrehandb_breakpoint_t	db_taken_bkpt = 0;
48209298Snwhitehorn#endif
49116964Sgrehan
50174782Smarcelint		db_inst_count;
51116964Sgrehan
52116964Sgrehan#include <ddb/db_watch.h>
53116964Sgrehan#include <ddb/db_output.h>
54116964Sgrehan#include <ddb/db_sym.h>
55230993Snwhitehorn#include <ddb/db_extern.h>
56116964Sgrehan
57116964Sgrehanint	db_run_mode;
58116964Sgrehan#define	STEP_NONE	0
59116964Sgrehan#define	STEP_ONCE	1
60116964Sgrehan#define	STEP_RETURN	2
61116964Sgrehan#define	STEP_CALLT	3
62116964Sgrehan#define	STEP_CONTINUE	4
63116964Sgrehan#define STEP_INVISIBLE	5
64116964Sgrehan#define	STEP_COUNT	6
65116964Sgrehan
66116964Sgrehanint	db_sstep_print;
67116964Sgrehanint		db_loop_count;
68116964Sgrehanint		db_call_depth;
69116964Sgrehan
70116964Sgrehanint
71116964Sgrehandb_stop_at_pc(db_regs_t *regs, int *is_breakpoint)
72116964Sgrehan{
73116964Sgrehan	db_addr_t	pc, old_pc;
74116964Sgrehan	db_breakpoint_t	bkpt;
75116964Sgrehan
76116964Sgrehan	db_clear_breakpoints();
77116964Sgrehan	db_clear_watchpoints();
78116964Sgrehan	old_pc = pc = PC_REGS(regs);
79116964Sgrehan
80116964Sgrehan#ifdef	FIXUP_PC_AFTER_BREAK
81116964Sgrehan	if (*is_breakpoint) {
82116964Sgrehan		/*
83266019Sian		 * Breakpoint trap.  Fix up the PC if the
84116964Sgrehan		 * machine requires it.
85266019Sian		 */
86266019Sian		FIXUP_PC_AFTER_BREAK(regs);
87116964Sgrehan		pc = PC_REGS(regs);
88116964Sgrehan	}
89116964Sgrehan#endif
90116964Sgrehan
91116964Sgrehan	/*
92116964Sgrehan	 * Now check for a breakpoint at this address.
93116964Sgrehan	 */
94116964Sgrehan	bkpt = db_find_breakpoint(pc);
95116964Sgrehan	if (bkpt) {
96116964Sgrehan		if (--bkpt->count == 0) {
97116964Sgrehan			db_clear_single_step(regs);
98116964Sgrehan			bkpt->count = bkpt->init_count;
99227843Smarius			*is_breakpoint = 1;
100116964Sgrehan			return 1;	/* stop here */
101116964Sgrehan		}
102116964Sgrehan	} else if (*is_breakpoint
103230994Snwhitehorn#ifdef SOFTWARE_SSTEP
104230993Snwhitehorn	    && !((db_taken_bkpt && db_taken_bkpt->address == pc) ||
105266160Sian	    (db_not_taken_bkpt && db_not_taken_bkpt->address == pc))
106116964Sgrehan#endif
107116964Sgrehan	    ) {
108116964Sgrehan#ifdef PC_ADVANCE
109116964Sgrehan		PC_ADVANCE(regs);
110183882Snwhitehorn#else
111116964Sgrehan# ifdef SET_PC_REGS
112183882Snwhitehorn		SET_PC_REGS(regs, old_pc);
113183882Snwhitehorn# else
114116964Sgrehan		PC_REGS(regs) = old_pc;
115116964Sgrehan# endif
116116964Sgrehan#endif
117116964Sgrehan	}
118116964Sgrehan	db_clear_single_step(regs);
119116964Sgrehan
120116964Sgrehan	*is_breakpoint = 0;
121116964Sgrehan
122116964Sgrehan	if (db_run_mode == STEP_INVISIBLE) {
123116964Sgrehan		db_run_mode = STEP_CONTINUE;
124116964Sgrehan		return 0;	/* continue */
125116964Sgrehan	}
126116964Sgrehan	if (db_run_mode == STEP_COUNT) {
127116964Sgrehan		return 0; /* continue */
128116964Sgrehan	}
129116964Sgrehan	if (db_run_mode == STEP_ONCE) {
130116964Sgrehan		if (--db_loop_count > 0) {
131116964Sgrehan			if (db_sstep_print) {
132116964Sgrehan				db_printf("\t\t");
133116964Sgrehan				db_print_loc_and_inst(pc);
134116964Sgrehan				db_printf("\n");
135116964Sgrehan			}
136116964Sgrehan			return 0;	/* continue */
137116964Sgrehan		}
138116964Sgrehan	}
139116964Sgrehan	if (db_run_mode == STEP_RETURN) {
140116964Sgrehan	    db_expr_t ins = db_get_value(pc, sizeof(int), 0);
141230993Snwhitehorn
142116964Sgrehan	    /* continue until matching return */
143116964Sgrehan
144116964Sgrehan	    if (!inst_trap_return(ins) &&
145116964Sgrehan		(!inst_return(ins) || --db_call_depth != 0)) {
146116964Sgrehan		if (db_sstep_print) {
147116964Sgrehan		    if (inst_call(ins) || inst_return(ins)) {
148116964Sgrehan			int i;
149116964Sgrehan
150116964Sgrehan			db_printf("[after %6d]     ", db_inst_count);
151116964Sgrehan			for (i = db_call_depth; --i > 0; )
152116964Sgrehan			    db_printf("  ");
153116964Sgrehan			db_print_loc_and_inst(pc);
154116964Sgrehan			db_printf("\n");
155116964Sgrehan		    }
156116964Sgrehan		}
157116964Sgrehan		if (inst_call(ins))
158116964Sgrehan		    db_call_depth++;
159116964Sgrehan		return 0;	/* continue */
160116964Sgrehan	    }
161116964Sgrehan	}
162116964Sgrehan	if (db_run_mode == STEP_CALLT) {
163125688Sgrehan	    db_expr_t ins = db_get_value(pc, sizeof(int), 0);
164116964Sgrehan
165116964Sgrehan	    /* continue until call or return */
166116964Sgrehan
167125688Sgrehan	    if (!inst_call(ins) && !inst_return(ins) &&
168116964Sgrehan		!inst_trap_return(ins)) {
169116964Sgrehan		return 0;	/* continue */
170116964Sgrehan	    }
171116964Sgrehan	}
172116964Sgrehan	db_run_mode = STEP_NONE;
173116964Sgrehan	return 1;
174116964Sgrehan}
175116964Sgrehan
176116964Sgrehanvoid
177116964Sgrehandb_restart_at_pc(db_regs_t *regs, int watchpt)
178116964Sgrehan{
179116964Sgrehan	db_addr_t pc = PC_REGS(regs);
180116964Sgrehan
181116964Sgrehan	if ((db_run_mode == STEP_COUNT) || (db_run_mode == STEP_RETURN) ||
182116964Sgrehan	    (db_run_mode == STEP_CALLT)) {
183116964Sgrehan		db_expr_t	ins;
184116964Sgrehan
185116964Sgrehan		/*
186116964Sgrehan		 * We are about to execute this instruction,
187116964Sgrehan		 * so count it now.
188116964Sgrehan		 */
189116964Sgrehan		ins = db_get_value(pc, sizeof(int), 0);
190116964Sgrehan		db_inst_count++;
191116964Sgrehan#ifdef	SOFTWARE_SSTEP
192116964Sgrehan		/* XXX works on mips, but... */
193116964Sgrehan		if (inst_branch(ins) || inst_call(ins)) {
194116964Sgrehan			ins = db_get_value(next_instr_address(pc, 1),
195116964Sgrehan			    sizeof(int), 0);
196116964Sgrehan			db_inst_count++;
197116964Sgrehan		}
198116964Sgrehan#endif	/* SOFTWARE_SSTEP */
199116964Sgrehan	}
200116964Sgrehan
201116964Sgrehan	if (db_run_mode == STEP_CONTINUE) {
202116964Sgrehan		if (watchpt || db_find_breakpoint(pc)) {
203116964Sgrehan			/*
204116964Sgrehan			 * Step over breakpoint/watchpoint.
205116964Sgrehan			 */
206116964Sgrehan			db_run_mode = STEP_INVISIBLE;
207116964Sgrehan			db_set_single_step(regs);
208116964Sgrehan		} else {
209116964Sgrehan			db_set_breakpoints();
210116964Sgrehan			db_set_watchpoints();
211116964Sgrehan		}
212116964Sgrehan	} else {
213116964Sgrehan		db_set_single_step(regs);
214116964Sgrehan	}
215116964Sgrehan}
216116964Sgrehan
217116964Sgrehanvoid
218116964Sgrehandb_single_step(db_regs_t *regs)
219116964Sgrehan{
220116964Sgrehan	if (db_run_mode == STEP_CONTINUE) {
221116964Sgrehan	    db_run_mode = STEP_INVISIBLE;
222116964Sgrehan	    db_set_single_step(regs);
223116964Sgrehan	}
224116964Sgrehan}
225116964Sgrehan
226125688Sgrehan/* single-step */
227116964Sgrehan/*ARGSUSED*/
228116964Sgrehanvoid
229116964Sgrehandb_single_step_cmd(db_expr_t addr, int have_addr, db_expr_t count, char *modif)
230116964Sgrehan{
231116964Sgrehan	int	print = 0;
232116964Sgrehan
233116964Sgrehan	if (count == -1)
234116964Sgrehan	    count = 1;
235116964Sgrehan
236116964Sgrehan	if (modif[0] == 'p')
237116964Sgrehan	    print = 1;
238116964Sgrehan
239116964Sgrehan	db_run_mode = STEP_ONCE;
240116964Sgrehan	db_loop_count = count;
241116964Sgrehan	db_sstep_print = print;
242116964Sgrehan	db_inst_count = 0;
243266019Sian
244266019Sian	db_cmd_loop_done = 1;
245266019Sian}
246266019Sian
247266019Sian/* trace and print until call/return */
248266019Sian/*ARGSUSED*/
249266019Sianvoid
250266019Siandb_trace_until_call_cmd(db_expr_t addr, int have_addr, db_expr_t count,
251266019Sian    char *modif)
252266019Sian{
253266019Sian	int	print = 0;
254266019Sian
255266019Sian	if (modif[0] == 'p')
256266019Sian	    print = 1;
257266019Sian
258266019Sian	db_run_mode = STEP_CALLT;
259266019Sian	db_sstep_print = print;
260266019Sian	db_inst_count = 0;
261266019Sian
262266019Sian	db_cmd_loop_done = 1;
263266019Sian}
264266019Sian
265266019Sian/*ARGSUSED*/
266266019Sianvoid
267266019Siandb_trace_until_matching_cmd(db_expr_t addr, int have_addr, db_expr_t count,
268266019Sian    char *modif)
269266019Sian{
270266019Sian	int	print = 0;
271266019Sian
272266019Sian	if (modif[0] == 'p')
273266019Sian	    print = 1;
274266019Sian
275266019Sian	db_run_mode = STEP_RETURN;
276266019Sian	db_call_depth = 1;
277266019Sian	db_sstep_print = print;
278266019Sian	db_inst_count = 0;
279266019Sian
280266019Sian	db_cmd_loop_done = 1;
281266019Sian}
282266019Sian
283266019Sian/* continue */
284266019Sian/*ARGSUSED*/
285266019Sianvoid
286266019Siandb_continue_cmd(db_expr_t addr, int have_addr, db_expr_t count, char *modif)
287116964Sgrehan{
288116964Sgrehan	if (modif[0] == 'c')
289116964Sgrehan	    db_run_mode = STEP_COUNT;
290116964Sgrehan	else
291116964Sgrehan	    db_run_mode = STEP_CONTINUE;
292116964Sgrehan	db_inst_count = 0;
293116964Sgrehan
294127703Sgrehan	db_cmd_loop_done = 1;
295127703Sgrehan}
296116964Sgrehan
297127703Sgrehan#ifdef	SOFTWARE_SSTEP
298116964Sgrehan/*
299116964Sgrehan *	Software implementation of single-stepping.
300116964Sgrehan *	If your machine does not have a trace mode
301116964Sgrehan *	similar to the vax or sun ones you can use
302116964Sgrehan *	this implementation, done for the mips.
303116964Sgrehan *	Just define the above conditional and provide
304116964Sgrehan *	the functions/macros defined below.
305116964Sgrehan *
306116964Sgrehan * extern int
307116964Sgrehan *	inst_branch(ins),	returns true if the instruction might branch
308116964Sgrehan * extern unsigned
309116964Sgrehan *	branch_taken(ins, pc, getreg_val, regs),
310116964Sgrehan *				return the address the instruction might
311116964Sgrehan *				branch to
312116964Sgrehan *	getreg_val(regs, reg),	return the value of a user register,
313116964Sgrehan *				as indicated in the hardware instruction
314116964Sgrehan *				encoding, e.g. 8 for r8
315116964Sgrehan *
316116964Sgrehan * next_instr_address(pc, bd)	returns the address of the first
317116964Sgrehan *				instruction following the one at "pc",
318116964Sgrehan *				which is either in the taken path of
319116964Sgrehan *				the branch (bd==1) or not.  This is
320116964Sgrehan *				for machines (mips) with branch delays.
321116964Sgrehan *
322116964Sgrehan *	A single-step may involve at most 2 breakpoints -
323116964Sgrehan *	one for branch-not-taken and one for branch taken.
324116964Sgrehan *	If one of these addresses does not already have a breakpoint,
325116964Sgrehan *	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), 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