db_run.c revision 1.1
1/*	$NetBSD: db_run.c,v 1.7 1994/10/09 08:30:08 mycroft Exp $	*/
2
3/*
4 * Mach Operating System
5 * Copyright (c) 1991,1990 Carnegie Mellon University
6 * All Rights Reserved.
7 *
8 * Permission to use, copy, modify and distribute this software and its
9 * documentation is hereby granted, provided that both the copyright
10 * notice and this permission notice appear in all copies of the
11 * software, derivative works or modified versions, and any portions
12 * thereof, and that both notices appear in supporting documentation.
13 *
14 * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS
15 * CONDITION.  CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
16 * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
17 *
18 * Carnegie Mellon requests users of this software to return to
19 *
20 *  Software Distribution Coordinator  or  Software.Distribution@CS.CMU.EDU
21 *  School of Computer Science
22 *  Carnegie Mellon University
23 *  Pittsburgh PA 15213-3890
24 *
25 * any improvements or extensions that they make and grant Carnegie the
26 * rights to redistribute these changes.
27 *
28 * 	Author: David B. Golub, Carnegie Mellon University
29 *	Date:	7/90
30 */
31
32/*
33 * Commands to run process.
34 */
35#include <sys/param.h>
36#include <sys/proc.h>
37
38#include <machine/db_machdep.h>
39
40#include <ddb/db_run.h>
41#include <ddb/db_lex.h>
42#include <ddb/db_break.h>
43#include <ddb/db_access.h>
44
45int	db_run_mode;
46#define	STEP_NONE	0
47#define	STEP_ONCE	1
48#define	STEP_RETURN	2
49#define	STEP_CALLT	3
50#define	STEP_CONTINUE	4
51#define STEP_INVISIBLE	5
52#define	STEP_COUNT	6
53
54boolean_t	db_sstep_print;
55int		db_loop_count;
56int		db_call_depth;
57
58boolean_t
59db_stop_at_pc(regs, is_breakpoint)
60	db_regs_t *regs;
61	boolean_t	*is_breakpoint;
62{
63	register db_addr_t	pc;
64	register db_breakpoint_t bkpt;
65
66	db_clear_single_step(regs);
67	db_clear_breakpoints();
68	db_clear_watchpoints();
69	pc = PC_REGS(regs);
70
71#ifdef	FIXUP_PC_AFTER_BREAK
72	if (*is_breakpoint) {
73	    /*
74	     * Breakpoint trap.  Fix up the PC if the
75	     * machine requires it.
76	     */
77	    FIXUP_PC_AFTER_BREAK
78	    pc = PC_REGS(regs);
79	}
80#endif
81
82	/*
83	 * Now check for a breakpoint at this address.
84	 */
85	bkpt = db_find_breakpoint_here(pc);
86	if (bkpt) {
87	    if (--bkpt->count == 0) {
88		bkpt->count = bkpt->init_count;
89		*is_breakpoint = TRUE;
90		return (TRUE);	/* stop here */
91	    }
92	} else if (*is_breakpoint) {
93		PC_REGS(regs) += BKPT_SIZE;
94	}
95
96	*is_breakpoint = FALSE;
97
98	if (db_run_mode == STEP_INVISIBLE) {
99	    db_run_mode = STEP_CONTINUE;
100	    return (FALSE);	/* continue */
101	}
102	if (db_run_mode == STEP_COUNT) {
103	    return (FALSE); /* continue */
104	}
105	if (db_run_mode == STEP_ONCE) {
106	    if (--db_loop_count > 0) {
107		if (db_sstep_print) {
108		    db_printf("\t\t");
109		    db_print_loc_and_inst(pc);
110		    db_printf("\n");
111		}
112		return (FALSE);	/* continue */
113	    }
114	}
115	if (db_run_mode == STEP_RETURN) {
116	    db_expr_t ins = db_get_value(pc, sizeof(int), FALSE);
117
118	    /* continue until matching return */
119
120	    if (!inst_trap_return(ins) &&
121		(!inst_return(ins) || --db_call_depth != 0)) {
122		if (db_sstep_print) {
123		    if (inst_call(ins) || inst_return(ins)) {
124			register int i;
125
126			db_printf("[after %6d]     ", db_inst_count);
127			for (i = db_call_depth; --i > 0; )
128			    db_printf("  ");
129			db_print_loc_and_inst(pc);
130			db_printf("\n");
131		    }
132		}
133		if (inst_call(ins))
134		    db_call_depth++;
135		return (FALSE);	/* continue */
136	    }
137	}
138	if (db_run_mode == STEP_CALLT) {
139	    db_expr_t ins = db_get_value(pc, sizeof(int), FALSE);
140
141	    /* continue until call or return */
142
143	    if (!inst_call(ins) &&
144		!inst_return(ins) &&
145		!inst_trap_return(ins)) {
146		return (FALSE);	/* continue */
147	    }
148	}
149	db_run_mode = STEP_NONE;
150	return (TRUE);
151}
152
153void
154db_restart_at_pc(regs, watchpt)
155	db_regs_t *regs;
156	boolean_t watchpt;
157{
158	register db_addr_t pc = PC_REGS(regs);
159
160	if ((db_run_mode == STEP_COUNT) ||
161	    (db_run_mode == STEP_RETURN) ||
162	    (db_run_mode == STEP_CALLT)) {
163	    db_expr_t		ins;
164
165	    /*
166	     * We are about to execute this instruction,
167	     * so count it now.
168	     */
169
170	    ins = db_get_value(pc, sizeof(int), FALSE);
171	    db_inst_count++;
172	    db_load_count += inst_load(ins);
173	    db_store_count += inst_store(ins);
174#ifdef	SOFTWARE_SSTEP
175	    /* XXX works on mips, but... */
176	    if (inst_branch(ins) || inst_call(ins)) {
177		ins = db_get_value(next_instr_address(pc,1),
178				   sizeof(int), FALSE);
179		db_inst_count++;
180		db_load_count += inst_load(ins);
181		db_store_count += inst_store(ins);
182	    }
183#endif	SOFTWARE_SSTEP
184	}
185
186	if (db_run_mode == STEP_CONTINUE) {
187	    if (watchpt || db_find_breakpoint_here(pc)) {
188		/*
189		 * Step over breakpoint/watchpoint.
190		 */
191		db_run_mode = STEP_INVISIBLE;
192		db_set_single_step(regs);
193	    } else {
194		db_set_breakpoints();
195		db_set_watchpoints();
196	    }
197	} else {
198	    db_set_single_step(regs);
199	}
200}
201
202void
203db_single_step(regs)
204	db_regs_t *regs;
205{
206	if (db_run_mode == STEP_CONTINUE) {
207	    db_run_mode = STEP_INVISIBLE;
208	    db_set_single_step(regs);
209	}
210}
211
212#ifdef	SOFTWARE_SSTEP
213/*
214 *	Software implementation of single-stepping.
215 *	If your machine does not have a trace mode
216 *	similar to the vax or sun ones you can use
217 *	this implementation, done for the mips.
218 *	Just define the above conditional and provide
219 *	the functions/macros defined below.
220 *
221 * extern boolean_t
222 *	inst_branch(),		returns true if the instruction might branch
223 * extern unsigned
224 *	branch_taken(),		return the address the instruction might
225 *				branch to
226 *	db_getreg_val();	return the value of a user register,
227 *				as indicated in the hardware instruction
228 *				encoding, e.g. 8 for r8
229 *
230 * next_instr_address(pc,bd)	returns the address of the first
231 *				instruction following the one at "pc",
232 *				which is either in the taken path of
233 *				the branch (bd==1) or not.  This is
234 *				for machines (mips) with branch delays.
235 *
236 *	A single-step may involve at most 2 breakpoints -
237 *	one for branch-not-taken and one for branch taken.
238 *	If one of these addresses does not already have a breakpoint,
239 *	we allocate a breakpoint and save it here.
240 *	These breakpoints are deleted on return.
241 */
242db_breakpoint_t	db_not_taken_bkpt = 0;
243db_breakpoint_t	db_taken_bkpt = 0;
244
245void
246db_set_single_step(regs)
247	register db_regs_t *regs;
248{
249	db_addr_t pc = PC_REGS(regs);
250	register unsigned	 inst, brpc;
251
252	/*
253	 *	User was stopped at pc, e.g. the instruction
254	 *	at pc was not executed.
255	 */
256	inst = db_get_value(pc, sizeof(int), FALSE);
257	if (inst_branch(inst) || inst_call(inst)) {
258	    extern unsigned getreg_val();
259
260	    brpc = branch_taken(inst, pc, getreg_val, regs);
261	    if (brpc != pc) {	/* self-branches are hopeless */
262		db_taken_bkpt = db_set_temp_breakpoint(brpc);
263	    }
264	    pc = next_instr_address(pc,1);
265	}
266	pc = next_instr_address(pc,0);
267	db_not_taken_bkpt = db_set_temp_breakpoint(pc);
268}
269
270void
271db_clear_single_step(regs)
272	db_regs_t *regs;
273{
274	register db_breakpoint_t	bkpt;
275
276	if (db_taken_bkpt != 0) {
277	    db_delete_temp_breakpoint(db_taken_bkpt);
278	    db_taken_bkpt = 0;
279	}
280	if (db_not_taken_bkpt != 0) {
281	    db_delete_temp_breakpoint(db_not_taken_bkpt);
282	    db_not_taken_bkpt = 0;
283	}
284}
285
286#endif	SOFTWARE_SSTEP
287
288extern int	db_cmd_loop_done;
289
290/* single-step */
291/*ARGSUSED*/
292void
293db_single_step_cmd(addr, have_addr, count, modif)
294	db_expr_t	addr;
295	int		have_addr;
296	db_expr_t	count;
297	char *		modif;
298{
299	boolean_t	print = FALSE;
300
301	if (count == -1)
302	    count = 1;
303
304	if (modif[0] == 'p')
305	    print = TRUE;
306
307	db_run_mode = STEP_ONCE;
308	db_loop_count = count;
309	db_sstep_print = print;
310	db_inst_count = 0;
311	db_load_count = 0;
312	db_store_count = 0;
313
314	db_cmd_loop_done = 1;
315}
316
317/* trace and print until call/return */
318/*ARGSUSED*/
319void
320db_trace_until_call_cmd(addr, have_addr, count, modif)
321	db_expr_t	addr;
322	int		have_addr;
323	db_expr_t	count;
324	char *		modif;
325{
326	boolean_t	print = FALSE;
327
328	if (modif[0] == 'p')
329	    print = TRUE;
330
331	db_run_mode = STEP_CALLT;
332	db_sstep_print = print;
333	db_inst_count = 0;
334	db_load_count = 0;
335	db_store_count = 0;
336
337	db_cmd_loop_done = 1;
338}
339
340/*ARGSUSED*/
341void
342db_trace_until_matching_cmd(addr, have_addr, count, modif)
343	db_expr_t	addr;
344	int		have_addr;
345	db_expr_t	count;
346	char *		modif;
347{
348	boolean_t	print = FALSE;
349
350	if (modif[0] == 'p')
351	    print = TRUE;
352
353	db_run_mode = STEP_RETURN;
354	db_call_depth = 1;
355	db_sstep_print = print;
356	db_inst_count = 0;
357	db_load_count = 0;
358	db_store_count = 0;
359
360	db_cmd_loop_done = 1;
361}
362
363/* continue */
364/*ARGSUSED*/
365void
366db_continue_cmd(addr, have_addr, count, modif)
367	db_expr_t	addr;
368	int		have_addr;
369	db_expr_t	count;
370	char *		modif;
371{
372	if (modif[0] == 'c')
373	    db_run_mode = STEP_COUNT;
374	else
375	    db_run_mode = STEP_CONTINUE;
376	db_inst_count = 0;
377	db_load_count = 0;
378	db_store_count = 0;
379
380	db_cmd_loop_done = 1;
381}
382