db_run.c revision 1.12
1/*	$OpenBSD: db_run.c,v 1.12 1999/06/17 18:17:08 art 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/proc.h>
38
39#include <vm/vm.h>
40
41#include <machine/db_machdep.h>
42
43#include <ddb/db_run.h>
44#include <ddb/db_lex.h>
45#include <ddb/db_break.h>
46#include <ddb/db_access.h>
47#include <ddb/db_watch.h>
48#include <ddb/db_output.h>
49#include <ddb/db_sym.h>
50#include <ddb/db_extern.h>
51
52int	db_run_mode;
53#define	STEP_NONE	0
54#define	STEP_ONCE	1
55#define	STEP_RETURN	2
56#define	STEP_CALLT	3
57#define	STEP_CONTINUE	4
58#define STEP_INVISIBLE	5
59#define	STEP_COUNT	6
60
61boolean_t	db_sstep_print;
62int		db_loop_count;
63int		db_call_depth;
64
65#ifdef SOFTWARE_SSTEP
66db_breakpoint_t	db_not_taken_bkpt = 0;
67db_breakpoint_t	db_taken_bkpt = 0;
68#endif
69
70boolean_t
71db_stop_at_pc(regs, is_breakpoint)
72	db_regs_t	*regs;
73	boolean_t	*is_breakpoint;
74{
75	db_addr_t	pc, old_pc;
76	db_breakpoint_t	bkpt;
77
78	db_clear_breakpoints();
79	db_clear_watchpoints();
80	old_pc = pc = PC_REGS(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(regs);
89		pc = PC_REGS(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			db_clear_single_step(regs);
100			bkpt->count = bkpt->init_count;
101			*is_breakpoint = TRUE;
102			return (TRUE);	/* stop here */
103		}
104	} else if (*is_breakpoint
105#ifdef SOFTWARE_SSTEP
106	    && !((db_taken_bkpt && db_taken_bkpt->address == pc) ||
107	    (db_not_taken_bkpt && db_not_taken_bkpt->address == pc))
108#endif
109	    ) {
110#ifdef PC_ADVANCE
111		PC_ADVANCE(regs);
112#else
113		/*
114		 * XXX why on earth is this ifndef'd?  Please explain!
115		 * I believe this was a workaround a bug where singlestep
116		 * breakpoints got deleted before recognized as such.  This
117		 * bug is now gone and probably this #ifndef should go too.
118		 */
119#ifndef m88k
120		PC_REGS(regs) = old_pc;
121#endif
122#endif
123	}
124	db_clear_single_step(regs);
125
126	*is_breakpoint = FALSE;
127
128	if (db_run_mode == STEP_INVISIBLE) {
129		db_run_mode = STEP_CONTINUE;
130		return (FALSE);	/* continue */
131	}
132	if (db_run_mode == STEP_COUNT) {
133		return (FALSE); /* continue */
134	}
135	if (db_run_mode == STEP_ONCE) {
136		if (--db_loop_count > 0) {
137			if (db_sstep_print) {
138				db_printf("\t\t");
139				db_print_loc_and_inst(pc);
140				db_printf("\n");
141			}
142			return (FALSE);	/* continue */
143		}
144	}
145	if (db_run_mode == STEP_RETURN) {
146	    db_expr_t ins = db_get_value(pc, sizeof(int), FALSE);
147
148	    /* continue until matching return */
149
150	    if (!inst_trap_return(ins) &&
151		(!inst_return(ins) || --db_call_depth != 0)) {
152		if (db_sstep_print) {
153		    if (inst_call(ins) || inst_return(ins)) {
154			register int i;
155
156			db_printf("[after %6d]     ", db_inst_count);
157			for (i = db_call_depth; --i > 0; )
158			    db_printf("  ");
159			db_print_loc_and_inst(pc);
160			db_printf("\n");
161		    }
162		}
163		if (inst_call(ins))
164		    db_call_depth++;
165		return (FALSE);	/* continue */
166	    }
167	}
168	if (db_run_mode == STEP_CALLT) {
169	    db_expr_t ins = db_get_value(pc, sizeof(int), FALSE);
170
171	    /* continue until call or return */
172
173	    if (!inst_call(ins) && !inst_return(ins) &&
174		!inst_trap_return(ins)) {
175		return (FALSE);	/* continue */
176	    }
177	}
178	db_run_mode = STEP_NONE;
179	return (TRUE);
180}
181
182void
183db_restart_at_pc(regs, watchpt)
184	db_regs_t *regs;
185	boolean_t watchpt;
186{
187	db_addr_t pc = PC_REGS(regs);
188
189	if ((db_run_mode == STEP_COUNT) || (db_run_mode == STEP_RETURN) ||
190	    (db_run_mode == STEP_CALLT)) {
191		db_expr_t	ins;
192
193		/*
194		 * We are about to execute this instruction,
195		 * so count it now.
196		 */
197		ins = db_get_value(pc, sizeof(int), FALSE);
198		db_inst_count++;
199		db_load_count += inst_load(ins);
200		db_store_count += inst_store(ins);
201#ifdef	SOFTWARE_SSTEP
202		/* XXX works on mips, but... */
203		if (inst_branch(ins) || inst_call(ins)) {
204			ins = db_get_value(next_instr_address(pc, 1),
205			    sizeof(int), FALSE);
206			db_inst_count++;
207			db_load_count += inst_load(ins);
208			db_store_count += inst_store(ins);
209		}
210#endif	SOFTWARE_SSTEP
211	}
212
213	if (db_run_mode == STEP_CONTINUE) {
214		if (watchpt || db_find_breakpoint_here(pc)) {
215			/*
216			 * Step over breakpoint/watchpoint.
217			 */
218			db_run_mode = STEP_INVISIBLE;
219			db_set_single_step(regs);
220		} else {
221			db_set_breakpoints();
222			db_set_watchpoints();
223		}
224	} else {
225		db_set_single_step(regs);
226	}
227}
228
229void
230db_single_step(regs)
231	db_regs_t *regs;
232{
233	if (db_run_mode == STEP_CONTINUE) {
234	    db_run_mode = STEP_INVISIBLE;
235	    db_set_single_step(regs);
236	}
237}
238
239#ifdef	SOFTWARE_SSTEP
240/*
241 *	Software implementation of single-stepping.
242 *	If your machine does not have a trace mode
243 *	similar to the vax or sun ones you can use
244 *	this implementation, done for the mips.
245 *	Just define the above conditional and provide
246 *	the functions/macros defined below.
247 *
248 * extern boolean_t
249 *	inst_branch(ins),	returns true if the instruction might branch
250 * extern unsigned
251 *	branch_taken(ins, pc, getreg_val, regs),
252 *				return the address the instruction might
253 *				branch to
254 *	getreg_val(regs, reg),	return the value of a user register,
255 *				as indicated in the hardware instruction
256 *				encoding, e.g. 8 for r8
257 *
258 * next_instr_address(pc, bd)	returns the address of the first
259 *				instruction following the one at "pc",
260 *				which is either in the taken path of
261 *				the branch (bd==1) or not.  This is
262 *				for machines (mips) with branch delays.
263 *
264 *	A single-step may involve at most 2 breakpoints -
265 *	one for branch-not-taken and one for branch taken.
266 *	If one of these addresses does not already have a breakpoint,
267 *	we allocate a breakpoint and save it here.
268 *	These breakpoints are deleted on return.
269 */
270
271void
272db_set_single_step(regs)
273	register db_regs_t *regs;
274{
275	db_addr_t pc = PC_REGS(regs);
276#ifndef SOFTWARE_SSTEP_EMUL
277	db_addr_t brpc;
278	u_int inst;
279
280	/*
281	 * User was stopped at pc, e.g. the instruction
282	 * at pc was not executed.
283	 */
284	inst = db_get_value(pc, sizeof(int), FALSE);
285	if (inst_branch(inst) || inst_call(inst) || inst_return(inst)) {
286	    brpc = branch_taken(inst, pc, getreg_val, regs);
287	    if (brpc != pc) {	/* self-branches are hopeless */
288		db_taken_bkpt = db_set_temp_breakpoint(brpc);
289	    }
290#if 0
291	    /* XXX this seems like a true bug, no?  */
292	    pc = next_instr_address(pc, 1);
293#endif
294	}
295#endif /*SOFTWARE_SSTEP_EMUL*/
296	pc = next_instr_address(pc, 0);
297	db_not_taken_bkpt = db_set_temp_breakpoint(pc);
298}
299
300void
301db_clear_single_step(regs)
302	db_regs_t *regs;
303{
304	if (db_taken_bkpt != 0) {
305	    db_delete_temp_breakpoint(db_taken_bkpt);
306	    db_taken_bkpt = 0;
307	}
308	if (db_not_taken_bkpt != 0) {
309	    db_delete_temp_breakpoint(db_not_taken_bkpt);
310	    db_not_taken_bkpt = 0;
311	}
312}
313
314#endif	SOFTWARE_SSTEP
315
316extern int	db_cmd_loop_done;
317
318/* single-step */
319/*ARGSUSED*/
320void
321db_single_step_cmd(addr, have_addr, count, modif)
322	db_expr_t	addr;
323	int		have_addr;
324	db_expr_t	count;
325	char *		modif;
326{
327	boolean_t	print = FALSE;
328
329	if (count == -1)
330	    count = 1;
331
332	if (modif[0] == 'p')
333	    print = TRUE;
334
335	db_run_mode = STEP_ONCE;
336	db_loop_count = count;
337	db_sstep_print = print;
338	db_inst_count = 0;
339	db_load_count = 0;
340	db_store_count = 0;
341
342	db_cmd_loop_done = 1;
343}
344
345/* trace and print until call/return */
346/*ARGSUSED*/
347void
348db_trace_until_call_cmd(addr, have_addr, count, modif)
349	db_expr_t	addr;
350	int		have_addr;
351	db_expr_t	count;
352	char *		modif;
353{
354	boolean_t	print = FALSE;
355
356	if (modif[0] == 'p')
357	    print = TRUE;
358
359	db_run_mode = STEP_CALLT;
360	db_sstep_print = print;
361	db_inst_count = 0;
362	db_load_count = 0;
363	db_store_count = 0;
364
365	db_cmd_loop_done = 1;
366}
367
368/*ARGSUSED*/
369void
370db_trace_until_matching_cmd(addr, have_addr, count, modif)
371	db_expr_t	addr;
372	int		have_addr;
373	db_expr_t	count;
374	char *		modif;
375{
376	boolean_t	print = FALSE;
377
378	if (modif[0] == 'p')
379	    print = TRUE;
380
381	db_run_mode = STEP_RETURN;
382	db_call_depth = 1;
383	db_sstep_print = print;
384	db_inst_count = 0;
385	db_load_count = 0;
386	db_store_count = 0;
387
388	db_cmd_loop_done = 1;
389}
390
391/* continue */
392/*ARGSUSED*/
393void
394db_continue_cmd(addr, have_addr, count, modif)
395	db_expr_t	addr;
396	int		have_addr;
397	db_expr_t	count;
398	char *		modif;
399{
400	if (modif[0] == 'c')
401	    db_run_mode = STEP_COUNT;
402	else
403	    db_run_mode = STEP_CONTINUE;
404	db_inst_count = 0;
405	db_load_count = 0;
406	db_store_count = 0;
407
408	db_cmd_loop_done = 1;
409}
410