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