db_run.c revision 12515
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.6 1995/11/24 14:13:40 bde 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
70#ifdef notused
71static void	db_single_step __P((db_regs_t *regs));
72#endif
73
74boolean_t
75db_stop_at_pc(is_breakpoint)
76	boolean_t	*is_breakpoint;
77{
78	register db_addr_t	pc;
79	register db_breakpoint_t bkpt;
80
81	db_clear_single_step(DDB_REGS);
82	db_clear_breakpoints();
83	db_clear_watchpoints();
84	pc = PC_REGS(DDB_REGS);
85
86#ifdef	FIXUP_PC_AFTER_BREAK
87	if (*is_breakpoint) {
88	    /*
89	     * Breakpoint trap.  Fix up the PC if the
90	     * machine requires it.
91	     */
92	    FIXUP_PC_AFTER_BREAK
93	    pc = PC_REGS(DDB_REGS);
94	}
95#endif
96
97	/*
98	 * Now check for a breakpoint at this address.
99	 */
100	bkpt = db_find_breakpoint_here(pc);
101	if (bkpt) {
102	    if (--bkpt->count == 0) {
103		bkpt->count = bkpt->init_count;
104		*is_breakpoint = TRUE;
105		return (TRUE);	/* stop here */
106	    }
107	} else if (*is_breakpoint) {
108		ddb_regs.tf_eip += 1;
109	}
110
111	*is_breakpoint = FALSE;
112
113	if (db_run_mode == STEP_INVISIBLE) {
114	    db_run_mode = STEP_CONTINUE;
115	    return (FALSE);	/* continue */
116	}
117	if (db_run_mode == STEP_COUNT) {
118	    return (FALSE); /* continue */
119	}
120	if (db_run_mode == STEP_ONCE) {
121	    if (--db_loop_count > 0) {
122		if (db_sstep_print) {
123		    db_printf("\t\t");
124		    db_print_loc_and_inst(pc);
125		    db_printf("\n");
126		}
127		return (FALSE);	/* continue */
128	    }
129	}
130	if (db_run_mode == STEP_RETURN) {
131	    db_expr_t ins = db_get_value(pc, sizeof(int), FALSE);
132
133	    /* continue until matching return */
134
135	    if (!inst_trap_return(ins) &&
136		(!inst_return(ins) || --db_call_depth != 0)) {
137		if (db_sstep_print) {
138		    if (inst_call(ins) || inst_return(ins)) {
139			register int i;
140
141			db_printf("[after %6d]     ", db_inst_count);
142			for (i = db_call_depth; --i > 0; )
143			    db_printf("  ");
144			db_print_loc_and_inst(pc);
145			db_printf("\n");
146		    }
147		}
148		if (inst_call(ins))
149		    db_call_depth++;
150		return (FALSE);	/* continue */
151	    }
152	}
153	if (db_run_mode == STEP_CALLT) {
154	    db_expr_t ins = db_get_value(pc, sizeof(int), FALSE);
155
156	    /* continue until call or return */
157
158	    if (!inst_call(ins) &&
159		!inst_return(ins) &&
160		!inst_trap_return(ins)) {
161		return (FALSE);	/* continue */
162	    }
163	}
164	db_run_mode = STEP_NONE;
165	return (TRUE);
166}
167
168void
169db_restart_at_pc(watchpt)
170	boolean_t watchpt;
171{
172	register db_addr_t	pc = PC_REGS(DDB_REGS);
173
174	if ((db_run_mode == STEP_COUNT) ||
175	    (db_run_mode == STEP_RETURN) ||
176	    (db_run_mode == STEP_CALLT)) {
177	    db_expr_t		ins;
178
179	    /*
180	     * We are about to execute this instruction,
181	     * so count it now.
182	     */
183
184	    ins = db_get_value(pc, sizeof(int), FALSE);
185	    db_inst_count++;
186	    db_load_count += inst_load(ins);
187	    db_store_count += inst_store(ins);
188#ifdef	SOFTWARE_SSTEP
189	    /* XXX works on mips, but... */
190	    if (inst_branch(ins) || inst_call(ins)) {
191		ins = db_get_value(next_instr_address(pc,1),
192				   sizeof(int), FALSE);
193		db_inst_count++;
194		db_load_count += inst_load(ins);
195		db_store_count += inst_store(ins);
196	    }
197#endif	SOFTWARE_SSTEP
198	}
199
200	if (db_run_mode == STEP_CONTINUE) {
201	    if (watchpt || db_find_breakpoint_here(pc)) {
202		/*
203		 * Step over breakpoint/watchpoint.
204		 */
205		db_run_mode = STEP_INVISIBLE;
206		db_set_single_step(DDB_REGS);
207	    } else {
208		db_set_breakpoints();
209		db_set_watchpoints();
210	    }
211	} else {
212	    db_set_single_step(DDB_REGS);
213	}
214}
215
216#ifdef notused
217static void
218db_single_step(regs)
219	db_regs_t *regs;
220{
221	if (db_run_mode == STEP_CONTINUE) {
222	    db_run_mode = STEP_INVISIBLE;
223	    db_set_single_step(regs);
224	}
225}
226#endif
227
228#ifdef	SOFTWARE_SSTEP
229/*
230 *	Software implementation of single-stepping.
231 *	If your machine does not have a trace mode
232 *	similar to the vax or sun ones you can use
233 *	this implementation, done for the mips.
234 *	Just define the above conditional and provide
235 *	the functions/macros defined below.
236 *
237 * extern boolean_t
238 *	inst_branch(),		returns true if the instruction might branch
239 * extern unsigned
240 *	branch_taken(),		return the address the instruction might
241 *				branch to
242 *	db_getreg_val();	return the value of a user register,
243 *				as indicated in the hardware instruction
244 *				encoding, e.g. 8 for r8
245 *
246 * next_instr_address(pc,bd)	returns the address of the first
247 *				instruction following the one at "pc",
248 *				which is either in the taken path of
249 *				the branch (bd==1) or not.  This is
250 *				for machines (mips) with branch delays.
251 *
252 *	A single-step may involve at most 2 breakpoints -
253 *	one for branch-not-taken and one for branch taken.
254 *	If one of these addresses does not already have a breakpoint,
255 *	we allocate a breakpoint and save it here.
256 *	These breakpoints are deleted on return.
257 */
258db_breakpoint_t	db_not_taken_bkpt = 0;
259db_breakpoint_t	db_taken_bkpt = 0;
260
261void
262db_set_single_step(regs)
263	register db_regs_t *regs;
264{
265	db_addr_t pc = PC_REGS(regs);
266	register unsigned	 inst, brpc;
267
268	/*
269	 *	User was stopped at pc, e.g. the instruction
270	 *	at pc was not executed.
271	 */
272	inst = db_get_value(pc, sizeof(int), FALSE);
273	if (inst_branch(inst) || inst_call(inst)) {
274	    extern unsigned getreg_val();
275
276	    brpc = branch_taken(inst, pc, getreg_val, regs);
277	    if (brpc != pc) {	/* self-branches are hopeless */
278		db_taken_bkpt = db_set_temp_breakpoint(brpc);
279	    }
280	    pc = next_instr_address(pc,1);
281	}
282	pc = next_instr_address(pc,0);
283	db_not_taken_bkpt = db_set_temp_breakpoint(pc);
284}
285
286void
287db_clear_single_step(regs)
288	db_regs_t *regs;
289{
290	register db_breakpoint_t	bkpt;
291
292	if (db_taken_bkpt != 0) {
293	    db_delete_temp_breakpoint(db_taken_bkpt);
294	    db_taken_bkpt = 0;
295	}
296	if (db_not_taken_bkpt != 0) {
297	    db_delete_temp_breakpoint(db_not_taken_bkpt);
298	    db_not_taken_bkpt = 0;
299	}
300}
301
302#endif	SOFTWARE_SSTEP
303
304extern int	db_cmd_loop_done;
305
306/* single-step */
307/*ARGSUSED*/
308void
309db_single_step_cmd(addr, have_addr, count, modif)
310	db_expr_t	addr;
311	boolean_t	have_addr;
312	db_expr_t	count;
313	char *		modif;
314{
315	boolean_t	print = FALSE;
316
317	if (count == -1)
318	    count = 1;
319
320	if (modif[0] == 'p')
321	    print = TRUE;
322
323	db_run_mode = STEP_ONCE;
324	db_loop_count = count;
325	db_sstep_print = print;
326	db_inst_count = 0;
327	db_load_count = 0;
328	db_store_count = 0;
329
330	db_cmd_loop_done = 1;
331}
332
333/* trace and print until call/return */
334/*ARGSUSED*/
335void
336db_trace_until_call_cmd(addr, have_addr, count, modif)
337	db_expr_t	addr;
338	boolean_t	have_addr;
339	db_expr_t	count;
340	char *		modif;
341{
342	boolean_t	print = FALSE;
343
344	if (modif[0] == 'p')
345	    print = TRUE;
346
347	db_run_mode = STEP_CALLT;
348	db_sstep_print = print;
349	db_inst_count = 0;
350	db_load_count = 0;
351	db_store_count = 0;
352
353	db_cmd_loop_done = 1;
354}
355
356/*ARGSUSED*/
357void
358db_trace_until_matching_cmd(addr, have_addr, count, modif)
359	db_expr_t	addr;
360	boolean_t	have_addr;
361	db_expr_t	count;
362	char *		modif;
363{
364	boolean_t	print = FALSE;
365
366	if (modif[0] == 'p')
367	    print = TRUE;
368
369	db_run_mode = STEP_RETURN;
370	db_call_depth = 1;
371	db_sstep_print = print;
372	db_inst_count = 0;
373	db_load_count = 0;
374	db_store_count = 0;
375
376	db_cmd_loop_done = 1;
377}
378
379/* continue */
380/*ARGSUSED*/
381void
382db_continue_cmd(addr, have_addr, count, modif)
383	db_expr_t	addr;
384	boolean_t	have_addr;
385	db_expr_t	count;
386	char *		modif;
387{
388	if (modif[0] == 'c')
389	    db_run_mode = STEP_COUNT;
390	else
391	    db_run_mode = STEP_CONTINUE;
392	db_inst_count = 0;
393	db_load_count = 0;
394	db_store_count = 0;
395
396	db_cmd_loop_done = 1;
397}
398